@buoy-gg/ask-buoy 7.0.77 → 7.0.79

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 (43) hide show
  1. package/lib/commonjs/blocks/ExtraBlocks.js +1 -1
  2. package/lib/commonjs/blocks/choiceLogic.js +1 -0
  3. package/lib/commonjs/blocks/choiceLogic.test.js +1 -0
  4. package/lib/commonjs/components/ApprovalCard.js +1 -1
  5. package/lib/commonjs/components/AskBuoyModal.js +2 -2
  6. package/lib/commonjs/components/AskBuoySettings.js +1 -1
  7. package/lib/commonjs/components/EmptyState.js +1 -1
  8. package/lib/commonjs/components/Markdown.js +1 -1
  9. package/lib/commonjs/components/sheetText.test.js +1 -0
  10. package/lib/commonjs/store/askBuoyStore.js +2 -2
  11. package/lib/commonjs/store/transcriptPersistence.js +1 -1
  12. package/lib/commonjs/store/transcriptPersistence.test.js +1 -1
  13. package/lib/commonjs/store/useAskBuoy.js +1 -1
  14. package/lib/module/blocks/ExtraBlocks.js +1 -1
  15. package/lib/module/blocks/choiceLogic.js +1 -0
  16. package/lib/module/blocks/choiceLogic.test.js +1 -0
  17. package/lib/module/components/ApprovalCard.js +1 -1
  18. package/lib/module/components/AskBuoyModal.js +2 -2
  19. package/lib/module/components/AskBuoySettings.js +1 -1
  20. package/lib/module/components/EmptyState.js +1 -1
  21. package/lib/module/components/Markdown.js +1 -1
  22. package/lib/module/components/sheetText.test.js +1 -0
  23. package/lib/module/store/askBuoyStore.js +1 -1
  24. package/lib/module/store/transcriptPersistence.js +1 -1
  25. package/lib/module/store/transcriptPersistence.test.js +1 -1
  26. package/lib/module/store/useAskBuoy.js +1 -1
  27. package/lib/typescript/blocks/choiceLogic.d.ts +41 -0
  28. package/lib/typescript/blocks/choiceLogic.test.d.ts +2 -0
  29. package/lib/typescript/components/sheetText.test.d.ts +2 -0
  30. package/lib/typescript/store/askBuoyStore.d.ts +6 -0
  31. package/lib/typescript/store/transcriptPersistence.d.ts +14 -0
  32. package/lib/typescript/sync/wire.d.ts +8 -6
  33. package/lib/web/index.mjs +11 -11
  34. package/package.json +7 -7
  35. package/lib/commonjs/blocks/AskBlocks.test.js +0 -1
  36. package/lib/commonjs/components/Composer.test.js +0 -1
  37. package/lib/commonjs/components/EmptyState.test.js +0 -1
  38. package/lib/module/blocks/AskBlocks.test.js +0 -1
  39. package/lib/module/components/Composer.test.js +0 -1
  40. package/lib/module/components/EmptyState.test.js +0 -1
  41. package/lib/typescript/blocks/AskBlocks.test.d.ts +0 -2
  42. package/lib/typescript/components/Composer.test.d.ts +0 -2
  43. package/lib/typescript/components/EmptyState.test.d.ts +0 -2
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useAskBuoy=useAskBuoy;exports.useAskBuoyState=useAskBuoyState;var _sharedUi=require("@buoy-gg/shared-ui");var _react=require("react");var _reactNative=require("react-native");var _agentCore=require("@buoy-gg/agent-core");var _askBuoyStore=require("./askBuoyStore");var _resetConversation=require("./resetConversation");var _transcriptPersistence=require("./transcriptPersistence");var _useAskBuoySettings=require("./useAskBuoySettings");var _hostedModel=require("../hostedModel");var _hostedModelSetting=require("./hostedModelSetting");const isRelease=typeof __DEV__!=="undefined"&&!__DEV__;function questionBehind(u){for(const r of _askBuoyStore.askBuoyStore.getState().messages){if(!r.restored)continue;for(const f of r.blocks){const l=f.block;if(l?.id!==u)continue;const d=l.question??l.label??l.title;return typeof d==="string"&&d.trim()?d.trim():void 0}}return void 0}function readAvailableActions(){try{const u=(0,_sharedUi.listToolActions)();for(const r of(0,_sharedUi.listSnapshotToolIds)()){if(!u[r])u[r]=[];if(!u[r].includes(_agentCore.SNAPSHOT_ACTION)){u[r]=[_agentCore.SNAPSHOT_ACTION,...u[r]]}}return u}catch{return{}}}const SAVE_DEBOUNCE_MS=600;function useAskBuoyState(){return(0,_react.useSyncExternalStore)(_askBuoyStore.askBuoyStore.subscribe,_askBuoyStore.askBuoyStore.getState,_askBuoyStore.askBuoyStore.getState)}function useAskBuoy(u){const r=useAskBuoyState();const f=(0,_hostedModelSetting.useHostedModel)();const l=(0,_react.useMemo)(()=>(0,_hostedModel.pickHostedModel)(u,f,r.claudeOut),[u,f,r.claudeOut]);const d=(0,_react.useRef)(l);if(r.messages.length===0||!d.current)d.current=l;const t=d.current;const q=(0,_react.useRef)(u);q.current=u;const E=!!u?.hostedModels?.claude;const I=r.messages.length===0;(0,_react.useEffect)(()=>{const e=q.current?.readUsage;if(!E||f!=="claude"||!I||!e)return;let o=true;e().then(s=>{if(o&&s.claude)_askBuoyStore.askBuoyStore.setClaudeOut(!s.claude.available)}).catch(()=>{});return()=>{o=false}},[E,f,I]);const S=(0,_sharedUi.getUseIsPro)()();const g=(0,_react.useRef)(S);g.current=S;const B=(0,_react.useCallback)((...e)=>{if(!(0,_sharedUi.hasAccountAccess)()||!g.current)return Promise.reject(new Error("Ask Buoy requires Pro access."));return(0,_sharedUi.dispatchToolAction)(...e)},[]);const i=(0,_react.useRef)(null);const P=(0,_react.useRef)(false);const y=(0,_react.useRef)(null);const M=(0,_react.useRef)(t);M.current=t;const b=!!t?.endpoint&&!!t?.model;const A=t?.persistTranscript!==false;const{bypassApprovals:m,readOnly:v}=(0,_useAskBuoySettings.useAskBuoySettings)();const O=(0,_react.useRef)(m);O.current=m;const w=(0,_react.useRef)(v);w.current=v;const T=(0,_react.useCallback)(()=>w.current?{...t?.policy??{},readOnly:true}:t?.policy??{},[t]);const[j,F]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{let e=false;void(0,_sharedUi.waitForToolRegistry)().then(()=>{if(!e)F(o=>o+1)});return()=>{e=true}},[]);const k=(0,_react.useMemo)(()=>readAvailableActions(),[j]);const H=Object.keys(k).sort().join(",");(0,_react.useEffect)(()=>{_askBuoyStore.askBuoyStore.setConfigured(b);_askBuoyStore.askBuoyStore.setMeta({model:t?.model,protocol:t?.protocol??(t?"anthropic":void 0),appName:t?.appName,platform:_reactNative.Platform.OS,build:isRelease?"release":"development"});if(!b||!t){i.current=null;_askBuoyStore.askBuoyStore.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");return}const e=t.protocol??"anthropic";if(isRelease&&t.apiKey){_askBuoyStore.askBuoyStore.setNotice("This build carries a plaintext apiKey \u2014 that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds.")}else if(e==="anthropic"&&/^https?:\/\/api\.openai\.com\//i.test(t.endpoint)){_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.')}else if(e==="openai"&&/^https?:\/\/api\.anthropic\.com\//i.test(t.endpoint)){_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".')}else{_askBuoyStore.askBuoyStore.setNotice(null)}_askBuoyStore.askBuoyStore.setReporter(t.reportProblem??null);_askBuoyStore.askBuoyStore.setUsageReader(t.readUsage??null);const o=[t.endpoint,t.protocol??"anthropic",t.model].join("|");const s=_askBuoyStore.askBuoyStore.getHeldSession();if(s&&s.signature===o){i.current=s.session;s.session.setAvailableActions(k);s.session.setApprovalBypass(O.current);s.session.setReadOnly(w.current);_askBuoyStore.askBuoyStore.setLedger(s.session.ledger);return}const n=(0,_agentCore.createAskBuoySession)({endpoint:t.endpoint,protocol:t.protocol??"anthropic",model:t.model,headers:t.headers,apiKey:t.apiKey,maxTokens:t.maxTokens,anthropicVersion:t.anthropicVersion,requestOverrides:t.requestOverrides,hosted:t.hosted===true,...t.hostedFallback?{fallback:t.hostedFallback,onModelSwitch:a=>{const R=(a==="fallback"?t.hostedFallback?.model:t.model)===_hostedModel.HOSTED_CLAUDE_MODEL;_askBuoyStore.askBuoyStore.setMeta({model:a==="fallback"?t.hostedFallback?.model:t.model});if(a==="fallback"){if(!R)_askBuoyStore.askBuoyStore.setClaudeOut(true);_askBuoyStore.askBuoyStore.setNotice(R?"Luna is down right now, so Claude is answering.":"Claude is busy right now, so Luna is answering.")}else{_askBuoyStore.askBuoyStore.setNotice(null)}}}:{},catalog:t.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0,dispatch:B,readSnapshot:_sharedUi.readToolSnapshot,availableActions:k,policy:t.policy,context:t.context,isRelease,appName:t.appName,platform:_reactNative.Platform.OS,requestApproval:a=>_askBuoyStore.askBuoyStore.askApproval(a)});i.current=n;n.setApprovalBypass(O.current);n.setReadOnly(w.current);_askBuoyStore.askBuoyStore.holdSession(n,o);_askBuoyStore.askBuoyStore.setLedger(n.ledger);let c=false;void(0,_sharedUi.waitForToolRegistry)().then(a=>{if(a&&!c)void n.prime()});if(A&&!P.current){P.current=true;void(0,_transcriptPersistence.readTranscript)().then(a=>{if(!a)return;const R=_askBuoyStore.askBuoyStore.hydrate({messages:a.messages,answers:a.answers,usage:a.usage,queued:a.queued});if(!R)return;i.current?.restore(a.history);if(a.ledger?.length)i.current?.ledger.restore(a.ledger);if(a.pending)_askBuoyStore.askBuoyStore.restorePending(a.pending)})}return()=>{c=true}},[b,t,k,H,A]);(0,_react.useEffect)(()=>{i.current?.setApprovalBypass(m)},[m]);(0,_react.useEffect)(()=>{i.current?.setReadOnly(v)},[v]);const N=r.messages;const x=r.answers;const $=r.usage;const L=r.busy;const _=r.pending;const D=r.queued;(0,_react.useEffect)(()=>{if(!A)return;if(N.length===0)return;const e=()=>{void(0,_transcriptPersistence.writeTranscript)((0,_transcriptPersistence.buildPayload)({messages:N,history:i.current?.messages??[],answers:x,usage:$,pending:_,ledger:_askBuoyStore.askBuoyStore.getLedger()?.list(),queued:D}))};if(!L||_){e();return}const o=setTimeout(e,SAVE_DEBOUNCE_MS);return()=>clearTimeout(o)},[A,L,N,x,$,_,D]);const C=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!g.current)return;const o=i.current;if(!o)return;_askBuoyStore.askBuoyStore.beginTurn();const s=_askBuoyStore.askBuoyStore.startAssistant();const n=new AbortController;y.current=n;o.setAvailableActions(readAvailableActions());const c=M.current?.onEvent;void(async()=>{try{for await(const a of o.send(e,n.signal)){_askBuoyStore.askBuoyStore.applyEvent(s,a);if(c){try{c(a)}catch{}}}}catch(a){if(!n.signal.aborted){_askBuoyStore.askBuoyStore.applyEvent(s,{type:"error",message:a instanceof Error?a.message:"Something went wrong talking to your AI endpoint."})}}finally{if(n.signal.aborted)_askBuoyStore.askBuoyStore.markStopped(s);_askBuoyStore.askBuoyStore.finishAssistant(s);y.current=null}})()},[]);const p=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!g.current)return;const o=e.trim();if(!i.current||!o)return;if(_askBuoyStore.askBuoyStore.getState().busy){_askBuoyStore.askBuoyStore.enqueue(o);return}const s=_askBuoyStore.askBuoyStore.getState().awaitingBlockId;if(s)_askBuoyStore.askBuoyStore.recordAnswer({blockId:s,kind:"skipped"});_askBuoyStore.askBuoyStore.addUser(o);C(o)},[C]);const G=(0,_react.useCallback)(()=>{const{messages:e,busy:o}=_askBuoyStore.askBuoyStore.getState();if(o||!i.current)return;const s=[...e].reverse().find(n=>n.role==="user");if(!s)return;C(s.text)},[C]);const h=(0,_react.useCallback)(()=>{_askBuoyStore.askBuoyStore.requestStop();if(_askBuoyStore.askBuoyStore.getState().pending)_askBuoyStore.askBuoyStore.answerApproval(false);y.current?.abort();y.current=null},[]);(0,_react.useEffect)(()=>{const e=(0,_sharedUi.subscribeAccountAccess)(()=>{if(!(0,_sharedUi.hasAccountAccess)())h()});return()=>{e();h()}},[h]);(0,_react.useEffect)(()=>{if(!S)h()},[S,h]);(0,_react.useEffect)(()=>{if(r.queued.length===0)return;if((0,_askBuoyStore.turnPhaseOf)(r)!=="idle")return;if((0,_askBuoyStore.queuePauseReason)(r)!==null)return;const e=_askBuoyStore.askBuoyStore.shiftQueued();if(e)p(e.text)},[r,p]);const K=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.takeQueuedToDraft(e),[]);const Q=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.removeQueued(e),[]);const V=(0,_react.useCallback)(()=>{y.current?.abort();y.current=null;(0,_resetConversation.clearConversation)()},[]);const W=(0,_react.useCallback)(()=>(0,_resetConversation.resetConversation)({dispatch:_sharedUi.dispatchToolAction,undoFirst:true}),[]);const U=(0,_react.useCallback)(async e=>{const o=_askBuoyStore.askBuoyStore.getLedger();if(!o){_askBuoyStore.askBuoyStore.setNotice(`${e.label} couldn't run \u2014 no active Ask Buoy session.`);return}const s=t?.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0;if(e.targetDigest){const c=await(0,_agentCore.readTargetDigest)({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:B,catalog:s});if(c!==void 0&&c!==e.targetDigest){_askBuoyStore.askBuoyStore.addNote(`\u201C${e.label}\u201D wasn't applied \u2014 what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`);return}}const n=await(0,_agentCore.runGatedAction)({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:B,ledger:o,policy:T(),isRelease,catalog:s});if(n.ok){_askBuoyStore.askBuoyStore.addNote(`Applied \u201C${e.label}\u201D, which was still waiting when the app restarted. Whatever else that answer was going to do is gone \u2014 ask again if there was more to it.`)}else{_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 ${n.error}`)}},[t,T]);const z=(0,_react.useCallback)(e=>{const o=_askBuoyStore.askBuoyStore.getState().pending;const s=typeof e==="boolean"?e:e.approved;_askBuoyStore.askBuoyStore.answerApproval(e);if(!o?.restored)return;if(!s){_askBuoyStore.askBuoyStore.addNote(`Skipped \u201C${o.label}\u201D. Nothing was changed.`);return}void U(o)},[U]);const J=(0,_react.useCallback)(e=>{_askBuoyStore.askBuoyStore.recordAnswer(e);const o=questionBehind(e.blockId);p(o?`${o} \u2014 ${(0,_agentCore.answerToText)(e)}`:(0,_agentCore.answerToText)(e))},[p]);const X=(0,_react.useCallback)(async(e,o)=>{if(!(0,_sharedUi.hasAccountAccess)()||!g.current){_askBuoyStore.askBuoyStore.setNotice("Ask Buoy requires Pro access.");return}if(e.send){p(e.send);return}if(!e.dispatch)return;const s=`${o}:${e.label}`;const n=_askBuoyStore.askBuoyStore.getLedger();if(!n){_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 no active Ask Buoy session.`);return}_askBuoyStore.askBuoyStore.setActionState(s,"running");const c=await(0,_agentCore.runGatedAction)({toolId:e.dispatch.toolId,action:e.dispatch.action,params:e.dispatch.params??{},dispatch:B,ledger:n,policy:T(),isRelease,catalog:t?.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0});if(c.ok){_askBuoyStore.askBuoyStore.setActionState(s,"done")}else{_askBuoyStore.askBuoyStore.setActionState(s,"failed");_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 ${c.error}`)}},[p,t,T]);return{state:r,send:p,stop:h,reset:V,undoThenReset:W,approve:z,retry:G,answer:J,runAction:X,editQueued:K,removeQueued:Q,configured:b,toolCount:Object.keys(k).length,availableToolIds:Object.keys(k)}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useAskBuoy=useAskBuoy;exports.useAskBuoyState=useAskBuoyState;var _sharedUi=require("@buoy-gg/shared-ui");var _react=require("react");var _reactNative=require("react-native");var _agentCore=require("@buoy-gg/agent-core");var _askBuoyStore=require("./askBuoyStore");var _resetConversation=require("./resetConversation");var _transcriptPersistence=require("./transcriptPersistence");var _useAskBuoySettings=require("./useAskBuoySettings");var _hostedModel=require("../hostedModel");var _hostedModelSetting=require("./hostedModelSetting");const isRelease=typeof __DEV__!=="undefined"&&!__DEV__;function questionBehind(u){for(const a of _askBuoyStore.askBuoyStore.getState().messages){if(!a.restored)continue;for(const f of a.blocks){const d=f.block;if(d?.id!==u)continue;const p=d.question??d.label??d.title;return typeof p==="string"&&p.trim()?p.trim():void 0}}return void 0}function readAvailableActions(){try{const u=(0,_sharedUi.listToolActions)();for(const a of(0,_sharedUi.listSnapshotToolIds)()){if(!u[a])u[a]=[];if(!u[a].includes(_agentCore.SNAPSHOT_ACTION)){u[a]=[_agentCore.SNAPSHOT_ACTION,...u[a]]}}return u}catch{return{}}}const SAVE_DEBOUNCE_MS=600;function useAskBuoyState(){return(0,_react.useSyncExternalStore)(_askBuoyStore.askBuoyStore.subscribe,_askBuoyStore.askBuoyStore.getState,_askBuoyStore.askBuoyStore.getState)}function useAskBuoy(u){const a=useAskBuoyState();const f=(0,_hostedModelSetting.useHostedModel)();const d=(0,_react.useMemo)(()=>(0,_hostedModel.pickHostedModel)(u,f,a.claudeOut),[u,f,a.claudeOut]);const p=(0,_react.useRef)(d);if(a.messages.length===0||!p.current)p.current=d;const t=p.current;const q=(0,_react.useRef)(u);q.current=u;const I=!!u?.hostedModels?.claude;const P=a.messages.length===0;(0,_react.useEffect)(()=>{const e=q.current?.readUsage;if(!I||f!=="claude"||!P||!e)return;let o=true;e().then(s=>{if(o&&s.claude)_askBuoyStore.askBuoyStore.setClaudeOut(!s.claude.available)}).catch(()=>{});return()=>{o=false}},[I,f,P]);const y=(0,_sharedUi.getUseIsPro)()();const S=(0,_react.useRef)(y);S.current=y;const A=(0,_react.useCallback)((...e)=>{if(!(0,_sharedUi.hasAccountAccess)()||!S.current)return Promise.reject(new Error("Ask Buoy requires Pro access."));return(0,_sharedUi.dispatchToolAction)(...e)},[]);const i=(0,_react.useRef)(null);const M=(0,_react.useRef)(false);const[x,L]=(0,_react.useState)(false);const k=(0,_react.useRef)(null);const D=(0,_react.useRef)(t);D.current=t;const B=!!t?.endpoint&&!!t?.model;const b=t?.persistTranscript!==false;const{bypassApprovals:m,readOnly:v}=(0,_useAskBuoySettings.useAskBuoySettings)();const O=(0,_react.useRef)(m);O.current=m;const w=(0,_react.useRef)(v);w.current=v;const R=(0,_react.useCallback)(()=>w.current?{...t?.policy??{},readOnly:true}:t?.policy??{},[t]);const[E,K]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{let e=false;void(0,_sharedUi.waitForToolRegistry)().then(()=>{if(!e)K(o=>o+1)});return()=>{e=true}},[]);const h=(0,_react.useMemo)(()=>readAvailableActions(),[E]);const Q=Object.keys(h).sort().join(",");(0,_react.useEffect)(()=>{_askBuoyStore.askBuoyStore.setConfigured(B);_askBuoyStore.askBuoyStore.setMeta({model:t?.model,protocol:t?.protocol??(t?"anthropic":void 0),appName:t?.appName,platform:_reactNative.Platform.OS,build:isRelease?"release":"development"});if(!B||!t){i.current=null;_askBuoyStore.askBuoyStore.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");return}const e=t.protocol??"anthropic";if(isRelease&&t.apiKey){_askBuoyStore.askBuoyStore.setNotice("This build carries a plaintext apiKey \u2014 that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds.")}else if(e==="anthropic"&&/^https?:\/\/api\.openai\.com\//i.test(t.endpoint)){_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.')}else if(e==="openai"&&/^https?:\/\/api\.anthropic\.com\//i.test(t.endpoint)){_askBuoyStore.askBuoyStore.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".')}else{_askBuoyStore.askBuoyStore.setNotice(null)}_askBuoyStore.askBuoyStore.setReporter(t.reportProblem??null);_askBuoyStore.askBuoyStore.setUsageReader(t.readUsage??null);const o=[t.endpoint,t.protocol??"anthropic",t.model].join("|");const s=_askBuoyStore.askBuoyStore.getHeldSession();if(s&&s.signature===o){i.current=s.session;s.session.setAvailableActions(h);s.session.setApprovalBypass(O.current);s.session.setReadOnly(w.current);_askBuoyStore.askBuoyStore.setLedger(s.session.ledger);return}const n=(0,_agentCore.createAskBuoySession)({endpoint:t.endpoint,protocol:t.protocol??"anthropic",model:t.model,headers:t.headers,apiKey:t.apiKey,maxTokens:t.maxTokens,anthropicVersion:t.anthropicVersion,requestOverrides:t.requestOverrides,hosted:t.hosted===true,...t.hostedFallback?{fallback:t.hostedFallback,onModelSwitch:r=>{const C=(r==="fallback"?t.hostedFallback?.model:t.model)===_hostedModel.HOSTED_CLAUDE_MODEL;_askBuoyStore.askBuoyStore.setMeta({model:r==="fallback"?t.hostedFallback?.model:t.model});if(r==="fallback"){if(!C)_askBuoyStore.askBuoyStore.setClaudeOut(true);_askBuoyStore.askBuoyStore.setNotice(C?"Luna is down right now, so Claude is answering.":"Claude is busy right now, so Luna is answering.")}else{_askBuoyStore.askBuoyStore.setNotice(null)}}}:{},catalog:t.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0,dispatch:A,readSnapshot:_sharedUi.readToolSnapshot,availableActions:h,policy:t.policy,context:t.context,isRelease,inApp:true,appName:t.appName,platform:_reactNative.Platform.OS,requestApproval:r=>_askBuoyStore.askBuoyStore.askApproval(r)});i.current=n;n.setApprovalBypass(O.current);n.setReadOnly(w.current);_askBuoyStore.askBuoyStore.holdSession(n,o);_askBuoyStore.askBuoyStore.setLedger(n.ledger);let c=false;void(0,_sharedUi.waitForToolRegistry)().then(r=>{if(r&&!c)void n.prime()});if(b&&!M.current){M.current=true;void(0,_transcriptPersistence.readTranscript)().then(r=>{if(!r)return;const C=_askBuoyStore.askBuoyStore.hydrate({messages:r.messages,answers:r.answers,usage:r.usage,queued:r.queued});if(!C)return;i.current?.restore(r.history);if(r.ledger?.length)i.current?.ledger.restore(r.ledger);if(r.pending)_askBuoyStore.askBuoyStore.restorePending(r.pending);const G=(0,_transcriptPersistence.afterReload)(r,Date.now());if(G==="resume")L(true);else if(G==="note")_askBuoyStore.askBuoyStore.addNote(_transcriptPersistence.RELOAD_NOTE)})}return()=>{c=true}},[B,t,h,Q,b]);(0,_react.useEffect)(()=>{i.current?.setApprovalBypass(m)},[m]);(0,_react.useEffect)(()=>{i.current?.setReadOnly(v)},[v]);const N=a.messages;const $=a.answers;const U=a.usage;const F=a.busy;const _=a.pending;const j=a.queued;(0,_react.useEffect)(()=>{if(!b)return;if(N.length===0)return;const e=()=>{void(0,_transcriptPersistence.writeTranscript)((0,_transcriptPersistence.buildPayload)({messages:N,history:i.current?.messages??[],answers:$,usage:U,pending:_,ledger:_askBuoyStore.askBuoyStore.getLedger()?.list(),queued:j}))};if(!F||_){e();return}const o=setTimeout(e,SAVE_DEBOUNCE_MS);return()=>clearTimeout(o)},[b,F,N,$,U,_,j]);const T=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!S.current)return;const o=i.current;if(!o)return;_askBuoyStore.askBuoyStore.beginTurn();const s=_askBuoyStore.askBuoyStore.startAssistant();const n=new AbortController;k.current=n;o.setAvailableActions(readAvailableActions());const c=D.current?.onEvent;void(async()=>{try{for await(const r of o.send(e,n.signal)){_askBuoyStore.askBuoyStore.applyEvent(s,r);if(c){try{c(r)}catch{}}}}catch(r){if(!n.signal.aborted){_askBuoyStore.askBuoyStore.applyEvent(s,{type:"error",message:r instanceof Error?r.message:"Something went wrong talking to your AI endpoint."})}}finally{if(n.signal.aborted)_askBuoyStore.askBuoyStore.markStopped(s);_askBuoyStore.askBuoyStore.finishAssistant(s);k.current=null}})()},[]);const l=(0,_react.useCallback)(e=>{if(!(0,_sharedUi.hasAccountAccess)()||!S.current)return;const o=e.trim();if(!i.current||!o)return;if(_askBuoyStore.askBuoyStore.getState().busy){_askBuoyStore.askBuoyStore.enqueue(o);return}const s=_askBuoyStore.askBuoyStore.getState().awaitingBlockId;if(s)_askBuoyStore.askBuoyStore.recordAnswer({blockId:s,kind:"skipped"});_askBuoyStore.askBuoyStore.addUser(o);T(o)},[T]);(0,_react.useEffect)(()=>{if(!x||!y||!(0,_sharedUi.hasAccountAccess)()||!i.current||E===0)return;L(false);l(_transcriptPersistence.RESUME_AFTER_RELOAD)},[x,y,E,l]);const V=(0,_react.useCallback)(()=>{const{messages:e,busy:o}=_askBuoyStore.askBuoyStore.getState();if(o||!i.current)return;const s=[...e].reverse().find(n=>n.role==="user");if(!s)return;T(s.text)},[T]);const g=(0,_react.useCallback)(()=>{_askBuoyStore.askBuoyStore.requestStop();if(_askBuoyStore.askBuoyStore.getState().pending)_askBuoyStore.askBuoyStore.answerApproval(false);k.current?.abort();k.current=null},[]);(0,_react.useEffect)(()=>{const e=(0,_sharedUi.subscribeAccountAccess)(()=>{if(!(0,_sharedUi.hasAccountAccess)())g()});return()=>{e();g()}},[g]);(0,_react.useEffect)(()=>{if(!y)g()},[y,g]);(0,_react.useEffect)(()=>{if(a.queued.length===0)return;if((0,_askBuoyStore.turnPhaseOf)(a)!=="idle")return;if((0,_askBuoyStore.queuePauseReason)(a)!==null)return;const e=_askBuoyStore.askBuoyStore.shiftQueued();if(e)l(e.text)},[a,l]);const W=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.takeQueuedToDraft(e),[]);const z=(0,_react.useCallback)(e=>_askBuoyStore.askBuoyStore.removeQueued(e),[]);const J=(0,_react.useCallback)(()=>{k.current?.abort();k.current=null;(0,_resetConversation.clearConversation)()},[]);const X=(0,_react.useCallback)(()=>(0,_resetConversation.resetConversation)({dispatch:_sharedUi.dispatchToolAction,undoFirst:true}),[]);const H=(0,_react.useCallback)(async e=>{const o=_askBuoyStore.askBuoyStore.getLedger();if(!o){_askBuoyStore.askBuoyStore.setNotice(`${e.label} couldn't run \u2014 no active Ask Buoy session.`);return}const s=t?.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0;if(e.targetDigest){const c=await(0,_agentCore.readTargetDigest)({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:A,catalog:s});if(c!==void 0&&c!==e.targetDigest){_askBuoyStore.askBuoyStore.addNote(`\u201C${e.label}\u201D wasn't applied \u2014 what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`);return}}const n=await(0,_agentCore.runGatedAction)({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:A,ledger:o,policy:R(),isRelease,catalog:s});if(n.ok){_askBuoyStore.askBuoyStore.addNote(`Applied \u201C${e.label}\u201D, which was still waiting when the app restarted. Whatever else that answer was going to do is gone \u2014 ask again if there was more to it.`)}else{_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 ${n.error}`)}},[t,R]);const Y=(0,_react.useCallback)(e=>{const o=_askBuoyStore.askBuoyStore.getState().pending;const s=typeof e==="boolean"?e:e.approved;_askBuoyStore.askBuoyStore.answerApproval(e);if(!o?.restored)return;if(!s){_askBuoyStore.askBuoyStore.addNote(`Skipped \u201C${o.label}\u201D. Nothing was changed.`);return}void H(o)},[H]);const Z=(0,_react.useCallback)(e=>{_askBuoyStore.askBuoyStore.recordAnswer(e);const o=questionBehind(e.blockId);l(o?`${o} \u2014 ${(0,_agentCore.answerToText)(e)}`:(0,_agentCore.answerToText)(e))},[l]);const ee=(0,_react.useCallback)(async(e,o)=>{if(!(0,_sharedUi.hasAccountAccess)()||!S.current){_askBuoyStore.askBuoyStore.setNotice("Ask Buoy requires Pro access.");return}if(e.send){l(e.send);return}if(!e.dispatch)return;const s=`${o}:${e.label}`;const n=_askBuoyStore.askBuoyStore.getLedger();if(!n){_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 no active Ask Buoy session.`);return}_askBuoyStore.askBuoyStore.setActionState(s,"running");const c=await(0,_agentCore.runGatedAction)({toolId:e.dispatch.toolId,action:e.dispatch.action,params:e.dispatch.params??{},dispatch:A,ledger:n,policy:R(),isRelease,catalog:t?.tools?.length?[..._agentCore.CATALOG,...t.tools]:void 0});if(c.ok){_askBuoyStore.askBuoyStore.setActionState(s,"done")}else{_askBuoyStore.askBuoyStore.setActionState(s,"failed");_askBuoyStore.askBuoyStore.setNotice(`${e.label} failed \u2014 ${c.error}`)}},[l,t,R]);return{state:a,send:l,stop:g,reset:J,undoThenReset:X,approve:Y,retry:V,answer:Z,runAction:ee,editQueued:W,removeQueued:z,configured:B,toolCount:Object.keys(h).length,availableToolIds:Object.keys(h)}}
@@ -1 +1 @@
1
- "use strict";import{monoFont as R}from"@buoy-gg/shared-ui";import{Platform as H}from"react-native";import{useEffect as U,useRef as W,useState as w}from"react";import{Animated as T,Image as $,Pressable as f,ScrollView as _,StyleSheet as I,Switch as K,Text as l,TextInput as P,View as s}from"react-native";import{AlertTriangle as J,Check as V,ChevronDown as L,ChevronRight as B,night as o,NightSegmented as G,RefreshCw as X,Search as Q,X as Y}from"@buoy-gg/shared-ui";import{useBlockHandlers as S}from"./context";import{ActionRow as D,blockStyles as b,Card as m,More as Z,toneColor as O,usePaged as k}from"./primitives";import{jsx as n,jsxs as a,Fragment as v}from"react/jsx-runtime";const E=e=>typeof e==="string"?e:e===void 0?"\u2014":JSON.stringify(e);export function Suggestions({block:e}){const r=S();return n(s,{style:t.chips,testID:`buoy-block-suggestions-${e.id}`,children:e.chips.slice(0,4).map(i=>n(f,{disabled:!!r.busy,onPress:()=>void r.runAction({label:i.label,send:i.send??i.label},e.id),style:({pressed:d})=>[t.chip,d&&t.pressed],accessibilityRole:"button",children:n(l,{style:t.chipText,children:i.label})},i.label))})}export function MultiSelect({block:e}){const r=S();const i=r.answered[e.id];const d=!!i;const[c,p]=w([]);const u=i?.kind==="multi"?i.optionIds:c;const y=g=>p(h=>h.includes(g)?h.filter(x=>x!==g):[...h,g]);const C=c.length>=(e.min??1)&&(!e.max||c.length<=e.max);return a(m,{testID:`buoy-block-multi-${e.id}`,tone:d?void 0:"info",children:[n(l,{style:t.question,children:e.question}),n(s,{style:t.optionsBox,children:e.options.map((g,h)=>{const x=u.includes(g.id);return a(f,{disabled:d,onPress:()=>y(g.id),style:[t.option,h>0&&b.rowDivider,d&&!x&&t.dim],accessibilityRole:"checkbox",accessibilityState:{checked:x},children:[n(s,{style:[t.checkbox,x&&t.checkboxOn],children:x?n(V,{size:11,color:o.color.onAccent}):null}),a(s,{style:t.flex,children:[n(l,{style:t.optionLabel,children:g.label}),g.detail?n(l,{style:t.optionDetail,children:g.detail}):null]})]},g.id)})}),d?null:n(s,{style:t.rowEnd,children:n(f,{testID:"buoy-block-multi-submit",disabled:!C,onPress:()=>r.answer({blockId:e.id,kind:"multi",optionIds:c,labels:e.options.filter(g=>c.includes(g.id)).map(g=>g.label)}),style:[t.button,t.buttonPrimary,!C&&t.disabled],children:n(l,{style:t.buttonTextPrimary,children:e.submitLabel??`Use ${c.length||""}`.trim()})})})]})}const ee=8;export function Select({block:e}){const r=S();const i=r.answered[e.id];const[d,c]=w("");const[p,u]=w(false);const y=i?.kind==="select"?i.label:null;const C=e.options.filter(h=>!d||h.label.toLowerCase().includes(d.toLowerCase())).slice(0,8);const g=e.searchable!==false&&e.options.length>=ee;return a(m,{testID:`buoy-block-select-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),y?n(l,{style:t.decided,children:y}):a(s,{style:t.selectBox,children:[a(f,{onPress:()=>u(h=>!h),style:t.selectField,accessibilityRole:"button",children:[n(l,{style:t.selectPlaceholder,children:p?"Choose\u2026":`${e.options.length} options`}),n(L,{size:14,color:o.color.textSecondary})]}),p&&a(v,{children:[g&&a(s,{style:t.search,children:[n(Q,{size:12,color:o.color.textSecondary}),n(P,{style:t.searchInput,value:d,onChangeText:c,placeholder:"Search",placeholderTextColor:o.color.placeholder,autoCapitalize:"none",autoCorrect:false})]}),C.map((h,x)=>a(f,{onPress:()=>r.answer({blockId:e.id,kind:"select",optionId:h.id,label:h.label}),style:[t.option,x>0&&b.rowDivider],accessibilityRole:"button",children:[a(s,{style:t.flex,children:[n(l,{style:t.optionLabel,children:h.label}),h.detail?n(l,{style:t.optionDetail,children:h.detail}):null]}),n(B,{size:12,color:o.color.textTertiary})]},h.id)),e.options.length>C.length?a(l,{style:t.hint,children:[e.options.length-C.length," more \u2014 keep typing"]}):null]})]})]})}function j({min:e,max:r,step:i=1,value:d,unit:c,onChange:p,disabled:u}){const y=W(0);const C=r>e?(d-e)/(r-e)*100:0;const g=z=>Math.min(r,Math.max(e,+(Math.round((z-e)/i)*i+e).toFixed(6)));const h=z=>!u&&p(g(d+z*i));const x=z=>{if(u||y.current<=0)return;const N=Math.min(1,Math.max(0,z.nativeEvent.locationX/y.current));p(g(e+N*(r-e)))};const q=z=>{y.current=z.nativeEvent.layout.width};return a(s,{style:t.sliderRow,children:[n(f,{disabled:u,onPress:()=>h(-1),style:t.stepBtn,accessibilityRole:"button",accessibilityLabel:"Decrease",children:n(l,{style:t.stepText,children:"\u2212"})}),n(f,{style:t.trackHit,onLayout:q,onPress:x,disabled:u,accessibilityRole:"adjustable",accessibilityValue:{min:e,max:r,now:d},accessibilityActions:[{name:"increment"},{name:"decrement"}],onAccessibilityAction:z=>h(z.nativeEvent.actionName==="increment"?1:-1),children:a(s,{style:t.track,pointerEvents:"none",children:[n(s,{style:[t.fill,{width:`${C}%`}]}),n(s,{style:[t.thumb,{left:`${C}%`}]})]})}),n(f,{disabled:u,onPress:()=>h(1),style:t.stepBtn,accessibilityRole:"button",accessibilityLabel:"Increase",children:n(l,{style:t.stepText,children:"+"})}),a(l,{style:t.sliderValue,children:[d,c?` ${c}`:""]})]})}export function Slider({block:e}){const r=S();const i=r.answered[e.id];const[d,c]=w(e.value??e.min);return a(m,{testID:`buoy-block-slider-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),n(s,{style:t.pad,children:n(j,{min:e.min,max:e.max,step:e.step,value:i?.kind==="slider"?i.value:d,unit:e.unit,onChange:c,disabled:!!i})}),i?null:n(s,{style:t.rowEnd,children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"slider",value:d,unit:e.unit}),style:[t.button,t.buttonPrimary],children:a(l,{style:t.buttonTextPrimary,children:["Use ",d,e.unit?` ${e.unit}`:""]})})})]})}export function DateTime({block:e}){const r=S();const i=r.answered[e.id];const[d,c]=w("");const p=e.presets??[{label:"Now",value:new Date().toISOString()},{label:"Tomorrow",value:new Date(Date.now()+864e5).toISOString()},{label:"+7 days",value:new Date(Date.now()+7*864e5).toISOString()},{label:"+30 days",value:new Date(Date.now()+30*864e5).toISOString()}];return a(m,{testID:`buoy-block-datetime-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),i?n(l,{style:t.decided,children:i.kind==="dateTime"?i.label??i.value:"(skipped)"}):a(v,{children:[n(s,{style:[t.chips,t.pad],children:p.map(u=>n(f,{onPress:()=>r.answer({blockId:e.id,kind:"dateTime",value:u.value,label:u.label}),style:t.chip,children:n(l,{style:t.chipText,children:u.label})},u.label))}),a(s,{style:[t.inputRow,t.pad],children:[n(P,{style:t.input,value:d,onChangeText:c,placeholder:e.mode==="time"?"14:30":"2026-09-15",placeholderTextColor:o.color.placeholder,keyboardType:"numbers-and-punctuation"}),n(f,{disabled:!d.trim(),onPress:()=>r.answer({blockId:e.id,kind:"dateTime",value:d.trim()}),style:[t.button,t.buttonPrimary,!d.trim()&&t.disabled],children:n(l,{style:t.buttonTextPrimary,children:"Use"})})]})]})]})}function A({f:e,value:r,onChange:i,disabled:d}){if(e.type==="toggle"){return a(s,{style:t.fieldRow,children:[n(l,{style:t.fieldLabel,children:e.label}),n(K,{value:!!r,onValueChange:i,disabled:d,trackColor:{true:o.color.accent,false:o.color.fillTertiary},thumbColor:o.color.knob})]})}if(e.type==="select"){return a(s,{style:t.fieldCol,children:[n(l,{style:t.fieldLabel,children:e.label}),n(s,{style:t.chips,children:e.options.map(c=>n(f,{disabled:d,onPress:()=>i(c.id),style:[t.chip,r===c.id&&t.chipOn],children:n(l,{style:[t.chipText,r===c.id&&t.chipTextOn],children:c.label})},c.id))})]})}if(e.type==="slider"){return a(s,{style:t.fieldCol,children:[n(l,{style:t.fieldLabel,children:e.label}),n(j,{min:e.min,max:e.max,step:e.step,unit:e.unit,value:typeof r==="number"?r:e.min,onChange:i,disabled:d})]})}return a(s,{style:t.fieldRow,children:[n(l,{style:t.fieldLabel,children:e.label}),n(P,{style:t.fieldInput,value:r===void 0||r===null?"":String(r),onChangeText:i,editable:!d,placeholder:e.placeholder??"",placeholderTextColor:o.color.placeholder,keyboardType:e.type==="number"?"numeric":"ascii-capable",secureTextEntry:e.type==="secret",autoCapitalize:"none",autoCorrect:false})]})}function F(e){const[r,i]=w(()=>Object.fromEntries(e.map(d=>[d.key,d.value])));return{values:r,set:(d,c)=>i(p=>({...p,[d]:c}))}}export function Form({block:e}){const r=S();const i=r.answered[e.id];const{values:d,set:c}=F(e.fields);return a(m,{title:e.title,testID:`buoy-block-form-${e.id}`,tone:i?void 0:"info",children:[n(s,{style:t.pad,children:e.fields.map(p=>n(A,{f:p,value:d[p.key],onChange:u=>c(p.key,u),disabled:!!i},p.key))}),i?n(l,{style:t.decided,children:"Sent"}):n(s,{style:t.rowEnd,children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"form",values:d}),style:[t.button,t.buttonPrimary],children:n(l,{style:t.buttonTextPrimary,children:e.submitLabel??"Send"})})})]})}export function Proposal({block:e}){const r=S();const i=r.answered[e.id];const{values:d,set:c}=F(e.fields);const p=i?.kind==="proposal"?i.approved:null;return a(m,{testID:`buoy-block-proposal-${e.id}`,tone:i?void 0:"warning",children:[n(l,{style:t.confirmTitle,children:e.title}),n(l,{style:t.confirmChange,children:e.change}),n(s,{style:t.pad,children:e.fields.map(u=>n(A,{f:u,value:d[u.key],onChange:y=>c(u.key,y),disabled:!!i},u.key))}),e.consequence?n(l,{style:t.consequence,children:e.consequence}):null,n(s,{style:t.actionsRow,children:i?n(l,{style:t.decided,children:p?"\u2713 Applied with these values":"\u2715 Not applied"}):a(v,{children:[n(f,{onPress:()=>r.answer({blockId:e.id,kind:"proposal",approved:true,values:d}),style:[t.button,t.buttonPrimary],children:n(l,{style:t.buttonTextPrimary,children:e.approveLabel??"Apply"})}),n(f,{onPress:()=>r.answer({blockId:e.id,kind:"proposal",approved:false}),style:[t.button,t.buttonPlate],children:n(l,{style:t.buttonTextPlate,children:"Not now"})})]})})]})}export function Claim({block:e}){const r=S();const[i,d]=w(null);const c=r.actionState?.[`${e.id}:${e.verify?.label??"Verify"}`];const p=e.verified||c==="done";return a(s,{style:t.claim,testID:`buoy-block-claim-${e.id}`,children:[a(l,{style:t.claimText,children:[e.text,e.sources.map((u,y)=>a(l,{onPress:()=>d(i===y?null:y),style:t.cite,children:[" [",y+1,"]"]},y)),p?n(l,{style:t.verified,children:" \u2713 verified"}):null]}),i!==null&&e.sources[i]?a(s,{style:t.citeBox,children:[a(l,{style:t.citeLabel,children:["[",i+1,"] ",e.sources[i].label]}),e.sources[i].detail?n(l,{style:t.citeDetail,children:e.sources[i].detail}):null]}):null,e.verify&&!p?a(f,{onPress:()=>void r.runAction(e.verify,e.id),style:t.verifyBtn,accessibilityRole:"button",children:[n(X,{size:11,color:o.color.accent}),n(l,{style:t.verifyText,children:c==="running"?"Checking\u2026":e.verify.label})]}):null]})}export function Details({block:e}){const[r,i]=w(!!e.open);return a(m,{testID:`buoy-block-details-${e.id}`,children:[a(f,{onPress:()=>i(d=>!d),style:b.row,accessibilityRole:"button",accessibilityState:{expanded:r},children:[n(l,{style:b.rowTitle,children:e.summary}),r?n(L,{size:12,color:o.color.textTertiary}):n(B,{size:12,color:o.color.textTertiary})]}),r?n(l,{style:t.detailsBody,selectable:true,children:e.body}):null]})}export function Tabs({block:e,renderNested:r}){const[i,d]=w(0);const c=e.tabs.map((u,y)=>({key:String(y),label:u.label}));const p=e.tabs[i];return a(s,{style:t.tabsWrap,testID:`buoy-block-tabs-${e.id}`,children:[n(G,{tabs:c,activeKey:String(i),onChange:u=>d(Number(u)),size:"sm"}),p&&r?r(p.block):null]})}const te=10;export function Timeline({block:e}){const r=k(e.events,te);return a(m,{title:e.title??"What happened",source:e.source,testID:`buoy-block-timeline-${e.id}`,children:[r.visible.map((i,d)=>a(s,{style:t.tlRow,children:[n(l,{style:t.tlAt,children:i.at}),a(s,{style:t.tlLine,children:[n(s,{style:[t.tlDot,{backgroundColor:O(i.tone??"muted")}]}),d<r.visible.length-1?n(s,{style:t.tlStem}):null]}),a(s,{style:t.flex,children:[n(l,{style:t.tlSource,children:i.source.toUpperCase()}),n(l,{style:b.rowTitle,numberOfLines:2,children:i.label})]})]},d)),n(Z,{paged:r})]})}export function Sparkline({block:e}){const r=e.series.slice(-60);const i=Math.max(1,...r);const d=Math.min(...r,0);const c=44;return a(m,{title:e.title,source:e.source,testID:`buoy-block-sparkline-${e.id}`,children:[a(s,{style:t.sparkHead,children:[a(l,{style:t.sparkCurrent,children:[e.current??r[r.length-1],e.unit?a(l,{style:t.sparkUnit,children:[" ",e.unit]}):null]}),e.live?n(LivePill,{}):null]}),n(s,{style:[t.spark,{height:c}],children:r.map((p,u)=>n(s,{style:[t.sparkBar,{height:Math.max(2,(p-d)/(i-d||1)*c),backgroundColor:u===r.length-1?o.color.accent:o.color.accentBorderStrong}]},u))}),e.label?n(l,{style:t.hint,children:e.label}):null]})}export function LivePill(){const e=W(new T.Value(1)).current;U(()=>{const r=T.loop(T.sequence([T.timing(e,{toValue:.3,duration:700,useNativeDriver:true}),T.timing(e,{toValue:1,duration:700,useNativeDriver:true})]));r.start();return()=>r.stop()},[e]);return a(s,{style:t.live,children:[n(T.View,{style:[t.liveDot,{opacity:e}]}),n(l,{style:t.liveText,children:"LIVE"})]})}export function Compare({block:e}){const r=[...new Set([...e.left.pairs.map(c=>c.key),...e.right.pairs.map(c=>c.key)])];const i=Object.fromEntries(e.left.pairs.map(c=>[c.key,c.value]));const d=Object.fromEntries(e.right.pairs.map(c=>[c.key,c.value]));return a(m,{title:e.title,source:e.source,testID:`buoy-block-compare-${e.id}`,children:[a(s,{style:t.cmpHead,children:[n(l,{style:t.cmpKey}),n(l,{style:t.cmpCol,children:e.left.title}),n(l,{style:t.cmpCol,children:e.right.title})]}),r.map((c,p)=>{const u=JSON.stringify(i[c])!==JSON.stringify(d[c]);return a(s,{style:[t.cmpRow,p>0&&b.rowDivider,u&&t.cmpDiff],children:[n(l,{style:t.cmpKey,numberOfLines:1,children:c}),n(l,{style:[t.cmpVal,u&&{color:o.color.danger}],numberOfLines:2,children:E(i[c])}),n(l,{style:[t.cmpVal,u&&{color:o.color.accent}],numberOfLines:2,children:E(d[c])})]},c)})]})}function M({c:e,id:r}){return a(v,{children:[e.image?n($,{testID:"buoy-internal-chat-image",source:{uri:e.image},style:t.cardImage,resizeMode:"contain",accessibilityLabel:e.title}):null,a(s,{style:t.cardText,children:[n(l,{style:t.cardTitle,numberOfLines:2,children:e.title}),e.subtitle?n(l,{style:b.rowSub,numberOfLines:2,children:e.subtitle}):null,e.facts?.slice(0,4).map(i=>a(s,{style:t.fact,children:[n(l,{style:t.factLabel,children:i.label}),n(l,{style:[t.factValue,i.tone&&{color:O(i.tone)}],children:i.value})]},i.label))]}),n(D,{actions:e.actions,blockId:r})]})}export function CardView({block:e}){return n(m,{source:e.source,testID:`buoy-block-card-${e.id}`,children:n(M,{c:e,id:e.id})})}export function CardCarousel({block:e}){return a(s,{testID:`buoy-block-cardcarousel-${e.id}`,children:[e.title?n(l,{style:t.carouselTitle,children:e.title}):null,n(_,{horizontal:true,showsHorizontalScrollIndicator:false,snapToInterval:232,decelerationRate:"fast",contentContainerStyle:t.carousel,children:e.cards.slice(0,8).map((r,i)=>n(s,{style:t.carouselCard,children:n(M,{c:r,id:`${e.id}-${i}`})},`${r.title}-${i}`))})]})}export function Screenshot({block:e}){const[r,i]=w(false);return a(m,{title:e.route?`Screen \xB7 ${e.route}`:"Screen",source:e.source,testID:`buoy-block-screenshot-${e.id}`,children:[n(f,{onPress:()=>i(d=>!d),accessibilityRole:"imagebutton",accessibilityLabel:e.caption??"Screenshot",children:n($,{testID:"buoy-internal-chat-image",source:{uri:e.url},style:[t.shot,r&&t.shotBig],resizeMode:"contain"})}),e.caption?n(l,{style:t.hint,children:e.caption}):null]})}export function Plan({block:e}){const r=e.steps.filter(i=>i.state==="done").length;return a(m,{title:e.title,testID:`buoy-block-plan-${e.id}`,tone:e.pending?"info":void 0,children:[n(s,{style:t.progress,children:n(s,{style:[t.progressFill,{width:`${r/Math.max(1,e.steps.length)*100}%`}]})}),e.steps.map((i,d)=>a(s,{style:[t.step,d>0&&b.rowDivider],children:[n(s,{style:[t.stepMark,i.state==="done"&&t.stepDone,i.state==="failed"&&t.stepFailed,i.state==="running"&&t.stepRunning],children:i.state==="done"?n(V,{size:10,color:o.color.onAccent}):i.state==="failed"?n(Y,{size:10,color:o.color.text}):n(l,{style:t.stepNum,children:d+1})}),a(l,{style:[b.rowTitle,i.state==="done"&&t.muted],children:[i.text,i.state==="running"?"\u2026":""]})]},d)),n(D,{actions:e.actions??(e.pending?[{label:"Go",primary:true,send:"Go ahead with the plan."},{label:"Change it",send:"Let me change the plan first."}]:void 0),blockId:e.id})]})}export function Checkpoint({block:e}){const r=S();return a(f,{testID:`buoy-block-checkpoint-${e.id}`,disabled:!e.restore,onPress:()=>e.restore&&void r.runAction(e.restore,e.id),style:t.checkpoint,accessibilityRole:"button",children:[n(s,{style:t.cpFlag}),a(l,{style:t.cpText,children:[e.label,e.at?` \xB7 ${e.at}`:""]}),e.restore?n(l,{style:t.cpRestore,children:e.restore.label}):null]})}export function Task({block:e}){const r=e.status==="failed"?o.color.danger:e.status==="done"?o.color.accent:o.color.info;return a(m,{testID:`buoy-block-task-${e.id}`,children:[a(s,{style:t.taskHead,children:[n(l,{style:t.cardTitle,children:e.title}),n(s,{style:[b.pill,{backgroundColor:`${r}22`}],children:n(l,{style:[b.pillText,{color:r}],children:e.status.toUpperCase()})})]}),e.progress!==void 0?n(s,{style:[t.progress,t.pad],children:n(s,{style:[t.progressFill,{width:`${Math.round(e.progress*100)}%`,backgroundColor:r}]})}):null,e.detail?n(l,{style:t.hint,children:e.detail}):null,n(D,{actions:e.actions,blockId:e.id})]})}export function Banner({block:e}){const r=e.tone==="danger"?o.color.danger:e.tone==="warning"?o.color.warning:o.color.info;return a(s,{style:[t.banner,{borderColor:r,backgroundColor:`${r}1A`}],testID:`buoy-block-banner-${e.id}`,children:[n(J,{size:13,color:r}),n(l,{style:[t.bannerText,{color:r}],numberOfLines:2,children:e.text}),e.actions?.[0]?n(D,{actions:e.actions.slice(0,1),blockId:e.id,align:"right"}):null]})}export function File({block:e}){return a(m,{testID:`buoy-block-file-${e.id}`,children:[a(s,{style:b.row,children:[n(s,{style:t.fileIcon,children:n(l,{style:t.fileExt,children:(e.type??e.name.split(".").pop()??"file").toUpperCase().slice(0,4)})}),a(s,{style:t.flex,children:[n(l,{style:b.rowTitle,numberOfLines:1,children:e.name}),e.size?n(l,{style:b.rowSub,children:e.size}):null]})]}),n(D,{actions:e.actions,blockId:e.id})]})}export function Upload({block:e}){const r=S();const i=r.answered[e.id];return a(m,{testID:`buoy-block-upload-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),i?n(l,{style:t.decided,children:i.kind==="upload"?`Attached ${i.name}`:"(skipped)"}):n(s,{style:[t.rowEnd,{justifyContent:"flex-start"}],children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"upload",name:"screenshot.png"}),style:[t.button,t.buttonPlate],children:n(l,{style:t.buttonTextPlate,children:e.accept==="image"?"Choose a screenshot":"Choose a file"})})})]})}const t=I.create({flex:{flex:1},pad:{paddingHorizontal:12,paddingBottom:8},muted:{color:o.color.textSecondary},dim:{opacity:.45},disabled:{opacity:.5},pressed:{opacity:.7},question:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",lineHeight:20,paddingHorizontal:12,paddingTop:12,paddingBottom:8},hint:{color:o.color.textTertiary,fontSize:o.font.micro,paddingHorizontal:12,paddingBottom:10},decided:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"600",paddingHorizontal:12,paddingVertical:10},rowEnd:{flexDirection:"row",justifyContent:"flex-end",paddingHorizontal:12,paddingBottom:12,gap:8},actionsRow:{flexDirection:"row",gap:8,paddingHorizontal:12,paddingVertical:12,alignItems:"center"},button:{paddingHorizontal:14,paddingVertical:9,borderRadius:o.radius.row},buttonPrimary:{backgroundColor:o.color.accent},buttonPlate:{backgroundColor:o.color.button},buttonTextPrimary:{color:o.color.onAccent,fontSize:o.font.caption,fontWeight:"600"},buttonTextPlate:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},chips:{flexDirection:"row",flexWrap:"wrap",gap:6},chip:{paddingHorizontal:12,paddingVertical:7,borderRadius:o.radius.chip,backgroundColor:o.color.fill,borderWidth:I.hairlineWidth,borderColor:o.color.border},chipOn:{backgroundColor:o.color.accentSoft,borderColor:o.color.accentBorderStrong},chipText:{color:o.color.text,fontSize:o.font.caption},chipTextOn:{color:o.color.accent,fontWeight:"600"},optionsBox:{marginHorizontal:8,marginBottom:8,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,overflow:"hidden"},option:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:12,paddingVertical:10},optionLabel:{color:o.color.text,fontSize:o.font.body,fontWeight:"500"},optionDetail:{color:o.color.textSecondary,fontSize:o.font.caption},checkbox:{width:20,height:20,borderRadius:6,borderWidth:1.5,borderColor:o.color.textTertiary,alignItems:"center",justifyContent:"center"},checkboxOn:{backgroundColor:o.color.accent,borderColor:o.color.accent},selectBox:{marginHorizontal:12,marginBottom:12,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,overflow:"hidden"},selectField:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:12,paddingVertical:11},selectPlaceholder:{color:o.color.textSecondary,fontSize:o.font.body},search:{flexDirection:"row",alignItems:"center",gap:6,paddingHorizontal:12,paddingVertical:6,borderTopWidth:I.hairlineWidth,borderTopColor:o.color.separator},searchInput:{flex:1,color:o.color.text,fontSize:o.font.caption,paddingVertical:4},sliderRow:{flexDirection:"row",alignItems:"center",gap:8},stepBtn:{width:28,height:28,borderRadius:14,backgroundColor:o.color.fill,alignItems:"center",justifyContent:"center"},stepText:{color:o.color.text,fontSize:16,fontWeight:"600"},trackHit:{flex:1,height:36,justifyContent:"center"},track:{height:6,borderRadius:3,backgroundColor:o.color.fillTertiary,justifyContent:"center"},fill:{height:6,borderRadius:3,backgroundColor:o.color.accent},thumb:{position:"absolute",width:20,height:20,borderRadius:10,backgroundColor:o.color.knob,marginLeft:-10,shadowColor:"#000",shadowOpacity:.35,shadowRadius:3,shadowOffset:{width:0,height:1}},sliderValue:{width:64,textAlign:"right",color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},inputRow:{flexDirection:"row",alignItems:"center",gap:8,paddingBottom:12},input:{flex:1,height:38,paddingHorizontal:12,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,color:o.color.text,fontSize:o.font.body},fieldRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:10,paddingVertical:6},fieldCol:{paddingVertical:6,gap:6},fieldLabel:{color:o.color.textSecondary,fontSize:o.font.caption,width:110},fieldInput:{flex:1,height:34,paddingHorizontal:10,borderRadius:o.radius.segment,backgroundColor:o.color.surfaceElevated,color:o.color.text,fontSize:o.font.caption},confirmTitle:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",paddingHorizontal:12,paddingTop:12},confirmChange:{color:o.color.text,fontSize:o.font.body,lineHeight:19,paddingHorizontal:12,paddingTop:6,paddingBottom:6},consequence:{color:o.color.warning,fontSize:o.font.caption,paddingHorizontal:12},claim:{gap:6,paddingHorizontal:2},claimText:{color:o.color.text,fontSize:o.font.row,lineHeight:22},cite:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"700"},verified:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"600"},citeBox:{padding:10,borderRadius:o.radius.row,backgroundColor:o.color.surface,borderWidth:I.hairlineWidth,borderColor:o.color.border,gap:2},citeLabel:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},citeDetail:{color:o.color.textSecondary,fontSize:o.font.caption,fontFamily:H.OS==="web"?R:"Menlo"},verifyBtn:{alignSelf:"flex-start",flexDirection:"row",alignItems:"center",gap:5,paddingHorizontal:10,paddingVertical:5,borderRadius:o.radius.chip,backgroundColor:o.color.accentSoft},verifyText:{color:o.color.accent,fontSize:o.font.micro,fontWeight:"700"},detailsBody:{color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:18,paddingHorizontal:12,paddingBottom:12},tabsWrap:{gap:8},tabBar:{flexDirection:"row",gap:6},tab:{paddingHorizontal:12,paddingVertical:6,borderRadius:o.radius.badge,backgroundColor:o.color.fill},tabOn:{backgroundColor:o.color.accentSoft},tabText:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"600"},tabTextOn:{color:o.color.accent},tlRow:{flexDirection:"row",gap:10,paddingHorizontal:12,paddingVertical:4,alignItems:"flex-start"},tlAt:{width:46,color:o.color.textTertiary,fontSize:o.font.micro,fontFamily:H.OS==="web"?R:"Menlo",paddingTop:3},tlLine:{width:12,alignItems:"center"},tlDot:{width:8,height:8,borderRadius:4,marginTop:5},tlStem:{width:1,flex:1,minHeight:18,backgroundColor:o.color.separator,marginTop:2},tlSource:{color:o.color.textTertiary,fontSize:9,fontWeight:"700",letterSpacing:.6},sparkHead:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:12,paddingTop:8},sparkCurrent:{color:o.color.text,fontSize:22,fontWeight:"700"},sparkUnit:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"500"},spark:{flexDirection:"row",alignItems:"flex-end",gap:2,paddingHorizontal:12,paddingTop:6,paddingBottom:6},sparkBar:{flex:1,borderRadius:1},live:{flexDirection:"row",alignItems:"center",gap:5,paddingHorizontal:8,paddingVertical:3,borderRadius:o.radius.badge,backgroundColor:o.color.dangerSoft},liveDot:{width:6,height:6,borderRadius:3,backgroundColor:o.color.danger},liveText:{color:o.color.danger,fontSize:9,fontWeight:"800",letterSpacing:.8},cmpHead:{flexDirection:"row",paddingHorizontal:12,paddingVertical:6,backgroundColor:o.color.fill,gap:8},cmpCol:{flex:1,color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"700",letterSpacing:.6,textTransform:"uppercase"},cmpRow:{flexDirection:"row",paddingHorizontal:12,paddingVertical:8,gap:8,alignItems:"flex-start"},cmpDiff:{backgroundColor:o.color.fill},cmpKey:{width:90,color:o.color.textSecondary,fontSize:o.font.caption},cmpVal:{flex:1,color:o.color.text,fontSize:o.font.caption,fontFamily:H.OS==="web"?R:"Menlo"},cardImage:{width:"100%",height:140,backgroundColor:o.color.fill},cardText:{paddingHorizontal:12,paddingTop:10,paddingBottom:4,gap:4},cardTitle:{color:o.color.text,fontSize:o.font.row,fontWeight:"600"},fact:{flexDirection:"row",justifyContent:"space-between",gap:8},factLabel:{color:o.color.textSecondary,fontSize:o.font.caption},factValue:{color:o.color.text,fontSize:o.font.caption,fontWeight:"500"},carouselTitle:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600",paddingBottom:8},carousel:{gap:8,paddingRight:8},carouselCard:{width:224,borderRadius:o.radius.card,borderWidth:I.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface,overflow:"hidden"},shot:{width:"100%",height:220,backgroundColor:o.color.fill},shotBig:{height:520},progress:{height:4,backgroundColor:o.color.fill,marginHorizontal:12,marginTop:6,marginBottom:8,borderRadius:2,overflow:"hidden"},progressFill:{height:4,backgroundColor:o.color.accent},step:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:12,paddingVertical:9},stepMark:{width:20,height:20,borderRadius:10,backgroundColor:o.color.fillTertiary,alignItems:"center",justifyContent:"center"},stepDone:{backgroundColor:o.color.accent},stepFailed:{backgroundColor:o.color.danger},stepRunning:{backgroundColor:o.color.info},stepNum:{color:o.color.text,fontSize:o.font.micro,fontWeight:"700"},checkpoint:{alignSelf:"flex-start",flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:12,paddingVertical:7,borderRadius:o.radius.chip,backgroundColor:o.color.fill},cpFlag:{width:8,height:8,borderRadius:2,backgroundColor:o.color.info},cpText:{color:o.color.textSecondary,fontSize:o.font.caption},cpRestore:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"600"},taskHead:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8,paddingHorizontal:12,paddingTop:12},banner:{flexDirection:"row",alignItems:"center",gap:8,paddingLeft:12,paddingRight:4,paddingVertical:4,borderRadius:o.radius.row,borderWidth:I.hairlineWidth},bannerText:{flex:1,fontSize:o.font.caption,fontWeight:"600"},fileIcon:{width:40,height:40,borderRadius:8,backgroundColor:o.color.fill,alignItems:"center",justifyContent:"center"},fileExt:{color:o.color.textSecondary,fontSize:10,fontWeight:"800"}});
1
+ "use strict";import{monoFont as P}from"@buoy-gg/shared-ui";import{Platform as H}from"react-native";import{useEffect as K,useRef as W,useState as w}from"react";import{Animated as T,Image as $,Pressable as f,ScrollView as _,StyleSheet as I,Switch as J,Text as l,TextInput as V,View as c}from"react-native";import{AlertTriangle as k,Check as B,ChevronDown as L,ChevronRight as O,night as o,NightSegmented as G,RefreshCw as X,Search as Q,X as Y}from"@buoy-gg/shared-ui";import{useBlockHandlers as S}from"./context";import{ActionRow as v,blockStyles as b,Card as y,More as Z,toneColor as E,usePaged as ee}from"./primitives";import{multiAnswer as te,selectView as oe,shownFormValues as j}from"./choiceLogic";import{jsx as n,jsxs as a,Fragment as D}from"react/jsx-runtime";const A=e=>typeof e==="string"?e:e===void 0?"\u2014":JSON.stringify(e);export function Suggestions({block:e}){const r=S();return n(c,{style:t.chips,testID:`buoy-block-suggestions-${e.id}`,children:e.chips.slice(0,4).map(i=>n(f,{disabled:!!r.busy,onPress:()=>void r.runAction({label:i.label,send:i.send??i.label},e.id),style:({pressed:d})=>[t.chip,d&&t.pressed],accessibilityRole:"button",children:n(l,{style:t.chipText,children:i.label})},i.label))})}export function MultiSelect({block:e}){const r=S();const i=r.answered[e.id];const d=!!i;const[s,p]=w([]);const u=i?.kind==="multi"?i.optionIds:s;const h=m=>p(x=>x.includes(m)?x.filter(g=>g!==m):[...x,m]);const C=s.length>=(e.min??1)&&(!e.max||s.length<=e.max);return a(y,{testID:`buoy-block-multi-${e.id}`,tone:d?void 0:"info",children:[n(l,{style:t.question,children:e.question}),n(c,{style:t.optionsBox,children:e.options.map((m,x)=>{const g=u.includes(m.id);return a(f,{disabled:d,onPress:()=>h(m.id),style:[t.option,x>0&&b.rowDivider,d&&!g&&t.dim],accessibilityRole:"checkbox",accessibilityState:{checked:g},children:[n(c,{style:[t.checkbox,g&&t.checkboxOn],children:g?n(B,{size:11,color:o.color.onAccent}):null}),a(c,{style:t.flex,children:[n(l,{style:t.optionLabel,children:m.label}),m.detail?n(l,{style:t.optionDetail,children:m.detail}):null]})]},m.id)})}),d?null:n(c,{style:t.rowEnd,children:n(f,{testID:"buoy-block-multi-submit",disabled:!C,onPress:()=>r.answer({blockId:e.id,kind:"multi",...te(e.options,s)}),style:[t.button,t.buttonPrimary,!C&&t.disabled],children:n(l,{style:t.buttonTextPrimary,children:e.submitLabel??`Use ${s.length||""}`.trim()})})})]})}export function Select({block:e}){const r=S();const i=r.answered[e.id];const[d,s]=w("");const[p,u]=w(false);const h=i?.kind==="select"?i.label:null;const{showSearch:C,items:m,hidden:x}=oe(e.options,d,e.searchable);return a(y,{testID:`buoy-block-select-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),h?n(l,{style:t.decided,children:h}):a(c,{style:t.selectBox,children:[a(f,{onPress:()=>u(g=>!g),style:t.selectField,accessibilityRole:"button",children:[n(l,{style:t.selectPlaceholder,children:p?"Choose\u2026":`${e.options.length} options`}),n(L,{size:14,color:o.color.textSecondary})]}),p&&a(D,{children:[C&&a(c,{style:t.search,children:[n(Q,{size:12,color:o.color.textSecondary}),n(V,{style:t.searchInput,value:d,onChangeText:s,placeholder:"Search",placeholderTextColor:o.color.placeholder,autoCapitalize:"none",autoCorrect:false})]}),m.map((g,R)=>a(f,{onPress:()=>r.answer({blockId:e.id,kind:"select",optionId:g.id,label:g.label}),style:[t.option,R>0&&b.rowDivider],accessibilityRole:"button",children:[a(c,{style:t.flex,children:[n(l,{style:t.optionLabel,children:g.label}),g.detail?n(l,{style:t.optionDetail,children:g.detail}):null]}),n(O,{size:12,color:o.color.textTertiary})]},g.id)),x>0?a(l,{style:t.hint,children:[x," more \u2014 keep typing"]}):null]})]})]})}function F({min:e,max:r,step:i=1,value:d,unit:s,onChange:p,disabled:u}){const h=W(0);const C=r>e?(d-e)/(r-e)*100:0;const m=z=>Math.min(r,Math.max(e,+(Math.round((z-e)/i)*i+e).toFixed(6)));const x=z=>!u&&p(m(d+z*i));const g=z=>{if(u||h.current<=0)return;const U=Math.min(1,Math.max(0,z.nativeEvent.locationX/h.current));p(m(e+U*(r-e)))};const R=z=>{h.current=z.nativeEvent.layout.width};return a(c,{style:t.sliderRow,children:[n(f,{disabled:u,onPress:()=>x(-1),style:t.stepBtn,accessibilityRole:"button",accessibilityLabel:"Decrease",children:n(l,{style:t.stepText,children:"\u2212"})}),n(f,{style:t.trackHit,onLayout:R,onPress:g,disabled:u,accessibilityRole:"adjustable",accessibilityValue:{min:e,max:r,now:d},accessibilityActions:[{name:"increment"},{name:"decrement"}],onAccessibilityAction:z=>x(z.nativeEvent.actionName==="increment"?1:-1),children:a(c,{style:t.track,pointerEvents:"none",children:[n(c,{style:[t.fill,{width:`${C}%`}]}),n(c,{style:[t.thumb,{left:`${C}%`}]})]})}),n(f,{disabled:u,onPress:()=>x(1),style:t.stepBtn,accessibilityRole:"button",accessibilityLabel:"Increase",children:n(l,{style:t.stepText,children:"+"})}),a(l,{style:t.sliderValue,children:[d,s?` ${s}`:""]})]})}export function Slider({block:e}){const r=S();const i=r.answered[e.id];const[d,s]=w(e.value??e.min);return a(y,{testID:`buoy-block-slider-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),n(c,{style:t.pad,children:n(F,{min:e.min,max:e.max,step:e.step,value:i?.kind==="slider"?i.value:d,unit:e.unit,onChange:s,disabled:!!i})}),i?null:n(c,{style:t.rowEnd,children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"slider",value:d,unit:e.unit}),style:[t.button,t.buttonPrimary],children:a(l,{style:t.buttonTextPrimary,children:["Use ",d,e.unit?` ${e.unit}`:""]})})})]})}export function DateTime({block:e}){const r=S();const i=r.answered[e.id];const[d,s]=w("");const p=e.presets??[{label:"Now",value:new Date().toISOString()},{label:"Tomorrow",value:new Date(Date.now()+864e5).toISOString()},{label:"+7 days",value:new Date(Date.now()+7*864e5).toISOString()},{label:"+30 days",value:new Date(Date.now()+30*864e5).toISOString()}];return a(y,{testID:`buoy-block-datetime-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),i?n(l,{style:t.decided,children:i.kind==="dateTime"?i.label??i.value:"(skipped)"}):a(D,{children:[n(c,{style:[t.chips,t.pad],children:p.map(u=>n(f,{onPress:()=>r.answer({blockId:e.id,kind:"dateTime",value:u.value,label:u.label}),style:t.chip,children:n(l,{style:t.chipText,children:u.label})},u.label))}),a(c,{style:[t.inputRow,t.pad],children:[n(V,{style:t.input,value:d,onChangeText:s,placeholder:e.mode==="time"?"14:30":"2026-09-15",placeholderTextColor:o.color.placeholder,keyboardType:"numbers-and-punctuation"}),n(f,{disabled:!d.trim(),onPress:()=>r.answer({blockId:e.id,kind:"dateTime",value:d.trim()}),style:[t.button,t.buttonPrimary,!d.trim()&&t.disabled],children:n(l,{style:t.buttonTextPrimary,children:"Use"})})]})]})]})}function M({f:e,value:r,onChange:i,disabled:d}){if(e.type==="toggle"){return a(c,{style:t.fieldRow,children:[n(l,{style:t.fieldLabel,children:e.label}),n(J,{value:!!r,onValueChange:i,disabled:d,trackColor:{true:o.color.accent,false:o.color.fillTertiary},thumbColor:o.color.knob})]})}if(e.type==="select"){return a(c,{style:t.fieldCol,children:[n(l,{style:t.fieldLabel,children:e.label}),n(c,{style:t.chips,children:e.options.map(s=>n(f,{disabled:d,onPress:()=>i(s.id),style:[t.chip,r===s.id&&t.chipOn],children:n(l,{style:[t.chipText,r===s.id&&t.chipTextOn],children:s.label})},s.id))})]})}if(e.type==="slider"){return a(c,{style:t.fieldCol,children:[n(l,{style:t.fieldLabel,children:e.label}),n(F,{min:e.min,max:e.max,step:e.step,unit:e.unit,value:typeof r==="number"?r:e.min,onChange:i,disabled:d})]})}return a(c,{style:t.fieldRow,children:[n(l,{style:t.fieldLabel,children:e.label}),n(V,{style:t.fieldInput,value:r===void 0||r===null?"":String(r),onChangeText:i,editable:!d,placeholder:e.placeholder??"",placeholderTextColor:o.color.placeholder,keyboardType:e.type==="number"?"numeric":"ascii-capable",secureTextEntry:e.type==="secret",autoCapitalize:"none",autoCorrect:false})]})}function q(e){const[r,i]=w(()=>Object.fromEntries(e.map(d=>[d.key,d.value])));return{values:r,set:(d,s)=>i(p=>({...p,[d]:s}))}}export function Form({block:e}){const r=S();const i=r.answered[e.id];const{values:d,set:s}=q(e.fields);const p=j(i,d);return a(y,{title:e.title,testID:`buoy-block-form-${e.id}`,tone:i?void 0:"info",children:[n(c,{style:t.pad,children:e.fields.map(u=>n(M,{f:u,value:p[u.key],onChange:h=>s(u.key,h),disabled:!!i},u.key))}),i?n(l,{style:t.decided,children:"Sent"}):n(c,{style:t.rowEnd,children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"form",values:p}),style:[t.button,t.buttonPrimary],children:n(l,{style:t.buttonTextPrimary,children:e.submitLabel??"Send"})})})]})}export function Proposal({block:e}){const r=S();const i=r.answered[e.id];const{values:d,set:s}=q(e.fields);const p=j(i,d);const u=i?.kind==="proposal"?i.approved:null;return a(y,{testID:`buoy-block-proposal-${e.id}`,tone:i?void 0:"warning",children:[n(l,{style:t.confirmTitle,children:e.title}),n(l,{style:t.confirmChange,children:e.change}),n(c,{style:t.pad,children:e.fields.map(h=>n(M,{f:h,value:p[h.key],onChange:C=>s(h.key,C),disabled:!!i},h.key))}),e.consequence?n(l,{style:t.consequence,children:e.consequence}):null,n(c,{style:t.actionsRow,children:i?n(l,{style:t.decided,children:u?"\u2713 Applied with these values":"\u2715 Not applied"}):a(D,{children:[n(f,{onPress:()=>r.answer({blockId:e.id,kind:"proposal",approved:true,values:p}),style:[t.button,t.buttonPrimary],children:n(l,{style:t.buttonTextPrimary,children:e.approveLabel??"Apply"})}),n(f,{onPress:()=>r.answer({blockId:e.id,kind:"proposal",approved:false}),style:[t.button,t.buttonPlate],children:n(l,{style:t.buttonTextPlate,children:"Not now"})})]})})]})}export function Claim({block:e}){const r=S();const[i,d]=w(null);const s=r.actionState?.[`${e.id}:${e.verify?.label??"Verify"}`];const p=e.verified||s==="done";return a(c,{style:t.claim,testID:`buoy-block-claim-${e.id}`,children:[a(l,{style:t.claimText,children:[e.text,e.sources.map((u,h)=>a(l,{onPress:()=>d(i===h?null:h),style:t.cite,children:[" [",h+1,"]"]},h)),p?n(l,{style:t.verified,children:" \u2713 verified"}):null]}),i!==null&&e.sources[i]?a(c,{style:t.citeBox,children:[a(l,{style:t.citeLabel,children:["[",i+1,"] ",e.sources[i].label]}),e.sources[i].detail?n(l,{style:t.citeDetail,children:e.sources[i].detail}):null]}):null,e.verify&&!p?a(f,{onPress:()=>void r.runAction(e.verify,e.id),style:t.verifyBtn,accessibilityRole:"button",children:[n(X,{size:11,color:o.color.accent}),n(l,{style:t.verifyText,children:s==="running"?"Checking\u2026":e.verify.label})]}):null]})}export function Details({block:e}){const[r,i]=w(!!e.open);return a(y,{testID:`buoy-block-details-${e.id}`,children:[a(f,{onPress:()=>i(d=>!d),style:b.row,accessibilityRole:"button",accessibilityState:{expanded:r},children:[n(l,{style:b.rowTitle,children:e.summary}),r?n(L,{size:12,color:o.color.textTertiary}):n(O,{size:12,color:o.color.textTertiary})]}),r?n(l,{style:t.detailsBody,selectable:true,children:e.body}):null]})}export function Tabs({block:e,renderNested:r}){const[i,d]=w(0);const s=e.tabs.map((u,h)=>({key:String(h),label:u.label}));const p=e.tabs[i];return a(c,{style:t.tabsWrap,testID:`buoy-block-tabs-${e.id}`,children:[n(G,{tabs:s,activeKey:String(i),onChange:u=>d(Number(u)),size:"sm"}),p&&r?r(p.block):null]})}const ne=10;export function Timeline({block:e}){const r=ee(e.events,ne);return a(y,{title:e.title??"What happened",source:e.source,testID:`buoy-block-timeline-${e.id}`,children:[r.visible.map((i,d)=>a(c,{style:t.tlRow,children:[n(l,{style:t.tlAt,children:i.at}),a(c,{style:t.tlLine,children:[n(c,{style:[t.tlDot,{backgroundColor:E(i.tone??"muted")}]}),d<r.visible.length-1?n(c,{style:t.tlStem}):null]}),a(c,{style:t.flex,children:[n(l,{style:t.tlSource,children:i.source.toUpperCase()}),n(l,{style:b.rowTitle,numberOfLines:2,children:i.label})]})]},d)),n(Z,{paged:r})]})}export function Sparkline({block:e}){const r=e.series.slice(-60);const i=Math.max(1,...r);const d=Math.min(...r,0);const s=44;return a(y,{title:e.title,source:e.source,testID:`buoy-block-sparkline-${e.id}`,children:[a(c,{style:t.sparkHead,children:[a(l,{style:t.sparkCurrent,children:[e.current??r[r.length-1],e.unit?a(l,{style:t.sparkUnit,children:[" ",e.unit]}):null]}),e.live?n(LivePill,{}):null]}),n(c,{style:[t.spark,{height:s}],children:r.map((p,u)=>n(c,{style:[t.sparkBar,{height:Math.max(2,(p-d)/(i-d||1)*s),backgroundColor:u===r.length-1?o.color.accent:o.color.accentBorderStrong}]},u))}),e.label?n(l,{style:t.hint,children:e.label}):null]})}export function LivePill(){const e=W(new T.Value(1)).current;K(()=>{const r=T.loop(T.sequence([T.timing(e,{toValue:.3,duration:700,useNativeDriver:true}),T.timing(e,{toValue:1,duration:700,useNativeDriver:true})]));r.start();return()=>r.stop()},[e]);return a(c,{style:t.live,children:[n(T.View,{style:[t.liveDot,{opacity:e}]}),n(l,{style:t.liveText,children:"LIVE"})]})}export function Compare({block:e}){const r=[...new Set([...e.left.pairs.map(s=>s.key),...e.right.pairs.map(s=>s.key)])];const i=Object.fromEntries(e.left.pairs.map(s=>[s.key,s.value]));const d=Object.fromEntries(e.right.pairs.map(s=>[s.key,s.value]));return a(y,{title:e.title,source:e.source,testID:`buoy-block-compare-${e.id}`,children:[a(c,{style:t.cmpHead,children:[n(l,{style:t.cmpKey}),n(l,{style:t.cmpCol,children:e.left.title}),n(l,{style:t.cmpCol,children:e.right.title})]}),r.map((s,p)=>{const u=JSON.stringify(i[s])!==JSON.stringify(d[s]);return a(c,{style:[t.cmpRow,p>0&&b.rowDivider,u&&t.cmpDiff],children:[n(l,{style:t.cmpKey,numberOfLines:1,children:s}),n(l,{style:[t.cmpVal,u&&{color:o.color.danger}],numberOfLines:2,children:A(i[s])}),n(l,{style:[t.cmpVal,u&&{color:o.color.accent}],numberOfLines:2,children:A(d[s])})]},s)})]})}function N({c:e,id:r}){return a(D,{children:[e.image?n($,{testID:"buoy-internal-chat-image",source:{uri:e.image},style:t.cardImage,resizeMode:"contain",accessibilityLabel:e.title}):null,a(c,{style:t.cardText,children:[n(l,{style:t.cardTitle,numberOfLines:2,children:e.title}),e.subtitle?n(l,{style:b.rowSub,numberOfLines:2,children:e.subtitle}):null,e.facts?.slice(0,4).map(i=>a(c,{style:t.fact,children:[n(l,{style:t.factLabel,children:i.label}),n(l,{style:[t.factValue,i.tone&&{color:E(i.tone)}],children:i.value})]},i.label))]}),n(v,{actions:e.actions,blockId:r})]})}export function CardView({block:e}){return n(y,{source:e.source,testID:`buoy-block-card-${e.id}`,children:n(N,{c:e,id:e.id})})}export function CardCarousel({block:e}){return a(c,{testID:`buoy-block-cardcarousel-${e.id}`,children:[e.title?n(l,{style:t.carouselTitle,children:e.title}):null,n(_,{horizontal:true,showsHorizontalScrollIndicator:false,snapToInterval:232,decelerationRate:"fast",contentContainerStyle:t.carousel,children:e.cards.slice(0,8).map((r,i)=>n(c,{style:t.carouselCard,children:n(N,{c:r,id:`${e.id}-${i}`})},`${r.title}-${i}`))})]})}export function Screenshot({block:e}){const[r,i]=w(false);return a(y,{title:e.route?`Screen \xB7 ${e.route}`:"Screen",source:e.source,testID:`buoy-block-screenshot-${e.id}`,children:[n(f,{onPress:()=>i(d=>!d),accessibilityRole:"imagebutton",accessibilityLabel:e.caption??"Screenshot",children:n($,{testID:"buoy-internal-chat-image",source:{uri:e.url},style:[t.shot,r&&t.shotBig],resizeMode:"contain"})}),e.caption?n(l,{style:t.hint,children:e.caption}):null]})}export function Plan({block:e}){const r=e.steps.filter(i=>i.state==="done").length;return a(y,{title:e.title,testID:`buoy-block-plan-${e.id}`,tone:e.pending?"info":void 0,children:[n(c,{style:t.progress,children:n(c,{style:[t.progressFill,{width:`${r/Math.max(1,e.steps.length)*100}%`}]})}),e.steps.map((i,d)=>a(c,{style:[t.step,d>0&&b.rowDivider],children:[n(c,{style:[t.stepMark,i.state==="done"&&t.stepDone,i.state==="failed"&&t.stepFailed,i.state==="running"&&t.stepRunning],children:i.state==="done"?n(B,{size:10,color:o.color.onAccent}):i.state==="failed"?n(Y,{size:10,color:o.color.text}):n(l,{style:t.stepNum,children:d+1})}),a(l,{style:[b.rowTitle,i.state==="done"&&t.muted],children:[i.text,i.state==="running"?"\u2026":""]})]},d)),n(v,{actions:e.actions??(e.pending?[{label:"Go",primary:true,send:"Go ahead with the plan."},{label:"Change it",send:"Let me change the plan first."}]:void 0),blockId:e.id})]})}export function Checkpoint({block:e}){const r=S();return a(f,{testID:`buoy-block-checkpoint-${e.id}`,disabled:!e.restore,onPress:()=>e.restore&&void r.runAction(e.restore,e.id),style:t.checkpoint,accessibilityRole:"button",children:[n(c,{style:t.cpFlag}),a(l,{style:t.cpText,children:[e.label,e.at?` \xB7 ${e.at}`:""]}),e.restore?n(l,{style:t.cpRestore,children:e.restore.label}):null]})}export function Task({block:e}){const r=e.status==="failed"?o.color.danger:e.status==="done"?o.color.accent:o.color.info;return a(y,{testID:`buoy-block-task-${e.id}`,children:[a(c,{style:t.taskHead,children:[n(l,{style:t.cardTitle,children:e.title}),n(c,{style:[b.pill,{backgroundColor:`${r}22`}],children:n(l,{style:[b.pillText,{color:r}],children:e.status.toUpperCase()})})]}),e.progress!==void 0?n(c,{style:[t.progress,t.pad],children:n(c,{style:[t.progressFill,{width:`${Math.round(e.progress*100)}%`,backgroundColor:r}]})}):null,e.detail?n(l,{style:t.hint,children:e.detail}):null,n(v,{actions:e.actions,blockId:e.id})]})}export function Banner({block:e}){const r=e.tone==="danger"?o.color.danger:e.tone==="warning"?o.color.warning:o.color.info;return a(c,{style:[t.banner,{borderColor:r,backgroundColor:`${r}1A`}],testID:`buoy-block-banner-${e.id}`,children:[n(k,{size:13,color:r}),n(l,{style:[t.bannerText,{color:r}],numberOfLines:2,children:e.text}),e.actions?.[0]?n(v,{actions:e.actions.slice(0,1),blockId:e.id,align:"right"}):null]})}export function File({block:e}){return a(y,{testID:`buoy-block-file-${e.id}`,children:[a(c,{style:b.row,children:[n(c,{style:t.fileIcon,children:n(l,{style:t.fileExt,children:(e.type??e.name.split(".").pop()??"file").toUpperCase().slice(0,4)})}),a(c,{style:t.flex,children:[n(l,{style:b.rowTitle,numberOfLines:1,children:e.name}),e.size?n(l,{style:b.rowSub,children:e.size}):null]})]}),n(v,{actions:e.actions,blockId:e.id})]})}export function Upload({block:e}){const r=S();const i=r.answered[e.id];return a(y,{testID:`buoy-block-upload-${e.id}`,tone:i?void 0:"info",children:[n(l,{style:t.question,children:e.label}),i?n(l,{style:t.decided,children:i.kind==="upload"?`Attached ${i.name}`:"(skipped)"}):n(c,{style:[t.rowEnd,{justifyContent:"flex-start"}],children:n(f,{onPress:()=>r.answer({blockId:e.id,kind:"upload",name:"screenshot.png"}),style:[t.button,t.buttonPlate],children:n(l,{style:t.buttonTextPlate,children:e.accept==="image"?"Choose a screenshot":"Choose a file"})})})]})}const t=I.create({flex:{flex:1},pad:{paddingHorizontal:12,paddingBottom:8},muted:{color:o.color.textSecondary},dim:{opacity:.45},disabled:{opacity:.5},pressed:{opacity:.7},question:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",lineHeight:20,paddingHorizontal:12,paddingTop:12,paddingBottom:8},hint:{color:o.color.textTertiary,fontSize:o.font.micro,paddingHorizontal:12,paddingBottom:10},decided:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"600",paddingHorizontal:12,paddingVertical:10},rowEnd:{flexDirection:"row",justifyContent:"flex-end",paddingHorizontal:12,paddingBottom:12,gap:8},actionsRow:{flexDirection:"row",gap:8,paddingHorizontal:12,paddingVertical:12,alignItems:"center"},button:{paddingHorizontal:14,paddingVertical:9,borderRadius:o.radius.row},buttonPrimary:{backgroundColor:o.color.accent},buttonPlate:{backgroundColor:o.color.button},buttonTextPrimary:{color:o.color.onAccent,fontSize:o.font.caption,fontWeight:"600"},buttonTextPlate:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},chips:{flexDirection:"row",flexWrap:"wrap",gap:6},chip:{paddingHorizontal:12,paddingVertical:7,borderRadius:o.radius.chip,backgroundColor:o.color.fill,borderWidth:I.hairlineWidth,borderColor:o.color.border},chipOn:{backgroundColor:o.color.accentSoft,borderColor:o.color.accentBorderStrong},chipText:{color:o.color.text,fontSize:o.font.caption},chipTextOn:{color:o.color.accent,fontWeight:"600"},optionsBox:{marginHorizontal:8,marginBottom:8,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,overflow:"hidden"},option:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:12,paddingVertical:10},optionLabel:{color:o.color.text,fontSize:o.font.body,fontWeight:"500"},optionDetail:{color:o.color.textSecondary,fontSize:o.font.caption},checkbox:{width:20,height:20,borderRadius:6,borderWidth:1.5,borderColor:o.color.textTertiary,alignItems:"center",justifyContent:"center"},checkboxOn:{backgroundColor:o.color.accent,borderColor:o.color.accent},selectBox:{marginHorizontal:12,marginBottom:12,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,overflow:"hidden"},selectField:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:12,paddingVertical:11},selectPlaceholder:{color:o.color.textSecondary,fontSize:o.font.body},search:{flexDirection:"row",alignItems:"center",gap:6,paddingHorizontal:12,paddingVertical:6,borderTopWidth:I.hairlineWidth,borderTopColor:o.color.separator},searchInput:{flex:1,color:o.color.text,fontSize:o.font.caption,paddingVertical:4},sliderRow:{flexDirection:"row",alignItems:"center",gap:8},stepBtn:{width:28,height:28,borderRadius:14,backgroundColor:o.color.fill,alignItems:"center",justifyContent:"center"},stepText:{color:o.color.text,fontSize:16,fontWeight:"600"},trackHit:{flex:1,height:36,justifyContent:"center"},track:{height:6,borderRadius:3,backgroundColor:o.color.fillTertiary,justifyContent:"center"},fill:{height:6,borderRadius:3,backgroundColor:o.color.accent},thumb:{position:"absolute",width:20,height:20,borderRadius:10,backgroundColor:o.color.knob,marginLeft:-10,shadowColor:"#000",shadowOpacity:.35,shadowRadius:3,shadowOffset:{width:0,height:1}},sliderValue:{width:64,textAlign:"right",color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},inputRow:{flexDirection:"row",alignItems:"center",gap:8,paddingBottom:12},input:{flex:1,height:38,paddingHorizontal:12,borderRadius:o.radius.row,backgroundColor:o.color.surfaceElevated,color:o.color.text,fontSize:o.font.body},fieldRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:10,paddingVertical:6},fieldCol:{paddingVertical:6,gap:6},fieldLabel:{color:o.color.textSecondary,fontSize:o.font.caption,width:110},fieldInput:{flex:1,height:34,paddingHorizontal:10,borderRadius:o.radius.segment,backgroundColor:o.color.surfaceElevated,color:o.color.text,fontSize:o.font.caption},confirmTitle:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",paddingHorizontal:12,paddingTop:12},confirmChange:{color:o.color.text,fontSize:o.font.body,lineHeight:19,paddingHorizontal:12,paddingTop:6,paddingBottom:6},consequence:{color:o.color.warning,fontSize:o.font.caption,paddingHorizontal:12},claim:{gap:6,paddingHorizontal:2},claimText:{color:o.color.text,fontSize:o.font.row,lineHeight:22},cite:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"700"},verified:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"600"},citeBox:{padding:10,borderRadius:o.radius.row,backgroundColor:o.color.surface,borderWidth:I.hairlineWidth,borderColor:o.color.border,gap:2},citeLabel:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},citeDetail:{color:o.color.textSecondary,fontSize:o.font.caption,fontFamily:H.OS==="web"?P:"Menlo"},verifyBtn:{alignSelf:"flex-start",flexDirection:"row",alignItems:"center",gap:5,paddingHorizontal:10,paddingVertical:5,borderRadius:o.radius.chip,backgroundColor:o.color.accentSoft},verifyText:{color:o.color.accent,fontSize:o.font.micro,fontWeight:"700"},detailsBody:{color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:18,paddingHorizontal:12,paddingBottom:12},tabsWrap:{gap:8},tabBar:{flexDirection:"row",gap:6},tab:{paddingHorizontal:12,paddingVertical:6,borderRadius:o.radius.badge,backgroundColor:o.color.fill},tabOn:{backgroundColor:o.color.accentSoft},tabText:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"600"},tabTextOn:{color:o.color.accent},tlRow:{flexDirection:"row",gap:10,paddingHorizontal:12,paddingVertical:4,alignItems:"flex-start"},tlAt:{width:46,color:o.color.textTertiary,fontSize:o.font.micro,fontFamily:H.OS==="web"?P:"Menlo",paddingTop:3},tlLine:{width:12,alignItems:"center"},tlDot:{width:8,height:8,borderRadius:4,marginTop:5},tlStem:{width:1,flex:1,minHeight:18,backgroundColor:o.color.separator,marginTop:2},tlSource:{color:o.color.textTertiary,fontSize:9,fontWeight:"700",letterSpacing:.6},sparkHead:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:12,paddingTop:8},sparkCurrent:{color:o.color.text,fontSize:22,fontWeight:"700"},sparkUnit:{color:o.color.textSecondary,fontSize:o.font.caption,fontWeight:"500"},spark:{flexDirection:"row",alignItems:"flex-end",gap:2,paddingHorizontal:12,paddingTop:6,paddingBottom:6},sparkBar:{flex:1,borderRadius:1},live:{flexDirection:"row",alignItems:"center",gap:5,paddingHorizontal:8,paddingVertical:3,borderRadius:o.radius.badge,backgroundColor:o.color.dangerSoft},liveDot:{width:6,height:6,borderRadius:3,backgroundColor:o.color.danger},liveText:{color:o.color.danger,fontSize:9,fontWeight:"800",letterSpacing:.8},cmpHead:{flexDirection:"row",paddingHorizontal:12,paddingVertical:6,backgroundColor:o.color.fill,gap:8},cmpCol:{flex:1,color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"700",letterSpacing:.6,textTransform:"uppercase"},cmpRow:{flexDirection:"row",paddingHorizontal:12,paddingVertical:8,gap:8,alignItems:"flex-start"},cmpDiff:{backgroundColor:o.color.fill},cmpKey:{width:90,color:o.color.textSecondary,fontSize:o.font.caption},cmpVal:{flex:1,color:o.color.text,fontSize:o.font.caption,fontFamily:H.OS==="web"?P:"Menlo"},cardImage:{width:"100%",height:140,backgroundColor:o.color.fill},cardText:{paddingHorizontal:12,paddingTop:10,paddingBottom:4,gap:4},cardTitle:{color:o.color.text,fontSize:o.font.row,fontWeight:"600"},fact:{flexDirection:"row",justifyContent:"space-between",gap:8},factLabel:{color:o.color.textSecondary,fontSize:o.font.caption},factValue:{color:o.color.text,fontSize:o.font.caption,fontWeight:"500"},carouselTitle:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600",paddingBottom:8},carousel:{gap:8,paddingRight:8},carouselCard:{width:224,borderRadius:o.radius.card,borderWidth:I.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface,overflow:"hidden"},shot:{width:"100%",height:220,backgroundColor:o.color.fill},shotBig:{height:520},progress:{height:4,backgroundColor:o.color.fill,marginHorizontal:12,marginTop:6,marginBottom:8,borderRadius:2,overflow:"hidden"},progressFill:{height:4,backgroundColor:o.color.accent},step:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:12,paddingVertical:9},stepMark:{width:20,height:20,borderRadius:10,backgroundColor:o.color.fillTertiary,alignItems:"center",justifyContent:"center"},stepDone:{backgroundColor:o.color.accent},stepFailed:{backgroundColor:o.color.danger},stepRunning:{backgroundColor:o.color.info},stepNum:{color:o.color.text,fontSize:o.font.micro,fontWeight:"700"},checkpoint:{alignSelf:"flex-start",flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:12,paddingVertical:7,borderRadius:o.radius.chip,backgroundColor:o.color.fill},cpFlag:{width:8,height:8,borderRadius:2,backgroundColor:o.color.info},cpText:{color:o.color.textSecondary,fontSize:o.font.caption},cpRestore:{color:o.color.accent,fontSize:o.font.caption,fontWeight:"600"},taskHead:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",gap:8,paddingHorizontal:12,paddingTop:12},banner:{flexDirection:"row",alignItems:"center",gap:8,paddingLeft:12,paddingRight:4,paddingVertical:4,borderRadius:o.radius.row,borderWidth:I.hairlineWidth},bannerText:{flex:1,fontSize:o.font.caption,fontWeight:"600"},fileIcon:{width:40,height:40,borderRadius:8,backgroundColor:o.color.fill,alignItems:"center",justifyContent:"center"},fileExt:{color:o.color.textSecondary,fontSize:10,fontWeight:"800"}});
@@ -0,0 +1 @@
1
+ "use strict";export const SEARCH_FROM=8;export const SEARCH_CAP=8;export function selectView(e,o,n){const t=n!==false&&e.length>=SEARCH_FROM;const r=t?o.trim().toLowerCase():"";const l=r?e.filter(i=>i.label.toLowerCase().includes(r)):e;const s=t?l.slice(0,SEARCH_CAP):l;return{showSearch:t,items:s,hidden:l.length-s.length}}export function multiAnswer(e,o){const n=e.filter(t=>o.includes(t.id));return{optionIds:n.map(t=>t.id),labels:n.map(t=>t.label)}}export function shownFormValues(e,o){if(e?.kind==="form")return e.values;if(e?.kind==="proposal"&&e.approved&&e.values)return e.values;return o}
@@ -0,0 +1 @@
1
+ "use strict";import{describe as r,expect as t,it as s}from"vitest";import{multiAnswer as d,selectView as l,shownFormValues as i}from"./choiceLogic";const n=(e,o=a=>`Option ${a}`)=>Array.from({length:e},(a,h)=>({id:`o${h}`,label:o(h)}));r("selectView",()=>{s("a short list shows every option and no search",()=>{const e=l(n(5),"");t(e.showSearch).toBe(false);t(e.items).toHaveLength(5);t(e.hidden).toBe(0)});s("searchable:false with a long list shows ALL options, not 8",()=>{const e=l(n(20),"",false);t(e.showSearch).toBe(false);t(e.items).toHaveLength(20);t(e.hidden).toBe(0)});s("searchable:false ignores a stale query",()=>{t(l(n(20),"zzz",false).items).toHaveLength(20)});s("a long searchable list caps at 8 and counts the rest",()=>{const e=l(n(20),"");t(e.showSearch).toBe(true);t(e.items.map(o=>o.id)).toEqual(n(8).map(o=>o.id));t(e.hidden).toBe(12)});s("the 'more' count only counts options that match the query",()=>{const e=n(20,a=>a%2?`odd ${a}`:`even ${a}`);const o=l(e,"EVEN");t(o.items).toHaveLength(8);t(o.items.every(a=>a.label.startsWith("even"))).toBe(true);t(o.hidden).toBe(2)});s("no 'more' count when every match fits",()=>{const e=n(20,a=>a<3?`pick ${a}`:`other ${a}`);const o=l(e,"pick");t(o.items).toHaveLength(3);t(o.hidden).toBe(0)});s("no matches shows nothing and counts nothing",()=>{const e=l(n(20),"nope");t(e.items).toHaveLength(0);t(e.hidden).toBe(0)})});r("multiAnswer",()=>{const e=[{id:"a",label:"Alpha"},{id:"b",label:"Bravo"},{id:"c",label:"Charlie"}];s("ids and labels are both in list order, whatever the tap order",()=>{t(d(e,["c","a"])).toEqual({optionIds:["a","c"],labels:["Alpha","Charlie"]})});s("drops ids that are not options so ids and labels pair up",()=>{t(d(e,["zz","b"])).toEqual({optionIds:["b"],labels:["Bravo"]})})});r("shownFormValues",()=>{const e={port:8080};s("unanswered shows local edits",()=>{t(i(void 0,e)).toBe(e)});s("a sent form shows the sent values, not reset local state",()=>{const o={port:9e3};t(i({blockId:"f",kind:"form",values:o},{})).toBe(o)});s("an applied proposal shows the applied values",()=>{const o={port:9e3};t(i({blockId:"p",kind:"proposal",approved:true,values:o},e)).toBe(o)});s("a declined proposal keeps local values (nothing was applied)",()=>{t(i({blockId:"p",kind:"proposal",approved:false},e)).toBe(e)})});
@@ -1 +1 @@
1
- "use strict";import{useState as h}from"react";import{Pressable as p,ScrollView as b,StyleSheet as u,Text as r,TextInput as y,View as s}from"react-native";import{AlertTriangle as f,ChevronDown as g,night as o}from"@buoy-gg/shared-ui";import{jsx as t,jsxs as d}from"react/jsx-runtime";export function ApprovalCard({pending:l,onAnswer:n}){const[i,a]=h("");return d(s,{style:e.card,testID:"buoy-ask-buoy-approval",children:[d(s,{style:e.titleRow,children:[t(f,{size:14,color:o.color.warning}),t(r,{style:e.title,children:"Allow this change?"})]}),l.restored?t(r,{style:e.restored,testID:"buoy-ask-buoy-approval-restored",children:"Asked before the app restarted \u2014 check it still applies."}):null,t(r,{style:e.label,children:l.label}),t(r,{style:e.reason,children:l.reason}),l.description?t(x,{text:l.description}):null,t(y,{testID:"buoy-ask-buoy-approval-note",value:i,onChangeText:a,placeholder:"Add a note for the agent (optional)",placeholderTextColor:o.color.textTertiary,style:e.note,returnKeyType:"done",blurOnSubmit:true}),d(s,{style:e.actions,children:[t(p,{testID:"buoy-ask-buoy-approve",accessibilityRole:"button",onPress:()=>n({approved:true,reason:i}),style:({pressed:c})=>[e.button,e.approve,c&&e.pressed],children:t(r,{style:e.approveText,children:"Allow"})}),l.restored?null:t(p,{testID:"buoy-ask-buoy-approve-trust",accessibilityRole:"button",onPress:()=>n({approved:true,reason:i,trust:true}),style:({pressed:c})=>[e.button,e.trust,c&&e.pressed],children:t(r,{style:e.trustText,children:"Allow for this chat"})}),t(p,{testID:"buoy-ask-buoy-reject",accessibilityRole:"button",onPress:()=>n({approved:false,reason:i}),style:({pressed:c})=>[e.button,e.reject,c&&e.pressed],children:t(r,{style:e.rejectText,children:"Not now"})})]})]})}function x({text:l}){const[n,i]=h(false);return d(s,{children:[d(p,{testID:"buoy-ask-buoy-approval-details",accessibilityRole:"button",accessibilityState:{expanded:n},onPress:()=>i(a=>!a),style:({pressed:a})=>[e.disclosure,a&&e.pressed],hitSlop:6,children:[t(s,{style:n?e.chevronOpen:e.chevronClosed,children:t(g,{size:12,color:o.color.textTertiary})}),t(r,{style:e.disclosureText,children:n?"Hide details":"Details"})]}),n?t(b,{style:e.detail,contentContainerStyle:e.detailContent,nestedScrollEnabled:true,children:t(r,{style:e.description,children:l})}):null]})}const e=u.create({card:{marginHorizontal:o.gutter,marginBottom:10,padding:o.cardPad,borderRadius:o.radius.card,borderWidth:u.hairlineWidth,borderColor:o.color.accentBorderStrong,backgroundColor:o.color.surface,gap:6},titleRow:{flexDirection:"row",alignItems:"center",gap:6},title:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},label:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",lineHeight:20},description:{color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:18},reason:{color:o.color.warning,fontSize:o.font.caption,lineHeight:18},restored:{color:o.color.info,backgroundColor:o.color.infoSoft,fontSize:o.font.micro,lineHeight:16,paddingHorizontal:8,paddingVertical:5,borderRadius:8},disclosure:{flexDirection:"row",alignItems:"center",gap:4,paddingVertical:4},disclosureText:{color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"600"},chevronOpen:{transform:[{rotate:"0deg"}]},chevronClosed:{transform:[{rotate:"-90deg"}]},detail:{maxHeight:150,marginTop:2},detailContent:{paddingBottom:2},note:{color:o.color.text,fontSize:o.font.caption,paddingHorizontal:10,paddingVertical:7,borderRadius:o.radius.row,borderWidth:u.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.button,marginTop:2},actions:{flexDirection:"row",flexWrap:"wrap",gap:8,marginTop:6},button:{paddingHorizontal:16,paddingVertical:9,borderRadius:o.radius.row},approve:{backgroundColor:o.color.accent},approveText:{color:o.color.onAccent,fontWeight:"600",fontSize:o.font.caption},trust:{backgroundColor:o.color.button,borderWidth:u.hairlineWidth,borderColor:o.color.accentBorderStrong},trustText:{color:o.color.accent,fontWeight:"600",fontSize:o.font.caption},reject:{backgroundColor:o.color.button},rejectText:{color:o.color.text,fontSize:o.font.caption},pressed:{opacity:.7}});
1
+ "use strict";import{useState as h}from"react";import{Pressable as p,ScrollView as b,StyleSheet as u,Text as r,TextInput as y,View as c}from"react-native";import{AlertTriangle as f,ChevronDown as g,night as o}from"@buoy-gg/shared-ui";import{jsx as t,jsxs as d}from"react/jsx-runtime";export function ApprovalCard({pending:l,onAnswer:n}){const[i,a]=h("");return d(c,{style:e.card,testID:"buoy-ask-buoy-approval",children:[d(c,{style:e.titleRow,children:[t(f,{size:14,color:o.color.warning}),t(r,{style:e.title,children:"Allow this change?"})]}),l.restored?t(r,{style:e.restored,testID:"buoy-ask-buoy-approval-restored",children:"Asked before the app restarted \u2014 check it still applies."}):null,t(r,{style:e.label,children:l.label}),t(r,{style:e.reason,children:l.reason}),l.description?t(x,{text:l.description}):null,t(y,{testID:"buoy-ask-buoy-approval-note",value:i,onChangeText:a,placeholder:"Add a note for the agent (optional)",placeholderTextColor:o.color.textTertiary,style:e.note,returnKeyType:"done",blurOnSubmit:true}),d(c,{style:e.actions,children:[t(p,{testID:"buoy-ask-buoy-approve",accessibilityRole:"button",onPress:()=>n({approved:true,reason:i}),style:({pressed:s})=>[e.button,e.approve,s&&e.pressed],children:t(r,{style:e.approveText,children:"Allow"})}),l.restored?null:t(p,{testID:"buoy-ask-buoy-approve-trust",accessibilityRole:"button",onPress:()=>n({approved:true,reason:i,trust:true}),style:({pressed:s})=>[e.button,e.trust,s&&e.pressed],children:t(r,{style:e.trustText,children:"Allow for this chat"})}),t(p,{testID:"buoy-ask-buoy-reject",accessibilityRole:"button",onPress:()=>n({approved:false,reason:i}),style:({pressed:s})=>[e.button,e.reject,s&&e.pressed],children:t(r,{style:e.rejectText,children:"Not now"})})]})]})}function x({text:l}){const[n,i]=h(false);return d(c,{children:[d(p,{testID:"buoy-ask-buoy-approval-details",accessibilityRole:"button",accessibilityState:{expanded:n},onPress:()=>i(a=>!a),style:({pressed:a})=>[e.disclosure,a&&e.pressed],hitSlop:6,children:[t(c,{style:n?e.chevronOpen:e.chevronClosed,children:t(g,{size:12,color:o.color.textTertiary})}),t(r,{style:e.disclosureText,children:n?"Hide details":"Details"})]}),n?t(b,{keyboardShouldPersistTaps:"handled",style:e.detail,contentContainerStyle:e.detailContent,nestedScrollEnabled:true,children:t(r,{style:e.description,children:l})}):null]})}const e=u.create({card:{marginHorizontal:o.gutter,marginBottom:10,padding:o.cardPad,borderRadius:o.radius.card,borderWidth:u.hairlineWidth,borderColor:o.color.accentBorderStrong,backgroundColor:o.color.surface,gap:6},titleRow:{flexDirection:"row",alignItems:"center",gap:6},title:{color:o.color.text,fontSize:o.font.caption,fontWeight:"600"},label:{color:o.color.text,fontSize:o.font.row,fontWeight:"600",lineHeight:20},description:{color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:18},reason:{color:o.color.warning,fontSize:o.font.caption,lineHeight:18},restored:{color:o.color.info,backgroundColor:o.color.infoSoft,fontSize:o.font.micro,lineHeight:16,paddingHorizontal:8,paddingVertical:5,borderRadius:8},disclosure:{flexDirection:"row",alignItems:"center",gap:4,paddingVertical:4},disclosureText:{color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"600"},chevronOpen:{transform:[{rotate:"0deg"}]},chevronClosed:{transform:[{rotate:"-90deg"}]},detail:{maxHeight:150,marginTop:2},detailContent:{paddingBottom:2},note:{color:o.color.text,fontSize:o.font.caption,paddingHorizontal:10,paddingVertical:7,borderRadius:o.radius.row,borderWidth:u.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.button,marginTop:2},actions:{flexDirection:"row",flexWrap:"wrap",gap:8,marginTop:6},button:{paddingHorizontal:16,paddingVertical:9,borderRadius:o.radius.row},approve:{backgroundColor:o.color.accent},approveText:{color:o.color.onAccent,fontWeight:"600",fontSize:o.font.caption},trust:{backgroundColor:o.color.button,borderWidth:u.hairlineWidth,borderColor:o.color.accentBorderStrong},trustText:{color:o.color.accent,fontWeight:"600",fontSize:o.font.caption},reject:{backgroundColor:o.color.button},rejectText:{color:o.color.text,fontSize:o.font.caption},pressed:{opacity:.7}});
@@ -1,3 +1,3 @@
1
- "use strict";import{useCallback as a,useEffect as y,useMemo as T,useRef as h,useState as H}from"react";import{Alert as Y,Animated as R,Pressable as Pe,StyleSheet as $e,Text as N,View as B}from"react-native";import{isReversible as He}from"@buoy-gg/agent-core";import{dispatchToolAction as Re,FeatureGate as Ne,Info as X,JsModal as Oe,ModalHeader as O,night as i,setToolAttention as u,useSafeAreaInsets as De}from"@buoy-gg/shared-ui";import{BlockHandlersProvider as Le}from"../blocks";import{ApprovalCard as Me}from"./ApprovalCard";import{ChangesBar as Ke,useLedgerEntries as Ee}from"./ChangesBar";import{ruleStatesFrom as je}from"../blocks/ruleStates";import{Composer as qe}from"./Composer";import{KeyboardPad as ze}from"./KeyboardPad";import{AskBuoyHeader as Fe}from"./AskBuoyHeader";import{AskBuoySettings as Ve}from"./AskBuoySettings";import{EmptyState as Ue}from"./EmptyState";import{MessageList as _e,transcriptToText as Qe}from"./MessageList";import{QueuedMessages as We}from"./QueuedMessages";import{askBuoyStore as l,queuePauseReason as Ge}from"../store/askBuoyStore";import{TICKET_PROMPT as Je}from"../store/ticketPrompt";import{useAskBuoy as Ye}from"../store/useAskBuoy";import{useAskBuoySettings as Xe}from"../store/useAskBuoySettings";import{askBuoySettings as Z}from"../store/askBuoySettings";import{jsx as o,jsxs as d,Fragment as Ze}from"react/jsx-runtime";export function AskBuoyModal({visible:x,onClose:D,onBack:L,onMinimize:b,persistenceKey:ee,config:g}){const{state:e,send:k,stop:C,reset:S,undoThenReset:M,approve:te,retry:oe,answer:K,runAction:E,editQueued:ne,removeQueued:se,configured:I,toolCount:re,availableToolIds:ae}=Ye(g);const[ie,le]=H(false);const ce=a(()=>{C();D()},[C,D]);const ue=a(()=>l.clearDraft(),[]);const[v,P]=H(false);const de=a(()=>P(false),[]);const ge=a(()=>P(true),[]);const p=!x;const j=!!e.pending||!!e.awaitingBlockId;const s=e.messages[e.messages.length-1];const w=h(false);const q=h(e.busy);y(()=>{if(p&&q.current&&!e.busy)w.current=true;if(!p)w.current=false;q.current=e.busy},[p,e.busy]);y(()=>{if(!p){u("ask-buoy",null);return}if(j){u("ask-buoy",{kind:"needsInput",label:e.pending?"Needs your approval":"Has a question for you"})}else if(w.current&&s?.role==="assistant"&&s.error){u("ask-buoy",{kind:"failed",label:"The last turn failed"})}else if(w.current&&!e.busy){u("ask-buoy",{kind:"done",label:"Finished"})}else{u("ask-buoy",null)}},[p,j,e.busy,e.pending,s]);y(()=>()=>u("ask-buoy",null),[]);y(()=>{if(e.pending&&v)P(false)},[e.pending,v]);const pe=De({minBottom:16});const[fe,me]=H("bottomSheet");const ye=fe==="floating"?0:pe.bottom;const he=a(()=>{const t=l.getLedger();if(!t)return;void(async()=>{const n=await t.revertAll(Re);if(n.failed.length===0){l.setNotice(null);return}const r=n.failed.map(m=>`${m.label} (${m.error})`).join("; ");l.setNotice(`Couldn't undo ${n.failed.length} change${n.failed.length===1?"":"s"} \u2014 ${r}`)})()},[]);const be=a(()=>le(t=>!t),[]);const f=h(new R.Value(560)).current;const $=h(false);const z=a(()=>{if($.current)return;$.current=true;f.stopAnimation(t=>{const n=typeof t==="number"&&t>200?t:560;R.timing(f,{toValue:120,duration:220,useNativeDriver:false}).start(()=>{setTimeout(()=>{R.spring(f,{toValue:n,friction:9,useNativeDriver:false}).start(()=>{$.current=false})},2500)})})},[f]);const A=T(()=>{const t=e.messages[e.messages.length-1];if(!t||t.role!=="assistant")return"";const n=t.blocks.filter(r=>r.block.kind==="peek").length;return n>0?`${t.id}:${n}`:""},[e.messages]);const F=h("");y(()=>{if(A&&A!==F.current&&g?.peek!==false)z();F.current=A},[A,z,g]);const ke=a(()=>{const t=l.getLedger();const n=t?.list().filter(r=>!r.undoneAt&&He(r.effect)).length??0;if(n===0){S();return}Y.alert("Undo changes first?",`Ask Buoy made ${n} change${n===1?"":"s"} that can still be undone. Clearing the conversation loses the only automatic undo for ${n===1?"it":"them"}.`,[{text:"Undo, then clear",onPress:()=>{void(async()=>{const r=await M();if(r.cleared)return;const m=r.reverted+r.failed.length;const Ie=r.failed.map(J=>`${J.label} (${J.error})`).join("\n");Y.alert(`Couldn't undo ${r.failed.length} of ${m} change${m===1?"":"s"}`,`${Ie}
1
+ "use strict";import{useCallback as a,useEffect as y,useMemo as T,useRef as h,useState as H}from"react";import{Alert as Y,Animated as R,Pressable as Pe,StyleSheet as $e,Text as N,View as B}from"react-native";import{isReversible as He}from"@buoy-gg/agent-core";import{dispatchToolAction as Re,FeatureGate as Ne,Info as X,JsModal as Oe,ModalHeader as O,night as i,setToolAttention as u,useSafeAreaInsets as De}from"@buoy-gg/shared-ui";import{BlockHandlersProvider as Le}from"../blocks";import{ApprovalCard as Ke}from"./ApprovalCard";import{ChangesBar as Me,useLedgerEntries as Ee}from"./ChangesBar";import{ruleStatesFrom as je}from"../blocks/ruleStates";import{Composer as qe}from"./Composer";import{KeyboardPad as ze}from"./KeyboardPad";import{AskBuoyHeader as Fe}from"./AskBuoyHeader";import{AskBuoySettings as Ve}from"./AskBuoySettings";import{EmptyState as Ue}from"./EmptyState";import{MessageList as _e,transcriptToText as Qe}from"./MessageList";import{QueuedMessages as We}from"./QueuedMessages";import{askBuoyStore as l,queuePauseReason as Ge}from"../store/askBuoyStore";import{TICKET_PROMPT as Je}from"../store/ticketPrompt";import{useAskBuoy as Ye}from"../store/useAskBuoy";import{useAskBuoySettings as Xe}from"../store/useAskBuoySettings";import{askBuoySettings as Z}from"../store/askBuoySettings";import{jsx as o,jsxs as d,Fragment as Ze}from"react/jsx-runtime";export function AskBuoyModal({visible:x,onClose:D,onBack:L,onMinimize:b,persistenceKey:ee,config:g}){const{state:e,send:k,stop:C,reset:S,undoThenReset:K,approve:te,retry:oe,answer:M,runAction:E,editQueued:se,removeQueued:ne,configured:I,toolCount:re,availableToolIds:ae}=Ye(g);const[ie,le]=H(false);const ce=a(()=>{C();D()},[C,D]);const ue=a(()=>l.clearDraft(),[]);const[v,P]=H(false);const de=a(()=>P(false),[]);const ge=a(()=>P(true),[]);const p=!x;const j=!!e.pending||!!e.awaitingBlockId;const n=e.messages[e.messages.length-1];const w=h(false);const q=h(e.busy);y(()=>{if(p&&q.current&&!e.busy)w.current=true;if(!p)w.current=false;q.current=e.busy},[p,e.busy]);y(()=>{if(!p){u("ask-buoy",null);return}if(j){u("ask-buoy",{kind:"needsInput",label:e.pending?"Needs your approval":"Has a question for you"})}else if(w.current&&n?.role==="assistant"&&n.error){u("ask-buoy",{kind:"failed",label:"The last turn failed"})}else if(w.current&&!e.busy){u("ask-buoy",{kind:"done",label:"Finished"})}else{u("ask-buoy",null)}},[p,j,e.busy,e.pending,n]);y(()=>()=>u("ask-buoy",null),[]);y(()=>{if(e.pending&&v)P(false)},[e.pending,v]);const pe=De({minBottom:16});const[fe,me]=H("bottomSheet");const ye=fe==="floating"?0:pe.bottom;const he=a(()=>{const t=l.getLedger();if(!t)return;void(async()=>{const s=await t.revertAll(Re);if(s.failed.length===0){l.setNotice(null);return}const r=s.failed.map(m=>`${m.label} (${m.error})`).join("; ");l.setNotice(`Couldn't undo ${s.failed.length} change${s.failed.length===1?"":"s"} \u2014 ${r}`)})()},[]);const be=a(()=>le(t=>!t),[]);const f=h(new R.Value(560)).current;const $=h(false);const z=a(()=>{if($.current)return;$.current=true;f.stopAnimation(t=>{const s=typeof t==="number"&&t>200?t:560;R.timing(f,{toValue:120,duration:220,useNativeDriver:false}).start(()=>{setTimeout(()=>{R.spring(f,{toValue:s,friction:9,useNativeDriver:false}).start(()=>{$.current=false})},2500)})})},[f]);const A=T(()=>{const t=e.messages[e.messages.length-1];if(!t||t.role!=="assistant")return"";const s=t.blocks.filter(r=>r.block.kind==="peek").length;return s>0?`${t.id}:${s}`:""},[e.messages]);const F=h("");y(()=>{if(A&&A!==F.current&&g?.peek!==false)z();F.current=A},[A,z,g]);const ke=a(()=>{const t=l.getLedger();const s=t?.list().filter(r=>!r.undoneAt&&He(r.effect)).length??0;if(s===0){S();return}Y.alert("Undo changes first?",`Ask Buoy made ${s} change${s===1?"":"s"} that can still be undone. Clearing the conversation loses the only automatic undo for ${s===1?"it":"them"}.`,[{text:"Undo, then clear",onPress:()=>{void(async()=>{const r=await K();if(r.cleared)return;const m=r.reverted+r.failed.length;const Ie=r.failed.map(J=>`${J.label} (${J.error})`).join("\n");Y.alert(`Couldn't undo ${r.failed.length} of ${m} change${m===1?"":"s"}`,`${Ie}
2
2
 
3
- Still applied in the app. Clear the conversation anyway?`,[{text:"Keep conversation",style:"cancel"},{text:"Clear anyway",style:"destructive",onPress:S}])})()}},{text:"Clear anyway",style:"destructive",onPress:S},{text:"Cancel",style:"cancel"}])},[S,M]);const Se=T(()=>{const t=e.messages[e.messages.length-1];if(!t||t.role!=="assistant"||!t.streaming)return null;return t.toolCalls.find(n=>n.status==="running")?.label??null},[e.messages]);const V=Ee(l.getLedger());const U=T(()=>je(V),[V]);const ve=T(()=>({answer:K,runAction:(t,n)=>{if(t.minimize){b?.({mode:"bottomSheet"});return}return E(t,n)},answered:e.answers,actionState:e.actionState,busy:e.busy,supports:{openTool:false,minimize:!!b,dispatch:true},rules:U}),[K,E,b,e.answers,e.actionState,e.busy,U]);const we=!e.busy&&s?.role==="assistant"&&!!s.error;const _=!e.busy&&s?.role==="assistant"&&s.stopped&&!s.error?s.id:null;const Ae=Ge(e);const{showThinking:Q,readOnly:W,claudeNoticeSeen:Te}=Xe();const G=g?.hosted===true&&!Te;const Be=a(()=>Qe(e.messages,Q),[e.messages,Q]);const xe=a(()=>k(Je),[k]);const Ce=v?d(O,{children:[o(O.Navigation,{onBack:de}),o(O.Content,{title:"Settings",subtitle:"Ask Buoy",testID:"buoy-ask-buoy-settings-title"})]}):o(Fe,{configured:I,busy:e.busy,failed:we,activeLabel:Se,awaitingApproval:!!e.pending,startedAt:s?.role==="assistant"&&s.streaming?s.startedAt??null:null,stopping:e.stopping,readOnly:W,toolCount:re,hasMessages:e.messages.length>0,onBack:L,onReset:ke,getTranscript:Be,onOpenSettings:ge,onTicket:xe});return o(Oe,{visible:x,onClose:ce,disableScrollWrapper:true,background:false,initialHeight:560,animatedHeight:f,header:{customContent:Ce,showToggleButton:false},onBack:L,onMinimize:b,onModeChange:me,persistenceKey:ee??"buoy-ask-buoy",enablePersistence:true,children:o(Le,{value:ve,children:d(B,{style:[c.root,{paddingBottom:ye}],children:[o(Ke,{ledger:l.getLedger(),onUndoAll:he,expanded:ie,onToggle:be}),G&&d(B,{style:c.notice,testID:"buoy-ask-buoy-hosted-notice",children:[o(X,{size:14,color:i.color.info}),d(B,{style:{flex:1,gap:8},children:[o(N,{style:c.noticeText,children:"Hosted Ask Buoy (beta) sends your question, and the app data it needs to answer, to Anthropic for Claude. If Claude is down, it goes to OpenAI through OpenRouter for Luna instead. Buoy keeps the cost of each answer, never the chat. Your plan includes weekly credits."}),o(Pe,{testID:"buoy-ask-buoy-hosted-notice-ok",accessibilityRole:"button",onPress:()=>{Z.set("claudeNoticeSeen",true);Z.set("hostedNoticeSeen",true)},style:({pressed:t})=>[c.noticeButton,t&&{opacity:.7}],children:o(N,{style:c.noticeButtonText,children:"Got it"})})]})]}),e.notice&&d(B,{style:c.notice,testID:"buoy-ask-buoy-notice",children:[o(X,{size:14,color:i.color.info}),o(N,{style:c.noticeText,children:e.notice})]}),o(ze,{children:o(Ne,{featureName:"Ask Buoy",children:v?o(Ve,{}):d(Ze,{children:[o(_e,{messages:e.messages,onRetry:oe,waitingOnUser:!!e.pending||!!e.awaitingBlockId,ListEmptyComponent:o(Ue,{configured:I,onPick:k,availableToolIds:ae,readOnly:W||!!g?.policy?.readOnly})}),o(We,{items:e.queued,paused:Ae,onEdit:ne,onRemove:se}),e.pending&&o(Me,{pending:e.pending,onAnswer:te}),o(qe,{onSend:k,onStop:C,busy:e.busy,draft:e.draft,onDraftTaken:ue,disabled:!I||G,focusKey:_,canFocus:x,placeholder:_?"Tell it what to do differently\u2026":e.awaitingBlockId?"Pick an option above, or type\u2026":void 0})]})})})]})})})}const c=$e.create({root:{flex:1,backgroundColor:"transparent"},notice:{flexDirection:"row",alignItems:"flex-start",gap:8,paddingHorizontal:i.gutter,paddingVertical:10,backgroundColor:i.color.infoSoft},noticeText:{flex:1,color:i.color.text,fontSize:i.font.caption,lineHeight:18},noticeButton:{alignSelf:"flex-start",paddingHorizontal:12,paddingVertical:6,borderRadius:i.radius.row,backgroundColor:i.color.button},noticeButtonText:{color:i.color.text,fontSize:i.font.caption,fontWeight:"600"}});
3
+ Still applied in the app. Clear the conversation anyway?`,[{text:"Keep conversation",style:"cancel"},{text:"Clear anyway",style:"destructive",onPress:S}])})()}},{text:"Clear anyway",style:"destructive",onPress:S},{text:"Cancel",style:"cancel"}])},[S,K]);const Se=T(()=>{const t=e.messages[e.messages.length-1];if(!t||t.role!=="assistant"||!t.streaming)return null;return t.toolCalls.find(s=>s.status==="running")?.label??null},[e.messages]);const V=Ee(l.getLedger());const U=T(()=>je(V),[V]);const ve=T(()=>({answer:M,runAction:(t,s)=>{if(t.minimize){b?.({mode:"bottomSheet"});return}return E(t,s)},answered:e.answers,actionState:e.actionState,busy:e.busy,supports:{openTool:false,minimize:!!b,dispatch:true},rules:U}),[M,E,b,e.answers,e.actionState,e.busy,U]);const we=!e.busy&&n?.role==="assistant"&&!!n.error;const _=!e.busy&&n?.role==="assistant"&&n.stopped&&!n.error?n.id:null;const Ae=Ge(e);const{showThinking:Q,readOnly:W,claudeNoticeSeen:Te}=Xe();const G=g?.hosted===true&&!Te;const Be=a(()=>Qe(e.messages,Q),[e.messages,Q]);const xe=a(()=>k(Je),[k]);const Ce=v?d(O,{children:[o(O.Navigation,{onBack:de}),o(O.Content,{title:"Settings",subtitle:"Ask Buoy",testID:"buoy-ask-buoy-settings-title"})]}):o(Fe,{configured:I,busy:e.busy,failed:we,activeLabel:Se,awaitingApproval:!!e.pending,startedAt:n?.role==="assistant"&&n.streaming?n.startedAt??null:null,stopping:e.stopping,readOnly:W,toolCount:re,hasMessages:e.messages.length>0,onBack:L,onReset:ke,getTranscript:Be,onOpenSettings:ge,onTicket:xe});return o(Oe,{avoidKeyboard:false,visible:x,onClose:ce,disableScrollWrapper:true,background:false,initialHeight:560,animatedHeight:f,header:{customContent:Ce,showToggleButton:false},onBack:L,onMinimize:b,onModeChange:me,persistenceKey:ee??"buoy-ask-buoy",enablePersistence:true,children:o(Le,{value:ve,children:d(B,{style:[c.root,{paddingBottom:ye}],children:[o(Me,{ledger:l.getLedger(),onUndoAll:he,expanded:ie,onToggle:be}),G&&d(B,{style:c.notice,testID:"buoy-ask-buoy-hosted-notice",children:[o(X,{size:14,color:i.color.info}),d(B,{style:{flex:1,gap:8},children:[o(N,{style:c.noticeText,children:"Hosted Ask Buoy (beta) sends your question, and the app data it needs to answer, to Anthropic for Claude. If Claude is down, it goes to OpenAI through OpenRouter for Luna instead. Buoy keeps the cost of each answer, never the chat. Your plan includes weekly credits."}),o(Pe,{testID:"buoy-ask-buoy-hosted-notice-ok",accessibilityRole:"button",onPress:()=>{Z.set("claudeNoticeSeen",true);Z.set("hostedNoticeSeen",true)},style:({pressed:t})=>[c.noticeButton,t&&{opacity:.7}],children:o(N,{style:c.noticeButtonText,children:"Got it"})})]})]}),e.notice&&d(B,{style:c.notice,testID:"buoy-ask-buoy-notice",children:[o(X,{size:14,color:i.color.info}),o(N,{style:c.noticeText,children:e.notice})]}),o(ze,{children:o(Ne,{featureName:"Ask Buoy",children:v?o(Ve,{}):d(Ze,{children:[o(_e,{messages:e.messages,onRetry:oe,waitingOnUser:!!e.pending||!!e.awaitingBlockId,ListEmptyComponent:o(Ue,{configured:I,onPick:k,availableToolIds:ae,readOnly:W||!!g?.policy?.readOnly})}),o(We,{items:e.queued,paused:Ae,onEdit:se,onRemove:ne}),e.pending&&o(Ke,{pending:e.pending,onAnswer:te}),o(qe,{onSend:k,onStop:C,busy:e.busy,draft:e.draft,onDraftTaken:ue,disabled:!I||G,focusKey:_,canFocus:x,placeholder:_?"Tell it what to do differently\u2026":e.awaitingBlockId?"Pick an option above, or type\u2026":void 0})]})})})]})})})}const c=$e.create({root:{flex:1,backgroundColor:"transparent"},notice:{flexDirection:"row",alignItems:"flex-start",gap:8,paddingHorizontal:i.gutter,paddingVertical:10,backgroundColor:i.color.infoSoft},noticeText:{flex:1,color:i.color.text,fontSize:i.font.caption,lineHeight:18},noticeButton:{alignSelf:"flex-start",paddingHorizontal:12,paddingVertical:6,borderRadius:i.radius.row,backgroundColor:i.color.button},noticeButtonText:{color:i.color.text,fontSize:i.font.caption,fontWeight:"600"}});
@@ -1 +1 @@
1
- "use strict";import{useCallback as f,useEffect as k,useState as w}from"react";import{Pressable as h,ScrollView as m,StyleSheet as y,Text as r,View as c}from"react-native";import{AlertTriangle as S,night as s,ToggleRow as g}from"@buoy-gg/shared-ui";import{useAskBuoySettings as T}from"../store/useAskBuoySettings";import{askBuoySettings as p}from"../store/askBuoySettings";import{hostedModelSetting as x,useHostedModel as I}from"../store/hostedModelSetting";import{askBuoyStore as u}from"../store/askBuoyStore";import{useAskBuoyState as b}from"../store/useAskBuoy";import{jsx as t,jsxs as l,Fragment as A}from"react/jsx-runtime";export function AskBuoySettings(){const n=T();const a=b().trusted;return l(m,{style:e.scroll,contentContainerStyle:e.content,testID:"buoy-ask-buoy-settings",children:[t(D,{}),t(v,{}),t(r,{style:e.section,children:"CHAT"}),t(g,{testID:"buoy-ask-buoy-setting-thinking",label:"Show agent thinking",description:"Adds a collapsed strip under each answer: the model's reasoning, and every step it ran with its parameters, result, duration and token cost. Copy includes the steps and reasoning while this is on, so you can read the run outside the app.",value:n.showThinking,onValueChange:o=>p.set("showThinking",o)}),t(r,{style:e.note,children:"Not every model reports reasoning. Claude thinks by default; most OpenAI models return none, and the strip then shows the steps alone."}),t(r,{style:e.section,children:"PERMISSIONS"}),t(g,{testID:"buoy-ask-buoy-setting-readonly",label:"Read only",description:"Inspect the app, change nothing. Every write and simulation is refused until this is off. Undo still works \u2014 putting things back is the safe direction.",value:n.readOnly,onValueChange:o=>p.set("readOnly",o)}),n.readOnly?t(r,{style:e.note,testID:"buoy-ask-buoy-readonly-note",children:"On across reloads until you turn it off here. The header says \u201CRead only\u201D while it is on. It adds to whatever this app already restricts \u2014 it never loosens anything."}):null,t(g,{testID:"buoy-ask-buoy-setting-bypass",tone:"warning",emphasizeLabel:n.bypassApprovals,disabled:n.readOnly,label:"Skip approval prompts",description:n.readOnly?"Unavailable while Read only is on \u2014 nothing can be approved.":"Runs every action the moment the agent calls it, with no Allow card \u2014 including the destructive ones that wipe or reset data.",value:n.bypassApprovals,onValueChange:o=>p.set("bypassApprovals",o)}),n.bypassApprovals&&!n.readOnly?l(c,{style:e.warning,testID:"buoy-ask-buoy-bypass-warning",children:[t(S,{size:14,color:s.color.warning}),t(r,{style:e.warningText,children:"Nothing will ask before a change lands. A wipe or a reset runs on the model's word alone, and Undo only reverses the changes that can be reversed \u2014 the bar tells you which. This stays on across reloads and app restarts until you turn it off here, so leave it off on any device holding data you care about. It does not widen what the agent may reach: read-only mode, this app's deny list and the release-build refusals still apply."})]}):t(r,{style:e.note,children:"On by default for destructive actions only \u2014 reads and ordinary writes already run without asking, and the changes bar records them."}),a.length>0?l(c,{style:e.trustedBox,testID:"buoy-ask-buoy-trusted",children:[t(r,{style:e.section,children:"ALLOWED WITHOUT ASKING \u2014 THIS CHAT"}),a.map(o=>l(c,{style:e.trustedRow,children:[t(r,{style:e.trustedLabel,numberOfLines:2,children:o.label}),t(h,{testID:`buoy-ask-buoy-untrust-${o.key}`,accessibilityRole:"button",onPress:()=>u.untrust(o.key),style:({pressed:i})=>[e.forget,i&&{opacity:.7}],hitSlop:6,children:t(r,{style:e.forgetText,children:"Ask again"})})]},o.key))]}):null]})}function C(n){const a=Date.parse(n.length===10?`${n}T00:00:00Z`:n);if(Number.isNaN(a))return n;const o=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const i=new Date(a);return`${o[i.getUTCMonth()]} ${i.getUTCDate()}`}function D(){const n=u.getUsageReader();const[a,o]=w({kind:"loading"});const i=f(()=>{if(!n)return;o({kind:"loading"});n().then(d=>{if(d.claude)u.setClaudeOut(!d.claude.available);o({kind:"ok",usage:d})}).catch(d=>o({kind:"error",message:d instanceof Error?d.message:"Could not load credits."}))},[n]);k(i,[i]);if(!n)return null;return l(c,{testID:"buoy-ask-buoy-credits",children:[t(r,{style:e.section,children:"HOSTED AI CREDITS"}),l(c,{style:e.trustedRow,children:[t(c,{style:{flex:1,gap:2},children:a.kind==="ok"?l(A,{children:[l(r,{style:e.trustedLabel,children:[a.usage.creditsLeft.toLocaleString()," of ",a.usage.grantCredits.toLocaleString()," credits left"]}),l(r,{style:e.note,children:[a.usage.resetAt?`Resets ${C(a.usage.resetAt)}. `:"","That's about ",Math.floor(a.usage.creditsLeft/5).toLocaleString()," more asks, included with your plan."]})]}):a.kind==="error"?t(r,{style:e.note,children:a.message}):t(r,{style:e.note,children:"Loading\u2026"})}),t(h,{testID:"buoy-ask-buoy-credits-refresh",accessibilityRole:"button",onPress:i,style:({pressed:d})=>[e.forget,d&&{opacity:.7}],hitSlop:6,children:t(r,{style:e.forgetText,children:"Refresh"})})]})]})}const R=[{id:"claude",label:"Claude Haiku 5.5",note:"The default. Answers faster."},{id:"luna",label:"GPT-6 Luna",note:"Uses fewer credits."}];function v(){const n=I();const{claudeOut:a}=b();if(!u.getUsageReader())return null;return l(c,{testID:"buoy-ask-buoy-model",style:{gap:6},children:[t(r,{style:e.section,children:"MODEL"}),R.map(o=>{const i=n===o.id;return l(h,{testID:`buoy-ask-buoy-model-${o.id}`,accessibilityRole:"radio",accessibilityState:{checked:i},onPress:()=>x.set(o.id),style:({pressed:d})=>[e.trustedRow,i&&e.picked,d&&{opacity:.7}],children:[l(c,{style:{flex:1,gap:2},children:[t(r,{style:e.trustedLabel,children:o.label}),t(r,{style:e.note,children:o.note})]}),t(r,{style:e.forgetText,children:i?"On":""})]},o.id)}),t(r,{style:e.note,testID:"buoy-ask-buoy-model-note",children:n==="claude"&&a?"Claude is busy right now. New chats use Luna until it opens again.":"If one model is down, the other answers. A change starts with your next new chat. You can also pick it on buoy.gg, on the Ask Buoy tab."})]})}const e=y.create({picked:{borderColor:s.color.info},scroll:{flex:1},content:{padding:s.gutter,gap:10,paddingBottom:32},section:{color:s.color.textTertiary,fontSize:10,fontWeight:"700",letterSpacing:.6,paddingHorizontal:4,paddingTop:4},warning:{flexDirection:"row",gap:8,padding:s.cardPad,borderRadius:s.radius.card,borderWidth:y.hairlineWidth,borderColor:s.color.warning,backgroundColor:s.color.surface},warningText:{flex:1,color:s.color.warning,fontSize:s.font.micro,lineHeight:16},trustedBox:{gap:6,paddingTop:6},trustedRow:{flexDirection:"row",alignItems:"center",gap:10,padding:s.cardPad,borderRadius:s.radius.card,borderWidth:y.hairlineWidth,borderColor:s.color.border,backgroundColor:s.color.surface},trustedLabel:{flex:1,color:s.color.text,fontSize:s.font.caption,lineHeight:18},forget:{paddingHorizontal:10,paddingVertical:6,borderRadius:s.radius.row,backgroundColor:s.color.button},forgetText:{color:s.color.text,fontSize:s.font.micro,fontWeight:"600"},note:{color:s.color.textTertiary,fontSize:s.font.micro,lineHeight:16,paddingHorizontal:4}});
1
+ "use strict";import{useCallback as f,useEffect as k,useState as w}from"react";import{Pressable as h,ScrollView as m,StyleSheet as y,Text as r,View as c}from"react-native";import{AlertTriangle as S,night as s,ToggleRow as g}from"@buoy-gg/shared-ui";import{useAskBuoySettings as T}from"../store/useAskBuoySettings";import{askBuoySettings as p}from"../store/askBuoySettings";import{hostedModelSetting as x,useHostedModel as I}from"../store/hostedModelSetting";import{askBuoyStore as u}from"../store/askBuoyStore";import{useAskBuoyState as b}from"../store/useAskBuoy";import{jsx as t,jsxs as l,Fragment as A}from"react/jsx-runtime";export function AskBuoySettings(){const n=T();const a=b().trusted;return l(m,{keyboardShouldPersistTaps:"handled",style:e.scroll,contentContainerStyle:e.content,testID:"buoy-ask-buoy-settings",children:[t(D,{}),t(v,{}),t(r,{style:e.section,children:"CHAT"}),t(g,{testID:"buoy-ask-buoy-setting-thinking",label:"Show agent thinking",description:"Adds a collapsed strip under each answer: the model's reasoning, and every step it ran with its parameters, result, duration and token cost. Copy includes the steps and reasoning while this is on, so you can read the run outside the app.",value:n.showThinking,onValueChange:o=>p.set("showThinking",o)}),t(r,{style:e.note,children:"Not every model reports reasoning. Claude thinks by default; most OpenAI models return none, and the strip then shows the steps alone."}),t(r,{style:e.section,children:"PERMISSIONS"}),t(g,{testID:"buoy-ask-buoy-setting-readonly",label:"Read only",description:"Inspect the app, change nothing. Every write and simulation is refused until this is off. Undo still works \u2014 putting things back is the safe direction.",value:n.readOnly,onValueChange:o=>p.set("readOnly",o)}),n.readOnly?t(r,{style:e.note,testID:"buoy-ask-buoy-readonly-note",children:"On across reloads until you turn it off here. The header says \u201CRead only\u201D while it is on. It adds to whatever this app already restricts \u2014 it never loosens anything."}):null,t(g,{testID:"buoy-ask-buoy-setting-bypass",tone:"warning",emphasizeLabel:n.bypassApprovals,disabled:n.readOnly,label:"Skip approval prompts",description:n.readOnly?"Unavailable while Read only is on \u2014 nothing can be approved.":"Runs every action the moment the agent calls it, with no Allow card \u2014 including the destructive ones that wipe or reset data.",value:n.bypassApprovals,onValueChange:o=>p.set("bypassApprovals",o)}),n.bypassApprovals&&!n.readOnly?l(c,{style:e.warning,testID:"buoy-ask-buoy-bypass-warning",children:[t(S,{size:14,color:s.color.warning}),t(r,{style:e.warningText,children:"Nothing will ask before a change lands. A wipe or a reset runs on the model's word alone, and Undo only reverses the changes that can be reversed \u2014 the bar tells you which. This stays on across reloads and app restarts until you turn it off here, so leave it off on any device holding data you care about. It does not widen what the agent may reach: read-only mode, this app's deny list and the release-build refusals still apply."})]}):t(r,{style:e.note,children:"On by default for destructive actions only \u2014 reads and ordinary writes already run without asking, and the changes bar records them."}),a.length>0?l(c,{style:e.trustedBox,testID:"buoy-ask-buoy-trusted",children:[t(r,{style:e.section,children:"ALLOWED WITHOUT ASKING \u2014 THIS CHAT"}),a.map(o=>l(c,{style:e.trustedRow,children:[t(r,{style:e.trustedLabel,numberOfLines:2,children:o.label}),t(h,{testID:`buoy-ask-buoy-untrust-${o.key}`,accessibilityRole:"button",onPress:()=>u.untrust(o.key),style:({pressed:i})=>[e.forget,i&&{opacity:.7}],hitSlop:6,children:t(r,{style:e.forgetText,children:"Ask again"})})]},o.key))]}):null]})}function C(n){const a=Date.parse(n.length===10?`${n}T00:00:00Z`:n);if(Number.isNaN(a))return n;const o=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const i=new Date(a);return`${o[i.getUTCMonth()]} ${i.getUTCDate()}`}function D(){const n=u.getUsageReader();const[a,o]=w({kind:"loading"});const i=f(()=>{if(!n)return;o({kind:"loading"});n().then(d=>{if(d.claude)u.setClaudeOut(!d.claude.available);o({kind:"ok",usage:d})}).catch(d=>o({kind:"error",message:d instanceof Error?d.message:"Could not load credits."}))},[n]);k(i,[i]);if(!n)return null;return l(c,{testID:"buoy-ask-buoy-credits",children:[t(r,{style:e.section,children:"HOSTED AI CREDITS"}),l(c,{style:e.trustedRow,children:[t(c,{style:{flex:1,gap:2},children:a.kind==="ok"?l(A,{children:[l(r,{style:e.trustedLabel,children:[a.usage.creditsLeft.toLocaleString()," of ",a.usage.grantCredits.toLocaleString()," credits left"]}),l(r,{style:e.note,children:[a.usage.resetAt?`Resets ${C(a.usage.resetAt)}. `:"","That's about ",Math.floor(a.usage.creditsLeft/5).toLocaleString()," more asks, included with your plan."]})]}):a.kind==="error"?t(r,{style:e.note,children:a.message}):t(r,{style:e.note,children:"Loading\u2026"})}),t(h,{testID:"buoy-ask-buoy-credits-refresh",accessibilityRole:"button",onPress:i,style:({pressed:d})=>[e.forget,d&&{opacity:.7}],hitSlop:6,children:t(r,{style:e.forgetText,children:"Refresh"})})]})]})}const R=[{id:"claude",label:"Claude Haiku 5.5",note:"The default. Answers faster."},{id:"luna",label:"GPT-6 Luna",note:"Uses fewer credits."}];function v(){const n=I();const{claudeOut:a}=b();if(!u.getUsageReader())return null;return l(c,{testID:"buoy-ask-buoy-model",style:{gap:6},children:[t(r,{style:e.section,children:"MODEL"}),R.map(o=>{const i=n===o.id;return l(h,{testID:`buoy-ask-buoy-model-${o.id}`,accessibilityRole:"radio",accessibilityState:{checked:i},onPress:()=>x.set(o.id),style:({pressed:d})=>[e.trustedRow,i&&e.picked,d&&{opacity:.7}],children:[l(c,{style:{flex:1,gap:2},children:[t(r,{style:e.trustedLabel,children:o.label}),t(r,{style:e.note,children:o.note})]}),t(r,{style:e.forgetText,children:i?"On":""})]},o.id)}),t(r,{style:e.note,testID:"buoy-ask-buoy-model-note",children:n==="claude"&&a?"Claude is busy right now. New chats use Luna until it opens again.":"If one model is down, the other answers. A change starts with your next new chat. You can also pick it on buoy.gg, on the Ask Buoy tab."})]})}const e=y.create({picked:{borderColor:s.color.info},scroll:{flex:1},content:{padding:s.gutter,gap:10,paddingBottom:32},section:{color:s.color.textTertiary,fontSize:10,fontWeight:"700",letterSpacing:.6,paddingHorizontal:4,paddingTop:4},warning:{flexDirection:"row",gap:8,padding:s.cardPad,borderRadius:s.radius.card,borderWidth:y.hairlineWidth,borderColor:s.color.warning,backgroundColor:s.color.surface},warningText:{flex:1,color:s.color.warning,fontSize:s.font.micro,lineHeight:16},trustedBox:{gap:6,paddingTop:6},trustedRow:{flexDirection:"row",alignItems:"center",gap:10,padding:s.cardPad,borderRadius:s.radius.card,borderWidth:y.hairlineWidth,borderColor:s.color.border,backgroundColor:s.color.surface},trustedLabel:{flex:1,color:s.color.text,fontSize:s.font.caption,lineHeight:18},forget:{paddingHorizontal:10,paddingVertical:6,borderRadius:s.radius.row,backgroundColor:s.color.button},forgetText:{color:s.color.text,fontSize:s.font.micro,fontWeight:"600"},note:{color:s.color.textTertiary,fontSize:s.font.micro,lineHeight:16,paddingHorizontal:4}});
@@ -1 +1 @@
1
- "use strict";import{monoFont as y}from"@buoy-gg/shared-ui";import{Platform as b}from"react-native";import{Pressable as f,StyleSheet as c,Text as r,View as l}from"react-native";import{ChevronRight as m,night as e,NightSectionLabel as w}from"@buoy-gg/shared-ui";import{pickSuggestions as x}from"../store/suggestions";import{jsx as t,jsxs as n}from"react/jsx-runtime";const h=typeof __DEV__!=="undefined"&&!__DEV__;export function EmptyState({configured:i,onPick:s,availableToolIds:a=[],readOnly:d=false}){const u=x({availableToolIds:a,readOnly:d,isRelease:h});if(!i){return n(l,{style:o.wrap,testID:"buoy-ask-buoy-unconfigured",children:[t(r,{style:o.title,children:"Not set up yet"}),n(r,{style:o.body,children:["Ask Buoy talks to your own company's AI endpoint. Nothing goes to Buoy. A developer needs to add the ",t(r,{style:o.code,children:"askBuoy"})," setting where the app sets up Buoy. It needs your team's endpoint and model."]})]})}return n(l,{style:o.wrap,testID:"buoy-ask-buoy-empty",children:[t(r,{style:o.title,children:"Ask about this app"}),t(r,{style:o.body,children:"Ask what the app is doing right now, or tell it to change something. Anything it changes shows up in a bar at the top, with what can be undone."}),t(r,{style:o.buildLine,testID:"buoy-ask-buoy-build-line",children:h?"Release build \u2014 reading, impersonation and navigation work; simulations (forcing server responses, driving the screen) need a dev build.":"Dev build \u2014 full simulation available."}),n(l,{style:o.group,children:[t(w,{children:"Try asking"}),t(l,{style:o.card,children:u.map((g,p)=>t(k,{text:g,onPick:s,last:p===u.length-1},g))})]})]})}function k({text:i,onPick:s,last:a}){return n(f,{testID:`buoy-ask-buoy-suggestion-${i.slice(0,12)}`,accessibilityRole:"button",onPress:()=>s(i),style:({pressed:d})=>[o.row,!a&&o.rowDivider,d&&o.pressed],children:[t(r,{style:o.rowText,children:i}),t(m,{size:14,color:e.color.textTertiary})]})}const o=c.create({wrap:{flex:1,justifyContent:"center",paddingHorizontal:e.gutter,paddingVertical:24,gap:8},title:{color:e.color.text,fontSize:22,fontWeight:"700",letterSpacing:-.3},body:{color:e.color.textSecondary,fontSize:e.font.body,lineHeight:20},code:{color:e.color.text,fontFamily:b.OS==="web"?y:"Menlo"},buildLine:{marginTop:8,color:e.color.textTertiary,fontSize:e.font.micro,lineHeight:15},group:{marginTop:e.groupGap-8},card:{borderRadius:e.radius.card,borderWidth:c.hairlineWidth,borderColor:e.color.border,backgroundColor:e.color.surface,overflow:"hidden"},row:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:e.rowPadH,paddingVertical:13},rowDivider:{borderBottomWidth:c.hairlineWidth,borderBottomColor:e.color.separator},rowText:{flex:1,color:e.color.text,fontSize:e.font.body,lineHeight:19},pressed:{backgroundColor:e.color.fill}});
1
+ "use strict";import{monoFont as y}from"@buoy-gg/shared-ui";import{Platform as b}from"react-native";import{Pressable as f,StyleSheet as c,Text as r,View as l}from"react-native";import{ChevronRight as m,night as e,NightSectionLabel as w}from"@buoy-gg/shared-ui";import{pickSuggestions as x}from"../store/suggestions";import{jsx as t,jsxs as n}from"react/jsx-runtime";const h=typeof __DEV__!=="undefined"&&!__DEV__;export function EmptyState({configured:i,onPick:s,availableToolIds:a=[],readOnly:d=false}){const u=x({availableToolIds:a,readOnly:d,isRelease:h});if(!i){return n(l,{style:o.wrap,testID:"buoy-ask-buoy-unconfigured",children:[t(r,{style:o.title,children:"Not set up yet"}),n(r,{style:o.body,children:["Ask Buoy talks to your own company's AI endpoint. Nothing goes to Buoy. A developer needs to add the ",t(r,{style:o.code,children:"askBuoy"})," setting. It goes where the app sets up Buoy. It needs your team's endpoint and model."]})]})}return n(l,{style:o.wrap,testID:"buoy-ask-buoy-empty",children:[t(r,{style:o.title,children:"Ask about this app"}),t(r,{style:o.body,children:"Ask what the app is doing right now, or tell it to change something. Anything it changes shows up in a bar at the top, with what can be undone."}),t(r,{style:o.buildLine,testID:"buoy-ask-buoy-build-line",children:h?"Release build \u2014 reading, impersonation and navigation work; simulations (forcing server responses, driving the screen) need a dev build.":"Dev build \u2014 full simulation available."}),n(l,{style:o.group,children:[t(w,{children:"Try asking"}),t(l,{style:o.card,children:u.map((g,p)=>t(k,{text:g,onPick:s,last:p===u.length-1},g))})]})]})}function k({text:i,onPick:s,last:a}){return n(f,{testID:`buoy-ask-buoy-suggestion-${i.slice(0,12)}`,accessibilityRole:"button",onPress:()=>s(i),style:({pressed:d})=>[o.row,!a&&o.rowDivider,d&&o.pressed],children:[t(r,{style:o.rowText,children:i}),t(m,{size:14,color:e.color.textTertiary})]})}const o=c.create({wrap:{flex:1,justifyContent:"center",paddingHorizontal:e.gutter,paddingVertical:24,gap:8},title:{color:e.color.text,fontSize:22,fontWeight:"700",letterSpacing:-.3},body:{color:e.color.textSecondary,fontSize:e.font.body,lineHeight:20},code:{color:e.color.text,fontFamily:b.OS==="web"?y:"Menlo"},buildLine:{marginTop:8,color:e.color.textTertiary,fontSize:e.font.micro,lineHeight:15},group:{marginTop:e.groupGap-8},card:{borderRadius:e.radius.card,borderWidth:c.hairlineWidth,borderColor:e.color.border,backgroundColor:e.color.surface,overflow:"hidden"},row:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:e.rowPadH,paddingVertical:13},rowDivider:{borderBottomWidth:c.hairlineWidth,borderBottomColor:e.color.separator},rowText:{flex:1,color:e.color.text,fontSize:e.font.body,lineHeight:19},pressed:{backgroundColor:e.color.fill}});
@@ -1 +1 @@
1
- "use strict";import{monoFont as y}from"@buoy-gg/shared-ui";import{Platform as w}from"react-native";import{useState as S}from"react";import{Pressable as W,ScrollView as g,StyleSheet as x,Text as c,View as p}from"react-native";import{night as o}from"@buoy-gg/shared-ui";import{parseBlocks as k,TABLE_PREVIEW_COLS as T,TABLE_PREVIEW_ROWS as C,tableTruncation as z}from"./markdownParse";import{jsx as r,jsxs as m}from"react/jsx-runtime";export function renderInline(d,i){const l=d.replace(/!?\[([^\]]*)\]\([^)]*\)/g,"$1");const t=[];const s=/(\*\*[^*]+\*\*|`[^`]+`|\*[^*\s][^*]*\*|_[^_\s][^_]*_)/g;let a=0;let h;let f=0;while(h=s.exec(l)){if(h.index>a)t.push(l.slice(a,h.index));const n=h[0];if(n.startsWith("**")){t.push(r(c,{style:[i,e.bold],children:n.slice(2,-2)},f++))}else if(n.startsWith("`")){t.push(r(c,{style:[i,e.code],children:n.slice(1,-1)},f++))}else{t.push(r(c,{style:[i,e.italic],children:n.slice(1,-1)},f++))}a=h.index+n.length}if(a<l.length)t.push(l.slice(a));return t}const _=d=>d.replace(/!?\[([^\]]*)\]\([^)]*\)/g,"$1");function H({header:d,rows:i}){const[l,t]=S(false);const s=z(d,i);const a=l?d:d.slice(0,T);const h=l?i:i.slice(0,C);const f=m(p,{style:l&&e.tableWide,children:[r(p,{style:e.tableHead,children:a.map((n,u)=>r(c,{style:[e.th,u===0&&e.firstCol,l&&e.colFixed],numberOfLines:1,children:n.replace(/\*\*/g,"")},u))}),h.map((n,u)=>r(p,{style:[e.tr,u>0&&e.trDivider],children:a.map((V,b)=>r(c,{style:[e.td,b===0&&e.firstCol,b===0&&e.tdFirst,l&&e.colFixed],numberOfLines:l?3:2,children:renderInline(_(n[b]??""),e.td)},b))},u))]});return m(p,{style:e.table,testID:"buoy-ask-buoy-md-table",children:[l?r(g,{style:e.tableScrollV,nestedScrollEnabled:true,children:r(g,{horizontal:true,showsHorizontalScrollIndicator:true,children:f})}):f,s?m(W,{testID:"buoy-ask-buoy-md-table-toggle",accessibilityRole:"button",accessibilityLabel:l?"Show fewer rows":"Show all rows and columns",onPress:()=>t(n=>!n),style:({pressed:n})=>[e.tfoot,n&&e.pressed],children:[r(c,{style:e.tfootText,children:l?`All ${i.length} rows \xB7 ${d.length} columns`:s}),r(c,{style:e.tfootAction,children:l?"Show fewer":"Show all"})]}):null]})}export function Markdown({text:d,style:i}){const l=k(d);return r(p,{style:e.wrap,children:l.map((t,s)=>{if(t.kind==="table")return r(H,{header:t.header,rows:t.rows},s);if(t.kind==="code"){return r(g,{horizontal:true,style:e.codeBlock,contentContainerStyle:e.codeBlockContent,testID:"buoy-ask-buoy-md-code",children:r(c,{style:e.codeText,selectable:true,children:t.text})},s)}if(t.kind==="li"){return m(p,{style:[e.li,{paddingLeft:4+t.depth*14}],children:[r(c,{style:[i,e.marker],children:t.marker}),r(c,{style:[i,e.liText],selectable:true,children:renderInline(t.text,i)})]},s)}return r(c,{style:[i,t.kind==="h"&&e.bold],selectable:true,children:renderInline(t.text,i)},s)})})}const e=x.create({wrap:{gap:8},bold:{fontWeight:"600"},italic:{fontStyle:"italic"},code:{fontFamily:w.OS==="web"?y:"Menlo",fontSize:13,color:o.color.accent,backgroundColor:o.color.fill},codeBlock:{borderRadius:o.radius.row,borderWidth:x.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface},codeBlockContent:{paddingHorizontal:12,paddingVertical:10},codeText:{fontFamily:w.OS==="web"?y:"Menlo",fontSize:12,lineHeight:18,color:o.color.text},li:{flexDirection:"row",gap:8},table:{borderRadius:o.radius.row,borderWidth:x.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface,overflow:"hidden"},tableWide:{minWidth:"100%"},tableScrollV:{maxHeight:320},tableHead:{flexDirection:"row",paddingHorizontal:10,paddingVertical:6,gap:8,backgroundColor:o.color.fill},th:{flex:1,color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"700",letterSpacing:.6,textTransform:"uppercase"},tr:{flexDirection:"row",paddingHorizontal:10,paddingVertical:7,gap:8},trDivider:{borderTopWidth:x.hairlineWidth,borderTopColor:o.color.separator},td:{flex:1,color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:17},tdFirst:{color:o.color.text,fontWeight:"500"},firstCol:{flex:1.3},colFixed:{flex:0,width:110},tfoot:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",paddingHorizontal:10,paddingVertical:8,borderTopWidth:x.hairlineWidth,borderTopColor:o.color.border},tfootText:{color:o.color.textSecondary,fontSize:o.font.micro},tfootAction:{color:o.color.accent,fontSize:o.font.micro,fontWeight:"600"},pressed:{opacity:.7},marker:{color:o.color.textSecondary,minWidth:14,textAlign:"right"},liText:{flex:1}});
1
+ "use strict";import{monoFont as y}from"@buoy-gg/shared-ui";import{Platform as w}from"react-native";import{useState as S}from"react";import{Pressable as k,ScrollView as g,StyleSheet as b,Text as c,View as p}from"react-native";import{night as o}from"@buoy-gg/shared-ui";import{parseBlocks as W,TABLE_PREVIEW_COLS as T,TABLE_PREVIEW_ROWS as C,tableTruncation as z}from"./markdownParse";import{jsx as r,jsxs as m}from"react/jsx-runtime";export function renderInline(d,i){const l=d.replace(/!?\[([^\]]*)\]\([^)]*\)/g,"$1");const t=[];const s=/(\*\*[^*]+\*\*|`[^`]+`|\*[^*\s][^*]*\*|_[^_\s][^_]*_)/g;let a=0;let h;let f=0;while(h=s.exec(l)){if(h.index>a)t.push(l.slice(a,h.index));const n=h[0];if(n.startsWith("**")){t.push(r(c,{style:[i,e.bold],children:n.slice(2,-2)},f++))}else if(n.startsWith("`")){t.push(r(c,{style:[i,e.code],children:n.slice(1,-1)},f++))}else{t.push(r(c,{style:[i,e.italic],children:n.slice(1,-1)},f++))}a=h.index+n.length}if(a<l.length)t.push(l.slice(a));return t}const _=d=>d.replace(/!?\[([^\]]*)\]\([^)]*\)/g,"$1");function H({header:d,rows:i}){const[l,t]=S(false);const s=z(d,i);const a=l?d:d.slice(0,T);const h=l?i:i.slice(0,C);const f=m(p,{style:l&&e.tableWide,children:[r(p,{style:e.tableHead,children:a.map((n,u)=>r(c,{style:[e.th,u===0&&e.firstCol,l&&e.colFixed],numberOfLines:1,children:n.replace(/\*\*/g,"")},u))}),h.map((n,u)=>r(p,{style:[e.tr,u>0&&e.trDivider],children:a.map((V,x)=>r(c,{style:[e.td,x===0&&e.firstCol,x===0&&e.tdFirst,l&&e.colFixed],numberOfLines:l?3:2,children:renderInline(_(n[x]??""),e.td)},x))},u))]});return m(p,{style:e.table,testID:"buoy-ask-buoy-md-table",children:[l?r(g,{keyboardShouldPersistTaps:"handled",style:e.tableScrollV,nestedScrollEnabled:true,children:r(g,{horizontal:true,showsHorizontalScrollIndicator:true,children:f})}):f,s?m(k,{testID:"buoy-ask-buoy-md-table-toggle",accessibilityRole:"button",accessibilityLabel:l?"Show fewer rows":"Show all rows and columns",onPress:()=>t(n=>!n),style:({pressed:n})=>[e.tfoot,n&&e.pressed],children:[r(c,{style:e.tfootText,children:l?`All ${i.length} rows \xB7 ${d.length} columns`:s}),r(c,{style:e.tfootAction,children:l?"Show fewer":"Show all"})]}):null]})}export function Markdown({text:d,style:i}){const l=W(d);return r(p,{style:e.wrap,children:l.map((t,s)=>{if(t.kind==="table")return r(H,{header:t.header,rows:t.rows},s);if(t.kind==="code"){return r(g,{horizontal:true,style:e.codeBlock,contentContainerStyle:e.codeBlockContent,testID:"buoy-ask-buoy-md-code",children:r(c,{style:e.codeText,selectable:true,children:t.text})},s)}if(t.kind==="li"){return m(p,{style:[e.li,{paddingLeft:4+t.depth*14}],children:[r(c,{style:[i,e.marker],children:t.marker}),r(c,{style:[i,e.liText],selectable:true,children:renderInline(t.text,i)})]},s)}return r(c,{style:[i,t.kind==="h"&&e.bold],selectable:true,children:renderInline(t.text,i)},s)})})}const e=b.create({wrap:{gap:8},bold:{fontWeight:"600"},italic:{fontStyle:"italic"},code:{fontFamily:w.OS==="web"?y:"Menlo",fontSize:13,color:o.color.accent,backgroundColor:o.color.fill},codeBlock:{borderRadius:o.radius.row,borderWidth:b.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface},codeBlockContent:{paddingHorizontal:12,paddingVertical:10},codeText:{fontFamily:w.OS==="web"?y:"Menlo",fontSize:12,lineHeight:18,color:o.color.text},li:{flexDirection:"row",gap:8},table:{borderRadius:o.radius.row,borderWidth:b.hairlineWidth,borderColor:o.color.border,backgroundColor:o.color.surface,overflow:"hidden"},tableWide:{minWidth:"100%"},tableScrollV:{maxHeight:320},tableHead:{flexDirection:"row",paddingHorizontal:10,paddingVertical:6,gap:8,backgroundColor:o.color.fill},th:{flex:1,color:o.color.textTertiary,fontSize:o.font.micro,fontWeight:"700",letterSpacing:.6,textTransform:"uppercase"},tr:{flexDirection:"row",paddingHorizontal:10,paddingVertical:7,gap:8},trDivider:{borderTopWidth:b.hairlineWidth,borderTopColor:o.color.separator},td:{flex:1,color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:17},tdFirst:{color:o.color.text,fontWeight:"500"},firstCol:{flex:1.3},colFixed:{flex:0,width:110},tfoot:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",paddingHorizontal:10,paddingVertical:8,borderTopWidth:b.hairlineWidth,borderTopColor:o.color.border},tfootText:{color:o.color.textSecondary,fontSize:o.font.micro},tfootAction:{color:o.color.accent,fontSize:o.font.micro,fontWeight:"600"},pressed:{opacity:.7},marker:{color:o.color.textSecondary,minWidth:14,textAlign:"right"},liText:{flex:1}});
@@ -0,0 +1 @@
1
+ "use strict";import{describe as r,expect as o,it as s,vi as i}from"vitest";i.mock("react",async e=>({...await e(),useState:t=>[typeof t==="function"?t():t,()=>{}],useRef:t=>({current:t}),useEffect:()=>{},useCallback:t=>t}));i.mock("../blocks/context",async e=>({...await e(),useBlockHandlers:()=>({answer:()=>{},runAction:()=>{},answered:{}})}));import{Choice as c}from"../blocks/AskBlocks";import{Composer as l}from"./Composer";import{EmptyState as u}from"./EmptyState";function n(e){if(Array.isArray(e))return e.flatMap(n);if(!e||typeof e!=="object")return[];const t=e;return[t,...n(t.props?.children)]}function a(e){if(typeof e==="string"||typeof e==="number")return String(e);if(Array.isArray(e))return e.map(a).join("");return e&&typeof e==="object"?a(e.props?.children):""}r("the setup message",()=>{s("names the setting, not a React component",()=>{const e=a(u({configured:false,onPick:()=>{}}));o(e).toContain("askBuoy");o(e).not.toContain("FloatingDevTools")})});r("the message box",()=>{s("has a name a screen reader can say",()=>{const e=n(l({onSend:()=>{},onStop:()=>{},busy:false})).find(t=>t.props?.testID==="buoy-ask-buoy-input");o(e?.props?.accessibilityLabel).toBe("Message for Ask Buoy")})});r("a choice card",()=>{const e={id:"ask-1",kind:"choice",question:"If you want the value of `e2e-after-local`, ask and I'll read it.",options:[{id:"yes",label:"Yes, go ahead"},{id:"no",label:"No thanks"}]};s("shows the question the model wrote",()=>{o(a(c({block:e}))).toContain("e2e-after-local")});s("leaves it out when the answer above already says it",()=>{const t=a(c({block:{...e,questionInAnswer:true}}));o(t).not.toContain("e2e-after-local");o(t).toContain("Yes, go ahead")})});
@@ -1,3 +1,3 @@
1
1
  "use strict";import{idFactory as c}from"@buoy-gg/agent-core";import{realNow as i}from"@buoy-gg/shared-ui";import{writeSavedLedger as k}from"./transcriptPersistence";const l=c("m");const f=400;function x(t){if(!t||Object.keys(t).length===0)return void 0;try{const e=JSON.stringify(t);return e.length>f?`${e.slice(0,f)}\u2026`:e}catch{return void 0}}const w=c("q");let s={messages:[],busy:false,stopping:false,pending:null,trusted:[],notice:null,configured:false,answers:{},awaitingBlockId:null,actionState:{},queued:[],draft:null,usage:{input:0,output:0,requests:0},credits:null,claudeOut:false,meta:{}};let m=null;let h=null;const d=new Set;let p=null;let b=null;let g=null;function n(t){s={...s,...t};for(const e of d){try{e()}catch{}}}export const askBuoyStore={getState:()=>s,subscribe(t){d.add(t);return()=>d.delete(t)},setLedger(t){if(t===p)return;b?.();p=t;b=t?t.subscribe(()=>void k(t.list())):null},getLedger(){return p},holdSession(t,e){g={session:t,signature:e}},getHeldSession(){return g},setReporter(t){m=t},getReporter(){return m},setUsageReader(t){h=t},getUsageReader(){return h},setNotice(t){n({notice:t})},setConfigured(t){if(t!==s.configured)n({configured:t})},setClaudeOut(t){if(s.claudeOut!==t)n({claudeOut:t})},setMeta(t){const e=Object.keys(t).every(a=>s.meta[a]===t[a]);if(!e)n({meta:{...s.meta,...t}})},addUser(t){n({messages:[...s.messages,{id:l(),role:"user",text:t,toolCalls:[],blocks:[]}],busy:true})},beginTurn(){n({busy:true})},enqueue(t){const e={id:w(),text:t};n({queued:[...s.queued,e]});return e},removeQueued(t){if(!s.queued.some(e=>e.id===t))return;n({queued:s.queued.filter(e=>e.id!==t)})},shiftQueued(){const[t,...e]=s.queued;if(!t)return void 0;n({queued:e});return t},takeQueuedToDraft(t){const e=s.queued.find(a=>a.id===t);if(!e)return;n({queued:s.queued.filter(a=>a.id!==t),draft:e.text})},requestStop(){if(!s.busy||s.stopping)return;n({stopping:true})},clearDraft(){if(s.draft===null)return;n({draft:null})},removeMessage(t){n({messages:s.messages.filter(e=>e.id!==t)})},recordAnswer(t){n({answers:{...s.answers,[t.blockId]:t},awaitingBlockId:s.awaitingBlockId===t.blockId?null:s.awaitingBlockId})},setActionState(t,e){const a={...s.actionState};if(e===null)delete a[t];else a[t]=e;n({actionState:a})},startAssistant(){const t=l();n({messages:[...s.messages,{id:t,role:"assistant",text:"",toolCalls:[],blocks:[],streaming:true,startedAt:i()}]});return t},applyEvent(t,e){if(e.type==="history-trimmed"){const r=s.messages.findIndex(y=>y.id===t);const o={id:l(),role:"assistant",system:true,text:`Older messages were trimmed from the agent's memory (${e.droppedRounds} exchange${e.droppedRounds===1?"":"s"}).`+(e.droppedPinned?` ${e.droppedPinned===1?"One of them made a change that is":`${e.droppedPinned} of them made changes that are`} still applied \u2014 the changes bar still lists ${e.droppedPinned===1?"it":"them"}, but the agent no longer remembers making ${e.droppedPinned===1?"it":"them"}.`:" It may not recall setup from earlier in this chat \u2014 the changes bar still lists what is applied."),toolCalls:[],blocks:[],endedAt:i()};const u=[...s.messages];u.splice(r<0?u.length:r,0,o);n({messages:u});return}const a=s.messages.map(r=>{if(r.id!==t)return r;switch(e.type){case"retrying":{const o=Math.max(1,Math.round(e.inMs/1e3));return{...r,waiting:e.reason==="throttled"?`Endpoint busy \u2014 retrying in ${o}s (${e.attempt} of ${e.maxAttempts})`:e.reason==="network"?"Connection dropped \u2014 trying again":`Request too large \u2014 trimming older messages and retrying`}}case"text":return{...r,waiting:void 0,text:(r.breakBeforeNextText||e.breakBefore)&&r.text?`${r.text.trimEnd()}
2
2
 
3
- ${e.delta}`:r.text+e.delta,breakBeforeNextText:false};case"reasoning":return{...r,breakBeforeNextText:true,reasoning:[...r.reasoning??[],{text:e.text,afterCalls:r.toolCalls.length}]};case"tool-start":return{...r,waiting:void 0,toolCalls:[...r.toolCalls,{id:e.id,toolId:e.toolId,action:e.action,label:e.label,description:e.description,status:"running",params:x(e.params),effect:e.effect,startedAt:i()}]};case"tool-end":return{...r,breakBeforeNextText:true,toolCalls:r.toolCalls.map(o=>o.id===e.id?{...o,status:!e.ok&&e.summary==="refused"?"refused":!e.ok&&e.summary==="declined"?"declined":!e.ok&&e.summary==="stopped"?"stopped":e.ok?"ok":"failed",summary:e.summary,durationMs:e.durationMs,result:e.result}:o)};case"tool-verified":return{...r,toolCalls:r.toolCalls.map(o=>o.id===e.id?{...o,verification:e.verification}:o)};case"block":return{...r,breakBeforeNextText:true,blocks:[...r.blocks,{block:e.block,forToolCallId:e.forToolCallId}]};case"usage":return{...r,usage:{input:(r.usage?.input??0)+e.input,output:(r.usage?.output??0)+e.output,requests:(r.usage?.requests??0)+1}};case"error":return{...r,waiting:void 0,error:e.message,...e.hosted?{errorInfo:{code:e.hosted.code,requestId:e.hosted.request_id,action:e.hosted.action,resetAt:e.hosted.reset_at,helpUrl:e.hosted.help_url}}:{}};default:return r}});n({messages:a,...e.type==="awaiting-user"?{awaitingBlockId:e.blockId}:{},...e.type==="error"&&e.hosted?.code==="claude_paused"?{claudeOut:true}:{},...e.type==="meter"&&e.frame.phase==="end"?{credits:{left:e.frame.credits_left??s.credits?.left??null,resetAt:e.frame.credits_reset??s.credits?.resetAt??null,pending:e.frame.pending===true}}:{},...e.type==="usage"?{usage:{input:s.usage.input+e.input,output:s.usage.output+e.output,requests:s.usage.requests+1}}:{}})},markStopped(t){n({messages:s.messages.map(e=>e.id===t?{...e,stopped:true}:e)})},finishAssistant(t){n({messages:s.messages.map(e=>e.id===t?{...e,streaming:false,endedAt:i()}:e),busy:false,stopping:false})},askApproval(t){return new Promise(e=>{n({pending:{...t,resolve:e}})})},answerApproval(t){const e=s.pending;const a=typeof t==="boolean"?t:t.approved;const r=typeof t!=="boolean"&&t.trust===true&&a;const o=e?`${e.toolId}.${e.action}`:"";n({pending:null,...r&&e&&!s.trusted.some(u=>u.key===o)?{trusted:[...s.trusted,{key:o,label:e.label}]}:{}});e?.resolve(t)},untrust(t){g?.session.untrust(t);n({trusted:s.trusted.filter(e=>e.key!==t)})},restorePending(t){if(s.pending||s.busy)return false;n({pending:{...t,restored:true,resolve:()=>{}}});return true},addNote(t){n({messages:[...s.messages,{id:l(),role:"assistant",text:t,toolCalls:[],blocks:[],endedAt:i()}]})},hydrate(t){if(s.messages.length>0||s.busy)return false;n({messages:t.messages.map(e=>({...e,restored:true})),answers:t.answers,usage:t.usage,queued:t.queued??[]});return true},reset(){s.pending?.resolve(false);n({messages:[],busy:false,stopping:false,pending:null,trusted:[],notice:null,answers:{},awaitingBlockId:null,actionState:{},queued:[],draft:null,usage:{input:0,output:0,requests:0}})}};export function turnPhaseOf(t){if(t.pending)return"awaitingApproval";if(t.busy)return t.stopping?"stopping":"running";if(t.awaitingBlockId)return"awaitingAnswer";return"idle"}export function queuePauseReason(t){const e=turnPhaseOf(t);if(e==="running"||e==="stopping")return null;if(e==="awaitingApproval")return"Paused \u2014 answer the approval above first.";if(e==="awaitingAnswer")return"Paused \u2014 answer the question above first.";const a=t.messages[t.messages.length-1];if(a?.role==="assistant"&&a.error)return"Paused \u2014 the last turn failed.";if(a?.role==="assistant"&&a.stopped)return"Paused \u2014 you stopped the last turn.";return null}
3
+ ${e.delta}`:r.text+e.delta,breakBeforeNextText:false};case"reasoning":return{...r,breakBeforeNextText:true,reasoning:[...r.reasoning??[],{text:e.text,afterCalls:r.toolCalls.length}]};case"tool-start":return{...r,waiting:void 0,toolCalls:[...r.toolCalls,{id:e.id,toolId:e.toolId,action:e.action,label:e.label,description:e.description,status:"running",params:x(e.params),effect:e.effect,startedAt:i()}]};case"tool-end":return{...r,breakBeforeNextText:true,toolCalls:r.toolCalls.map(o=>o.id===e.id?{...o,status:!e.ok&&e.summary==="refused"?"refused":!e.ok&&e.summary==="declined"?"declined":!e.ok&&e.summary==="stopped"?"stopped":e.ok?"ok":"failed",summary:e.summary,durationMs:e.durationMs,result:e.result}:o)};case"tool-verified":return{...r,toolCalls:r.toolCalls.map(o=>o.id===e.id?{...o,verification:e.verification}:o)};case"block":return{...r,breakBeforeNextText:true,blocks:[...r.blocks,{block:e.block,forToolCallId:e.forToolCallId}]};case"done":return e.stopReason==="realm-died"?{...r,reloadedApp:true}:r;case"usage":return{...r,usage:{input:(r.usage?.input??0)+e.input,output:(r.usage?.output??0)+e.output,requests:(r.usage?.requests??0)+1}};case"error":return{...r,waiting:void 0,error:e.message,...e.hosted?{errorInfo:{code:e.hosted.code,requestId:e.hosted.request_id,action:e.hosted.action,resetAt:e.hosted.reset_at,helpUrl:e.hosted.help_url}}:{}};default:return r}});n({messages:a,...e.type==="awaiting-user"?{awaitingBlockId:e.blockId}:{},...e.type==="error"&&e.hosted?.code==="claude_paused"?{claudeOut:true}:{},...e.type==="meter"&&e.frame.phase==="end"?{credits:{left:e.frame.credits_left??s.credits?.left??null,resetAt:e.frame.credits_reset??s.credits?.resetAt??null,pending:e.frame.pending===true}}:{},...e.type==="usage"?{usage:{input:s.usage.input+e.input,output:s.usage.output+e.output,requests:s.usage.requests+1}}:{}})},markStopped(t){n({messages:s.messages.map(e=>e.id===t?{...e,stopped:true}:e)})},finishAssistant(t){n({messages:s.messages.map(e=>e.id===t?{...e,streaming:false,endedAt:i()}:e),busy:false,stopping:false})},askApproval(t){return new Promise(e=>{n({pending:{...t,resolve:e}})})},answerApproval(t){const e=s.pending;const a=typeof t==="boolean"?t:t.approved;const r=typeof t!=="boolean"&&t.trust===true&&a;const o=e?`${e.toolId}.${e.action}`:"";n({pending:null,...r&&e&&!s.trusted.some(u=>u.key===o)?{trusted:[...s.trusted,{key:o,label:e.label}]}:{}});e?.resolve(t)},untrust(t){g?.session.untrust(t);n({trusted:s.trusted.filter(e=>e.key!==t)})},restorePending(t){if(s.pending||s.busy)return false;n({pending:{...t,restored:true,resolve:()=>{}}});return true},addNote(t){n({messages:[...s.messages,{id:l(),role:"assistant",text:t,toolCalls:[],blocks:[],endedAt:i()}]})},hydrate(t){if(s.messages.length>0||s.busy)return false;n({messages:t.messages.map(e=>({...e,restored:true})),answers:t.answers,usage:t.usage,queued:t.queued??[]});return true},reset(){s.pending?.resolve(false);n({messages:[],busy:false,stopping:false,pending:null,trusted:[],notice:null,answers:{},awaitingBlockId:null,actionState:{},queued:[],draft:null,usage:{input:0,output:0,requests:0}})}};export function turnPhaseOf(t){if(t.pending)return"awaitingApproval";if(t.busy)return t.stopping?"stopping":"running";if(t.awaitingBlockId)return"awaitingAnswer";return"idle"}export function queuePauseReason(t){const e=turnPhaseOf(t);if(e==="running"||e==="stopping")return null;if(e==="awaitingApproval")return"Paused \u2014 answer the approval above first.";if(e==="awaitingAnswer")return"Paused \u2014 answer the question above first.";const a=t.messages[t.messages.length-1];if(a?.role==="assistant"&&a.error)return"Paused \u2014 the last turn failed.";if(a?.role==="assistant"&&a.stopped)return"Paused \u2014 you stopped the last turn.";return null}
@@ -1 +1 @@
1
- "use strict";import{persistentStorage as o,devToolsStorageKeys as d}from"@buoy-gg/shared-ui";import{redactForWire as i}from"../sync/wire";export const TRANSCRIPT_KEY=`${d.base}_ask_buoy_transcript`;const u=1;const c=60;const g=256e3;const p="[not kept across a restart \u2014 read it again if you need it]";function s(e){return e?i(e):e}function y(e){const t=e.streaming===true;return{...e,text:s(e.text),streaming:false,reasoning:e.reasoning?.map(r=>({...r,text:s(r.text)})),toolCalls:e.toolCalls.map(r=>{const n=r.params?s(r.params):void 0;const{result:A,...l}=r;return r.status==="running"?{...l,params:n,status:"failed",summary:"Interrupted"}:{...l,params:n}}),error:e.error??(t&&!e.text?"This answer was interrupted \u2014 the app restarted mid-turn.":void 0)}}function h(e){return e.map(t=>{if(t.role==="tool-results"){return{...t,results:t.results.map(r=>({...r,content:p}))}}if(t.role==="assistant")return{...t,text:s(t.text)};return{...t,text:s(t.text)}})}export function fitToCaps(e){let t={...e,messages:e.messages.slice(-c),history:e.history.slice(-c*2)};let r=JSON.stringify(t);while(r.length>g&&t.messages.length>1){t={...t,messages:t.messages.slice(1),history:t.history.slice(Math.min(2,t.history.length))};r=JSON.stringify(t)}return t}function m(e){if(!e)return null;const t=e.params;if(!t)return null;let r;try{r=JSON.stringify(t)}catch{return null}if(i(r)!==r)return null;return{id:e.id,toolId:e.toolId,action:e.action,label:s(e.label),description:e.description?s(e.description):void 0,reason:s(e.reason),params:t,targetDigest:e.targetDigest}}function x(e){if(typeof e!=="object"||e===null)return false;const t=e;return typeof t.id==="string"&&typeof t.toolId==="string"&&typeof t.action==="string"&&typeof t.label==="string"&&typeof t.reason==="string"&&typeof t.params==="object"&&t.params!==null}function f(e){if(!e?.length)return void 0;const t=e.filter(r=>{try{const n=JSON.stringify(r);return i(n)===n}catch{return false}});return t.length?t:void 0}export function buildPayload(e){return fitToCaps({v:u,savedAt:Date.now(),messages:e.messages.map(y),history:h(e.history),answers:e.answers,usage:e.usage,pending:m(e.pending),ledger:f(e.ledger),queued:e.queued?.length?e.queued.map(t=>({id:t.id,text:s(t.text)})):void 0})}export function parsePayload(e){if(!e)return null;try{const t=JSON.parse(e);if(t?.v!==u||!Array.isArray(t.messages))return null;if(t.messages.length===0)return null;return{v:u,savedAt:typeof t.savedAt==="number"?t.savedAt:0,messages:t.messages,history:Array.isArray(t.history)?t.history:[],answers:t.answers??{},usage:t.usage??{input:0,output:0,requests:0},pending:x(t.pending)?t.pending:null,ledger:Array.isArray(t.ledger)?t.ledger.filter(r=>typeof r==="object"&&r!==null&&typeof r.id==="string"):void 0,queued:Array.isArray(t.queued)?t.queued.filter(r=>typeof r==="object"&&r!==null&&typeof r.id==="string"&&typeof r.text==="string"):void 0}}catch{return null}}export async function readTranscript(){try{return parsePayload(await o.getItem(TRANSCRIPT_KEY))}catch{return null}}let a=0;export async function writeTranscript(e){a+=1;try{await o.setItem(TRANSCRIPT_KEY,JSON.stringify(e))}catch{}}export async function writeSavedLedger(e){const t=a;const r=await readTranscript();if(!r||t!==a)return;await writeTranscript(fitToCaps({...r,ledger:f(e)}))}export async function clearTranscript(){a+=1;try{await o.removeItem(TRANSCRIPT_KEY)}catch{}}
1
+ "use strict";import{persistentStorage as i,devToolsStorageKeys as p}from"@buoy-gg/shared-ui";import{redactForWire as u}from"../sync/wire";export const TRANSCRIPT_KEY=`${p.base}_ask_buoy_transcript`;const l=1;const f=60;const g=256e3;const y="[not kept across a restart \u2014 read it again if you need it]";function s(t){return t?u(t):t}function h(t){const e=t.streaming===true;return{...t,text:s(t.text),streaming:false,reasoning:t.reasoning?.map(r=>({...r,text:s(r.text)})),toolCalls:t.toolCalls.map(r=>{const n=r.params?s(r.params):void 0;const{result:c,...a}=r;return r.status==="running"?{...a,params:n,status:"failed",summary:"Interrupted"}:{...a,params:n}}),error:t.error??(e&&!t.text?"This answer was interrupted \u2014 the app restarted mid-turn.":void 0)}}function m(t){return t.map(e=>{if(e.role==="tool-results"){return{...e,results:e.results.map(r=>({...r,content:y}))}}if(e.role==="assistant")return{...e,text:s(e.text)};return{...e,text:s(e.text)}})}export function fitToCaps(t){let e={...t,messages:t.messages.slice(-f),history:t.history.slice(-f*2)};let r=JSON.stringify(e);while(r.length>g&&e.messages.length>1){e={...e,messages:e.messages.slice(1),history:e.history.slice(Math.min(2,e.history.length))};r=JSON.stringify(e)}return e}function A(t){if(!t)return null;const e=t.params;if(!e)return null;let r;try{r=JSON.stringify(e)}catch{return null}if(u(r)!==r)return null;return{id:t.id,toolId:t.toolId,action:t.action,label:s(t.label),description:t.description?s(t.description):void 0,reason:s(t.reason),params:e,targetDigest:t.targetDigest}}function x(t){if(typeof t!=="object"||t===null)return false;const e=t;return typeof e.id==="string"&&typeof e.toolId==="string"&&typeof e.action==="string"&&typeof e.label==="string"&&typeof e.reason==="string"&&typeof e.params==="object"&&e.params!==null}function d(t){if(!t?.length)return void 0;const e=t.filter(r=>{try{const n=JSON.stringify(r);return u(n)===n}catch{return false}});return e.length?e:void 0}export function buildPayload(t){return fitToCaps({v:l,savedAt:Date.now(),messages:t.messages.map(h),history:m(t.history),answers:t.answers,usage:t.usage,pending:A(t.pending),ledger:d(t.ledger),queued:t.queued?.length?t.queued.map(e=>({id:e.id,text:s(e.text)})):void 0})}export function parsePayload(t){if(!t)return null;try{const e=JSON.parse(t);if(e?.v!==l||!Array.isArray(e.messages))return null;if(e.messages.length===0)return null;return{v:l,savedAt:typeof e.savedAt==="number"?e.savedAt:0,messages:e.messages,history:Array.isArray(e.history)?e.history:[],answers:e.answers??{},usage:e.usage??{input:0,output:0,requests:0},pending:x(e.pending)?e.pending:null,ledger:Array.isArray(e.ledger)?e.ledger.filter(r=>typeof r==="object"&&r!==null&&typeof r.id==="string"):void 0,queued:Array.isArray(e.queued)?e.queued.filter(r=>typeof r==="object"&&r!==null&&typeof r.id==="string"&&typeof r.text==="string"):void 0}}catch{return null}}export const RESUME_AFTER_RELOAD="The app reloaded. Please finish what you were doing.";export const RELOAD_NOTE='The app reloaded during my last step. Say "continue" to finish.';const S=2*6e4;export function afterReload(t,e){const r=t.messages[t.messages.length-1];if(r?.role!=="assistant"||!r.reloadedApp)return null;const n=[...t.messages].reverse().find(a=>a.role==="user");const c=e-t.savedAt<S;return c&&n?.text!==RESUME_AFTER_RELOAD?"resume":"note"}export async function readTranscript(){try{return parsePayload(await i.getItem(TRANSCRIPT_KEY))}catch{return null}}let o=0;export async function writeTranscript(t){o+=1;try{await i.setItem(TRANSCRIPT_KEY,JSON.stringify(t))}catch{}}export async function writeSavedLedger(t){const e=o;const r=await readTranscript();if(!r||e!==o)return;await writeTranscript(fitToCaps({...r,ledger:d(t)}))}export async function clearTranscript(){o+=1;try{await i.removeItem(TRANSCRIPT_KEY)}catch{}}
@@ -1 +1 @@
1
- "use strict";import{describe as d,expect as t,it as o}from"vitest";import{buildPayload as g,fitToCaps as c,parsePayload as i}from"./transcriptPersistence";const n=(e={})=>({id:"m1",role:"assistant",text:"ok",toolCalls:[],blocks:[],...e});const r=(e,a=[])=>g({messages:e,history:a,answers:{},usage:{input:0,output:0,requests:0}});d("what reaches the disk",()=>{o("never writes a tool result",()=>{const e=[{role:"user",text:"what's in the bag?"},{role:"assistant",text:"",toolCalls:[{id:"c1",name:"buoy_zustand",input:{}}]},{role:"tool-results",results:[{toolCallId:"c1",content:'{"lines":[{"card":"4111111111111111"}]}'}]},{role:"assistant",text:"Five items."}];const a=r([n()],e);const s=JSON.stringify(a);t(s).not.toContain("4111111111111111");t(a.history.map(l=>l.role)).toEqual(["user","assistant","tool-results","assistant"])});o("never writes a step's result, even when it writes the step",()=>{const e=r([n({toolCalls:[{id:"c1",toolId:"zustand",action:"getStoreState",label:"Read the bag",status:"ok",summary:"Done",params:'{"storeName":"bag"}',result:'{"card":"4111111111111111"}'}]})]);t(JSON.stringify(e)).not.toContain("4111111111111111");t(e.messages[0].toolCalls[0].result).toBeUndefined();t(e.messages[0].toolCalls[0].action).toBe("getStoreState")});o("scrubs credential shapes out of what the agent said",()=>{const e=r([n({text:"The header was Bearer abcdefghijklmnop123456"})]);t(e.messages[0].text).not.toContain("abcdefghijklmnop123456")});o("caps a long session instead of growing a file forever",()=>{const e=Array.from({length:400},(s,l)=>n({id:`m${l}`,text:`answer ${l}`}));const a=r(e);t(a.messages.length).toBeLessThanOrEqual(60);t(a.messages[a.messages.length-1].text).toBe("answer 399")});o("drops from the oldest end until a heavy transcript fits",()=>{const e=Array.from({length:20},(s,l)=>n({id:`m${l}`,text:"x".repeat(4e4)}));const a=c({v:1,savedAt:0,messages:e,history:[],answers:{},usage:{input:0,output:0,requests:0}});t(JSON.stringify(a).length).toBeLessThanOrEqual(256e3);t(a.messages.length).toBeGreaterThan(0)})});d("what comes back",()=>{o("marks a turn the app died in as interrupted rather than blank",()=>{const e=r([n({text:"",streaming:true})]);t(e.messages[0].streaming).toBe(false);t(e.messages[0].error).toMatch(/interrupted/i)});o("never restores a tool call still claiming to run",()=>{const e=r([n({streaming:true,toolCalls:[{id:"c1",toolId:"storage",action:"set",label:"Write a key",status:"running"}]})]);t(e.messages[0].toolCalls[0].status).toBe("failed");t(e.messages[0].toolCalls[0].summary).toBe("Interrupted")});o("keeps an answer that already finished exactly as it was",()=>{const e=r([n({text:"Five items.",streaming:false})]);t(e.messages[0].error).toBeUndefined();t(e.messages[0].text).toBe("Five items.")});o("treats anything it cannot trust as no transcript at all",()=>{t(i(null)).toBeNull();t(i("not json")).toBeNull();t(i(JSON.stringify({v:0,messages:[n()]}))).toBeNull();t(i(JSON.stringify({v:1,messages:[]}))).toBeNull()});o("round-trips a real conversation",()=>{const e=r([n({role:"user",text:"hi"}),n({id:"m2",text:"hello"})]);const a=i(JSON.stringify(e));t(a?.messages.map(s=>s.text)).toEqual(["hi","hello"])})});d("an unanswered approval",()=>{const e={id:"c1",toolId:"zustand",action:"setState",label:"Update the Pok\xE9 Mart bag store",description:"Change a live zustand store.",reason:"This will change the app's state.",params:{storeName:"bag",data:{lines:[]}},resolve:()=>{}};const a=(s={})=>g({messages:[n()],history:[],answers:{},usage:{input:0,output:0,requests:0},pending:{...e,...s}});o("survives the round trip with the exact call it asked about",()=>{const s=i(JSON.stringify(a()));t(s?.pending).toMatchObject({toolId:"zustand",action:"setState",label:"Update the Pok\xE9 Mart bag store",params:{storeName:"bag",data:{lines:[]}}})});o("is saved again after it has already been restored once",()=>{t(a({restored:true}).pending).toBeTruthy()});o("is dropped rather than scrubbed when the call carries a credential",()=>{const s=a({params:{key:"auth",value:"Bearer abcdefghijklmnop123456"}});t(s.pending).toBeNull();t(JSON.stringify(s)).not.toContain("abcdefghijklmnop123456")});o("reads back as absent from a transcript written before it existed",()=>{const s=r([n()]);t(i(JSON.stringify(s))?.pending).toBeNull()});o("refuses a malformed one rather than restoring half a question",()=>{const s=JSON.stringify({...r([n()]),pending:{toolId:"zustand"}});t(i(s)?.pending).toBeNull()})});d("the effect ledger",()=>{const e={id:"fx-1-1",at:1,toolId:"storage",action:"async.setItem",effect:{kind:"storage-write",toolId:"storage",action:"async.setItem",label:"Save @qa/key",key:"@qa/key",had:true,previous:"old value"}};const a=s=>g({messages:[n()],history:[],answers:{},usage:{input:0,output:0,requests:0},ledger:s});o("survives the round trip with the prior value an undo needs",()=>{const s=i(JSON.stringify(a([e])));t(s?.ledger).toHaveLength(1);t(s?.ledger?.[0]).toMatchObject({toolId:"storage",action:"async.setItem"})});o("drops an entry holding a credential rather than rewriting it",()=>{const s={...e,effect:{...e.effect,previous:"Bearer abcdefghijklmnop123456"}};const l=a([s]);t(l.ledger).toBeUndefined();t(JSON.stringify(l)).not.toContain("abcdefghijklmnop123456")});o("reads back as absent from a transcript written before it existed",()=>{t(i(JSON.stringify(r([n()])))?.ledger).toBeUndefined()})});
1
+ "use strict";import{describe as d,expect as t,it as n}from"vitest";import{afterReload as c,buildPayload as u,fitToCaps as h,parsePayload as l,RESUME_AFTER_RELOAD as p}from"./transcriptPersistence";import{askBuoyStore as g}from"./askBuoyStore";const r=(e={})=>({id:"m1",role:"assistant",text:"ok",toolCalls:[],blocks:[],...e});const i=(e,o=[])=>u({messages:e,history:o,answers:{},usage:{input:0,output:0,requests:0}});d("what reaches the disk",()=>{n("never writes a tool result",()=>{const e=[{role:"user",text:"what's in the bag?"},{role:"assistant",text:"",toolCalls:[{id:"c1",name:"buoy_zustand",input:{}}]},{role:"tool-results",results:[{toolCallId:"c1",content:'{"lines":[{"card":"4111111111111111"}]}'}]},{role:"assistant",text:"Five items."}];const o=i([r()],e);const s=JSON.stringify(o);t(s).not.toContain("4111111111111111");t(o.history.map(a=>a.role)).toEqual(["user","assistant","tool-results","assistant"])});n("never writes a step's result, even when it writes the step",()=>{const e=i([r({toolCalls:[{id:"c1",toolId:"zustand",action:"getStoreState",label:"Read the bag",status:"ok",summary:"Done",params:'{"storeName":"bag"}',result:'{"card":"4111111111111111"}'}]})]);t(JSON.stringify(e)).not.toContain("4111111111111111");t(e.messages[0].toolCalls[0].result).toBeUndefined();t(e.messages[0].toolCalls[0].action).toBe("getStoreState")});n("scrubs credential shapes out of what the agent said",()=>{const e=i([r({text:"The header was Bearer abcdefghijklmnop123456"})]);t(e.messages[0].text).not.toContain("abcdefghijklmnop123456")});n("caps a long session instead of growing a file forever",()=>{const e=Array.from({length:400},(s,a)=>r({id:`m${a}`,text:`answer ${a}`}));const o=i(e);t(o.messages.length).toBeLessThanOrEqual(60);t(o.messages[o.messages.length-1].text).toBe("answer 399")});n("drops from the oldest end until a heavy transcript fits",()=>{const e=Array.from({length:20},(s,a)=>r({id:`m${a}`,text:"x".repeat(4e4)}));const o=h({v:1,savedAt:0,messages:e,history:[],answers:{},usage:{input:0,output:0,requests:0}});t(JSON.stringify(o).length).toBeLessThanOrEqual(256e3);t(o.messages.length).toBeGreaterThan(0)})});d("what comes back",()=>{n("marks a turn the app died in as interrupted rather than blank",()=>{const e=i([r({text:"",streaming:true})]);t(e.messages[0].streaming).toBe(false);t(e.messages[0].error).toMatch(/interrupted/i)});n("never restores a tool call still claiming to run",()=>{const e=i([r({streaming:true,toolCalls:[{id:"c1",toolId:"storage",action:"set",label:"Write a key",status:"running"}]})]);t(e.messages[0].toolCalls[0].status).toBe("failed");t(e.messages[0].toolCalls[0].summary).toBe("Interrupted")});n("keeps an answer that already finished exactly as it was",()=>{const e=i([r({text:"Five items.",streaming:false})]);t(e.messages[0].error).toBeUndefined();t(e.messages[0].text).toBe("Five items.")});n("treats anything it cannot trust as no transcript at all",()=>{t(l(null)).toBeNull();t(l("not json")).toBeNull();t(l(JSON.stringify({v:0,messages:[r()]}))).toBeNull();t(l(JSON.stringify({v:1,messages:[]}))).toBeNull()});n("round-trips a real conversation",()=>{const e=i([r({role:"user",text:"hi"}),r({id:"m2",text:"hello"})]);const o=l(JSON.stringify(e));t(o?.messages.map(s=>s.text)).toEqual(["hi","hello"])})});d("an unanswered approval",()=>{const e={id:"c1",toolId:"zustand",action:"setState",label:"Update the Pok\xE9 Mart bag store",description:"Change a live zustand store.",reason:"This will change the app's state.",params:{storeName:"bag",data:{lines:[]}},resolve:()=>{}};const o=(s={})=>u({messages:[r()],history:[],answers:{},usage:{input:0,output:0,requests:0},pending:{...e,...s}});n("survives the round trip with the exact call it asked about",()=>{const s=l(JSON.stringify(o()));t(s?.pending).toMatchObject({toolId:"zustand",action:"setState",label:"Update the Pok\xE9 Mart bag store",params:{storeName:"bag",data:{lines:[]}}})});n("is saved again after it has already been restored once",()=>{t(o({restored:true}).pending).toBeTruthy()});n("is dropped rather than scrubbed when the call carries a credential",()=>{const s=o({params:{key:"auth",value:"Bearer abcdefghijklmnop123456"}});t(s.pending).toBeNull();t(JSON.stringify(s)).not.toContain("abcdefghijklmnop123456")});n("reads back as absent from a transcript written before it existed",()=>{const s=i([r()]);t(l(JSON.stringify(s))?.pending).toBeNull()});n("refuses a malformed one rather than restoring half a question",()=>{const s=JSON.stringify({...i([r()]),pending:{toolId:"zustand"}});t(l(s)?.pending).toBeNull()})});d("the effect ledger",()=>{const e={id:"fx-1-1",at:1,toolId:"storage",action:"async.setItem",effect:{kind:"storage-write",toolId:"storage",action:"async.setItem",label:"Save @qa/key",key:"@qa/key",had:true,previous:"old value"}};const o=s=>u({messages:[r()],history:[],answers:{},usage:{input:0,output:0,requests:0},ledger:s});n("survives the round trip with the prior value an undo needs",()=>{const s=l(JSON.stringify(o([e])));t(s?.ledger).toHaveLength(1);t(s?.ledger?.[0]).toMatchObject({toolId:"storage",action:"async.setItem"})});n("drops an entry holding a credential rather than rewriting it",()=>{const s={...e,effect:{...e.effect,previous:"Bearer abcdefghijklmnop123456"}};const a=o([s]);t(a.ledger).toBeUndefined();t(JSON.stringify(a)).not.toContain("abcdefghijklmnop123456")});n("reads back as absent from a transcript written before it existed",()=>{t(l(JSON.stringify(i([r()])))?.ledger).toBeUndefined()})});d("a turn that reloaded the app",()=>{const e=a=>r({id:`u-${a}`,role:"user",text:a});const o=r({id:"a1",text:"The app is reloading.",reloadedApp:true});const s=a=>l(JSON.stringify(i(a)));n("is marked when the engine stops for a reload",()=>{g.reset();const a=g.startAssistant();g.applyEvent(a,{type:"done",stopReason:"realm-died"});t(g.getState().messages[0].reloadedApp).toBe(true)});n("picks the task back up once, right after the reload",()=>{const a=s([e("make /v0 fail, reload, then undo"),o]);t(c(a,a.savedAt+1e3)).toBe("resume")});n("leaves a note instead of looping when the picked-up task reloads again",()=>{const a=s([e("make /v0 fail, reload, then undo"),o,e(p),o]);t(c(a,a.savedAt+1e3)).toBe("note")});n("leaves a note for a chat opened long after the reload",()=>{const a=s([e("reload the app"),o]);t(c(a,a.savedAt+10*6e4)).toBe("note")});n("does nothing for a turn that ended any other way",()=>{const a=s([e("hi"),r({text:"Hello."})]);t(c(a,a.savedAt)).toBeNull()})});
@@ -1 +1 @@
1
- "use strict";import{getUseIsPro as fe,hasAccountAccess as v,subscribeAccountAccess as he}from"@buoy-gg/shared-ui";import{useCallback as l,useEffect as p,useMemo as W,useRef as f,useState as ge,useSyncExternalStore as me}from"react";import{Platform as z}from"react-native";import{dispatchToolAction as J,listSnapshotToolIds as ye,listToolActions as be,readToolSnapshot as Ae,waitForToolRegistry as X}from"@buoy-gg/shared-ui";import{answerToText as Y,CATALOG as D,createAskBuoySession as ke,readTargetDigest as we,runGatedAction as Z,SNAPSHOT_ACTION as ee}from"@buoy-gg/agent-core";import{askBuoyStore as t,queuePauseReason as ve,turnPhaseOf as Se}from"./askBuoyStore";import{clearConversation as Te,resetConversation as Ne}from"./resetConversation";import{buildPayload as Oe,readTranscript as Re,writeTranscript as Be}from"./transcriptPersistence";import{useAskBuoySettings as Ie}from"./useAskBuoySettings";import{HOSTED_CLAUDE_MODEL as Ee,pickHostedModel as qe}from"../hostedModel";import{useHostedModel as Ce}from"./hostedModelSetting";const S=typeof __DEV__!=="undefined"&&!__DEV__;function Pe(c){for(const n of t.getState().messages){if(!n.restored)continue;for(const y of n.blocks){const h=y.block;if(h?.id!==c)continue;const g=h.question??h.label??h.title;return typeof g==="string"&&g.trim()?g.trim():void 0}}return void 0}function te(){try{const c=be();for(const n of ye()){if(!c[n])c[n]=[];if(!c[n].includes(ee)){c[n]=[ee,...c[n]]}}return c}catch{return{}}}const _e=600;export function useAskBuoyState(){return me(t.subscribe,t.getState,t.getState)}export function useAskBuoy(c){const n=useAskBuoyState();const y=Ce();const h=W(()=>qe(c,y,n.claudeOut),[c,y,n.claudeOut]);const g=f(h);if(n.messages.length===0||!g.current)g.current=h;const o=g.current;const L=f(c);L.current=c;const M=!!c?.hostedModels?.claude;const U=n.messages.length===0;p(()=>{const e=L.current?.readUsage;if(!M||y!=="claude"||!U||!e)return;let s=true;e().then(r=>{if(s&&r.claude)t.setClaudeOut(!r.claude.available)}).catch(()=>{});return()=>{s=false}},[M,y,U]);const T=fe()();const w=f(T);w.current=T;const N=l((...e)=>{if(!v()||!w.current)return Promise.reject(new Error("Ask Buoy requires Pro access."));return J(...e)},[]);const u=f(null);const j=f(false);const b=f(null);const F=f(o);F.current=o;const O=!!o?.endpoint&&!!o?.model;const R=o?.persistTranscript!==false;const{bypassApprovals:B,readOnly:I}=Ie();const _=f(B);_.current=B;const E=f(I);E.current=I;const q=l(()=>E.current?{...o?.policy??{},readOnly:true}:o?.policy??{},[o]);const[oe,se]=ge(0);p(()=>{let e=false;void X().then(()=>{if(!e)se(s=>s+1)});return()=>{e=true}},[]);const A=W(()=>te(),[oe]);const re=Object.keys(A).sort().join(",");p(()=>{t.setConfigured(O);t.setMeta({model:o?.model,protocol:o?.protocol??(o?"anthropic":void 0),appName:o?.appName,platform:z.OS,build:S?"release":"development"});if(!O||!o){u.current=null;t.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");return}const e=o.protocol??"anthropic";if(S&&o.apiKey){t.setNotice("This build carries a plaintext apiKey \u2014 that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds.")}else if(e==="anthropic"&&/^https?:\/\/api\.openai\.com\//i.test(o.endpoint)){t.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.')}else if(e==="openai"&&/^https?:\/\/api\.anthropic\.com\//i.test(o.endpoint)){t.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".')}else{t.setNotice(null)}t.setReporter(o.reportProblem??null);t.setUsageReader(o.readUsage??null);const s=[o.endpoint,o.protocol??"anthropic",o.model].join("|");const r=t.getHeldSession();if(r&&r.signature===s){u.current=r.session;r.session.setAvailableActions(A);r.session.setApprovalBypass(_.current);r.session.setReadOnly(E.current);t.setLedger(r.session.ledger);return}const a=ke({endpoint:o.endpoint,protocol:o.protocol??"anthropic",model:o.model,headers:o.headers,apiKey:o.apiKey,maxTokens:o.maxTokens,anthropicVersion:o.anthropicVersion,requestOverrides:o.requestOverrides,hosted:o.hosted===true,...o.hostedFallback?{fallback:o.hostedFallback,onModelSwitch:i=>{const P=(i==="fallback"?o.hostedFallback?.model:o.model)===Ee;t.setMeta({model:i==="fallback"?o.hostedFallback?.model:o.model});if(i==="fallback"){if(!P)t.setClaudeOut(true);t.setNotice(P?"Luna is down right now, so Claude is answering.":"Claude is busy right now, so Luna is answering.")}else{t.setNotice(null)}}}:{},catalog:o.tools?.length?[...D,...o.tools]:void 0,dispatch:N,readSnapshot:Ae,availableActions:A,policy:o.policy,context:o.context,isRelease:S,appName:o.appName,platform:z.OS,requestApproval:i=>t.askApproval(i)});u.current=a;a.setApprovalBypass(_.current);a.setReadOnly(E.current);t.holdSession(a,s);t.setLedger(a.ledger);let d=false;void X().then(i=>{if(i&&!d)void a.prime()});if(R&&!j.current){j.current=true;void Re().then(i=>{if(!i)return;const P=t.hydrate({messages:i.messages,answers:i.answers,usage:i.usage,queued:i.queued});if(!P)return;u.current?.restore(i.history);if(i.ledger?.length)u.current?.ledger.restore(i.ledger);if(i.pending)t.restorePending(i.pending)})}return()=>{d=true}},[O,o,A,re,R]);p(()=>{u.current?.setApprovalBypass(B)},[B]);p(()=>{u.current?.setReadOnly(I)},[I]);const $=n.messages;const H=n.answers;const K=n.usage;const Q=n.busy;const x=n.pending;const V=n.queued;p(()=>{if(!R)return;if($.length===0)return;const e=()=>{void Be(Oe({messages:$,history:u.current?.messages??[],answers:H,usage:K,pending:x,ledger:t.getLedger()?.list(),queued:V}))};if(!Q||x){e();return}const s=setTimeout(e,_e);return()=>clearTimeout(s)},[R,Q,$,H,K,x,V]);const C=l(e=>{if(!v()||!w.current)return;const s=u.current;if(!s)return;t.beginTurn();const r=t.startAssistant();const a=new AbortController;b.current=a;s.setAvailableActions(te());const d=F.current?.onEvent;void(async()=>{try{for await(const i of s.send(e,a.signal)){t.applyEvent(r,i);if(d){try{d(i)}catch{}}}}catch(i){if(!a.signal.aborted){t.applyEvent(r,{type:"error",message:i instanceof Error?i.message:"Something went wrong talking to your AI endpoint."})}}finally{if(a.signal.aborted)t.markStopped(r);t.finishAssistant(r);b.current=null}})()},[]);const m=l(e=>{if(!v()||!w.current)return;const s=e.trim();if(!u.current||!s)return;if(t.getState().busy){t.enqueue(s);return}const r=t.getState().awaitingBlockId;if(r)t.recordAnswer({blockId:r,kind:"skipped"});t.addUser(s);C(s)},[C]);const ne=l(()=>{const{messages:e,busy:s}=t.getState();if(s||!u.current)return;const r=[...e].reverse().find(a=>a.role==="user");if(!r)return;C(r.text)},[C]);const k=l(()=>{t.requestStop();if(t.getState().pending)t.answerApproval(false);b.current?.abort();b.current=null},[]);p(()=>{const e=he(()=>{if(!v())k()});return()=>{e();k()}},[k]);p(()=>{if(!T)k()},[T,k]);p(()=>{if(n.queued.length===0)return;if(Se(n)!=="idle")return;if(ve(n)!==null)return;const e=t.shiftQueued();if(e)m(e.text)},[n,m]);const ie=l(e=>t.takeQueuedToDraft(e),[]);const ae=l(e=>t.removeQueued(e),[]);const ce=l(()=>{b.current?.abort();b.current=null;Te()},[]);const le=l(()=>Ne({dispatch:J,undoFirst:true}),[]);const G=l(async e=>{const s=t.getLedger();if(!s){t.setNotice(`${e.label} couldn't run \u2014 no active Ask Buoy session.`);return}const r=o?.tools?.length?[...D,...o.tools]:void 0;if(e.targetDigest){const d=await we({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:N,catalog:r});if(d!==void 0&&d!==e.targetDigest){t.addNote(`\u201C${e.label}\u201D wasn't applied \u2014 what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`);return}}const a=await Z({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:N,ledger:s,policy:q(),isRelease:S,catalog:r});if(a.ok){t.addNote(`Applied \u201C${e.label}\u201D, which was still waiting when the app restarted. Whatever else that answer was going to do is gone \u2014 ask again if there was more to it.`)}else{t.setNotice(`${e.label} failed \u2014 ${a.error}`)}},[o,q]);const ue=l(e=>{const s=t.getState().pending;const r=typeof e==="boolean"?e:e.approved;t.answerApproval(e);if(!s?.restored)return;if(!r){t.addNote(`Skipped \u201C${s.label}\u201D. Nothing was changed.`);return}void G(s)},[G]);const de=l(e=>{t.recordAnswer(e);const s=Pe(e.blockId);m(s?`${s} \u2014 ${Y(e)}`:Y(e))},[m]);const pe=l(async(e,s)=>{if(!v()||!w.current){t.setNotice("Ask Buoy requires Pro access.");return}if(e.send){m(e.send);return}if(!e.dispatch)return;const r=`${s}:${e.label}`;const a=t.getLedger();if(!a){t.setNotice(`${e.label} failed \u2014 no active Ask Buoy session.`);return}t.setActionState(r,"running");const d=await Z({toolId:e.dispatch.toolId,action:e.dispatch.action,params:e.dispatch.params??{},dispatch:N,ledger:a,policy:q(),isRelease:S,catalog:o?.tools?.length?[...D,...o.tools]:void 0});if(d.ok){t.setActionState(r,"done")}else{t.setActionState(r,"failed");t.setNotice(`${e.label} failed \u2014 ${d.error}`)}},[m,o,q]);return{state:n,send:m,stop:k,reset:ce,undoThenReset:le,approve:ue,retry:ne,answer:de,runAction:pe,editQueued:ie,removeQueued:ae,configured:O,toolCount:Object.keys(A).length,availableToolIds:Object.keys(A)}}
1
+ "use strict";import{getUseIsPro as ye,hasAccountAccess as v,subscribeAccountAccess as Ae}from"@buoy-gg/shared-ui";import{useCallback as l,useEffect as p,useMemo as Y,useRef as h,useState as Z,useSyncExternalStore as be}from"react";import{Platform as ee}from"react-native";import{dispatchToolAction as te,listSnapshotToolIds as ke,listToolActions as we,readToolSnapshot as ve,waitForToolRegistry as oe}from"@buoy-gg/shared-ui";import{answerToText as se,CATALOG as L,createAskBuoySession as Se,readTargetDigest as Te,runGatedAction as re,SNAPSHOT_ACTION as ne}from"@buoy-gg/agent-core";import{askBuoyStore as t,queuePauseReason as Re,turnPhaseOf as Oe}from"./askBuoyStore";import{clearConversation as Ne,resetConversation as Ee}from"./resetConversation";import{afterReload as Be,buildPayload as Ie,readTranscript as _e,RELOAD_NOTE as qe,RESUME_AFTER_RELOAD as Ce,writeTranscript as xe}from"./transcriptPersistence";import{useAskBuoySettings as De}from"./useAskBuoySettings";import{HOSTED_CLAUDE_MODEL as Pe,pickHostedModel as $e}from"../hostedModel";import{useHostedModel as Le}from"./hostedModelSetting";const T=typeof __DEV__!=="undefined"&&!__DEV__;function Me(c){for(const i of t.getState().messages){if(!i.restored)continue;for(const y of i.blocks){const g=y.block;if(g?.id!==c)continue;const m=g.question??g.label??g.title;return typeof m==="string"&&m.trim()?m.trim():void 0}}return void 0}function ie(){try{const c=we();for(const i of ke()){if(!c[i])c[i]=[];if(!c[i].includes(ne)){c[i]=[ne,...c[i]]}}return c}catch{return{}}}const Ue=600;export function useAskBuoyState(){return be(t.subscribe,t.getState,t.getState)}export function useAskBuoy(c){const i=useAskBuoyState();const y=Le();const g=Y(()=>$e(c,y,i.claudeOut),[c,y,i.claudeOut]);const m=h(g);if(i.messages.length===0||!m.current)m.current=g;const o=m.current;const M=h(c);M.current=c;const U=!!c?.hostedModels?.claude;const F=i.messages.length===0;p(()=>{const e=M.current?.readUsage;if(!U||y!=="claude"||!F||!e)return;let s=true;e().then(r=>{if(s&&r.claude)t.setClaudeOut(!r.claude.available)}).catch(()=>{});return()=>{s=false}},[U,y,F]);const A=ye()();const S=h(A);S.current=A;const R=l((...e)=>{if(!v()||!S.current)return Promise.reject(new Error("Ask Buoy requires Pro access."));return te(...e)},[]);const u=h(null);const j=h(false);const[H,K]=Z(false);const b=h(null);const Q=h(o);Q.current=o;const O=!!o?.endpoint&&!!o?.model;const N=o?.persistTranscript!==false;const{bypassApprovals:E,readOnly:B}=De();const x=h(E);x.current=E;const I=h(B);I.current=B;const _=l(()=>I.current?{...o?.policy??{},readOnly:true}:o?.policy??{},[o]);const[D,ae]=Z(0);p(()=>{let e=false;void oe().then(()=>{if(!e)ae(s=>s+1)});return()=>{e=true}},[]);const k=Y(()=>ie(),[D]);const ce=Object.keys(k).sort().join(",");p(()=>{t.setConfigured(O);t.setMeta({model:o?.model,protocol:o?.protocol??(o?"anthropic":void 0),appName:o?.appName,platform:ee.OS,build:T?"release":"development"});if(!O||!o){u.current=null;t.setNotice("Ask Buoy isn't set up in this app yet. A developer needs to point it at your team's AI endpoint.");return}const e=o.protocol??"anthropic";if(T&&o.apiKey){t.setNotice("This build carries a plaintext apiKey \u2014 that's dev-only and it ships to every install. Use the headers callback with your own gateway for distributed builds.")}else if(e==="anthropic"&&/^https?:\/\/api\.openai\.com\//i.test(o.endpoint)){t.setNotice('This endpoint is api.openai.com but the protocol is "anthropic". Set protocol: "openai" in the askBuoy config.')}else if(e==="openai"&&/^https?:\/\/api\.anthropic\.com\//i.test(o.endpoint)){t.setNotice('This endpoint is api.anthropic.com but the protocol is "openai". Remove protocol or set it to "anthropic".')}else{t.setNotice(null)}t.setReporter(o.reportProblem??null);t.setUsageReader(o.readUsage??null);const s=[o.endpoint,o.protocol??"anthropic",o.model].join("|");const r=t.getHeldSession();if(r&&r.signature===s){u.current=r.session;r.session.setAvailableActions(k);r.session.setApprovalBypass(x.current);r.session.setReadOnly(I.current);t.setLedger(r.session.ledger);return}const a=Se({endpoint:o.endpoint,protocol:o.protocol??"anthropic",model:o.model,headers:o.headers,apiKey:o.apiKey,maxTokens:o.maxTokens,anthropicVersion:o.anthropicVersion,requestOverrides:o.requestOverrides,hosted:o.hosted===true,...o.hostedFallback?{fallback:o.hostedFallback,onModelSwitch:n=>{const C=(n==="fallback"?o.hostedFallback?.model:o.model)===Pe;t.setMeta({model:n==="fallback"?o.hostedFallback?.model:o.model});if(n==="fallback"){if(!C)t.setClaudeOut(true);t.setNotice(C?"Luna is down right now, so Claude is answering.":"Claude is busy right now, so Luna is answering.")}else{t.setNotice(null)}}}:{},catalog:o.tools?.length?[...L,...o.tools]:void 0,dispatch:R,readSnapshot:ve,availableActions:k,policy:o.policy,context:o.context,isRelease:T,inApp:true,appName:o.appName,platform:ee.OS,requestApproval:n=>t.askApproval(n)});u.current=a;a.setApprovalBypass(x.current);a.setReadOnly(I.current);t.holdSession(a,s);t.setLedger(a.ledger);let d=false;void oe().then(n=>{if(n&&!d)void a.prime()});if(N&&!j.current){j.current=true;void _e().then(n=>{if(!n)return;const C=t.hydrate({messages:n.messages,answers:n.answers,usage:n.usage,queued:n.queued});if(!C)return;u.current?.restore(n.history);if(n.ledger?.length)u.current?.ledger.restore(n.ledger);if(n.pending)t.restorePending(n.pending);const X=Be(n,Date.now());if(X==="resume")K(true);else if(X==="note")t.addNote(qe)})}return()=>{d=true}},[O,o,k,ce,N]);p(()=>{u.current?.setApprovalBypass(E)},[E]);p(()=>{u.current?.setReadOnly(B)},[B]);const P=i.messages;const V=i.answers;const G=i.usage;const W=i.busy;const $=i.pending;const z=i.queued;p(()=>{if(!N)return;if(P.length===0)return;const e=()=>{void xe(Ie({messages:P,history:u.current?.messages??[],answers:V,usage:G,pending:$,ledger:t.getLedger()?.list(),queued:z}))};if(!W||$){e();return}const s=setTimeout(e,Ue);return()=>clearTimeout(s)},[N,W,P,V,G,$,z]);const q=l(e=>{if(!v()||!S.current)return;const s=u.current;if(!s)return;t.beginTurn();const r=t.startAssistant();const a=new AbortController;b.current=a;s.setAvailableActions(ie());const d=Q.current?.onEvent;void(async()=>{try{for await(const n of s.send(e,a.signal)){t.applyEvent(r,n);if(d){try{d(n)}catch{}}}}catch(n){if(!a.signal.aborted){t.applyEvent(r,{type:"error",message:n instanceof Error?n.message:"Something went wrong talking to your AI endpoint."})}}finally{if(a.signal.aborted)t.markStopped(r);t.finishAssistant(r);b.current=null}})()},[]);const f=l(e=>{if(!v()||!S.current)return;const s=e.trim();if(!u.current||!s)return;if(t.getState().busy){t.enqueue(s);return}const r=t.getState().awaitingBlockId;if(r)t.recordAnswer({blockId:r,kind:"skipped"});t.addUser(s);q(s)},[q]);p(()=>{if(!H||!A||!v()||!u.current||D===0)return;K(false);f(Ce)},[H,A,D,f]);const le=l(()=>{const{messages:e,busy:s}=t.getState();if(s||!u.current)return;const r=[...e].reverse().find(a=>a.role==="user");if(!r)return;q(r.text)},[q]);const w=l(()=>{t.requestStop();if(t.getState().pending)t.answerApproval(false);b.current?.abort();b.current=null},[]);p(()=>{const e=Ae(()=>{if(!v())w()});return()=>{e();w()}},[w]);p(()=>{if(!A)w()},[A,w]);p(()=>{if(i.queued.length===0)return;if(Oe(i)!=="idle")return;if(Re(i)!==null)return;const e=t.shiftQueued();if(e)f(e.text)},[i,f]);const ue=l(e=>t.takeQueuedToDraft(e),[]);const de=l(e=>t.removeQueued(e),[]);const pe=l(()=>{b.current?.abort();b.current=null;Ne()},[]);const fe=l(()=>Ee({dispatch:te,undoFirst:true}),[]);const J=l(async e=>{const s=t.getLedger();if(!s){t.setNotice(`${e.label} couldn't run \u2014 no active Ask Buoy session.`);return}const r=o?.tools?.length?[...L,...o.tools]:void 0;if(e.targetDigest){const d=await Te({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:R,catalog:r});if(d!==void 0&&d!==e.targetDigest){t.addNote(`\u201C${e.label}\u201D wasn't applied \u2014 what it would change has moved on since the app restarted. Nothing was written. Ask again if you still want it.`);return}}const a=await re({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:R,ledger:s,policy:_(),isRelease:T,catalog:r});if(a.ok){t.addNote(`Applied \u201C${e.label}\u201D, which was still waiting when the app restarted. Whatever else that answer was going to do is gone \u2014 ask again if there was more to it.`)}else{t.setNotice(`${e.label} failed \u2014 ${a.error}`)}},[o,_]);const he=l(e=>{const s=t.getState().pending;const r=typeof e==="boolean"?e:e.approved;t.answerApproval(e);if(!s?.restored)return;if(!r){t.addNote(`Skipped \u201C${s.label}\u201D. Nothing was changed.`);return}void J(s)},[J]);const ge=l(e=>{t.recordAnswer(e);const s=Me(e.blockId);f(s?`${s} \u2014 ${se(e)}`:se(e))},[f]);const me=l(async(e,s)=>{if(!v()||!S.current){t.setNotice("Ask Buoy requires Pro access.");return}if(e.send){f(e.send);return}if(!e.dispatch)return;const r=`${s}:${e.label}`;const a=t.getLedger();if(!a){t.setNotice(`${e.label} failed \u2014 no active Ask Buoy session.`);return}t.setActionState(r,"running");const d=await re({toolId:e.dispatch.toolId,action:e.dispatch.action,params:e.dispatch.params??{},dispatch:R,ledger:a,policy:_(),isRelease:T,catalog:o?.tools?.length?[...L,...o.tools]:void 0});if(d.ok){t.setActionState(r,"done")}else{t.setActionState(r,"failed");t.setNotice(`${e.label} failed \u2014 ${d.error}`)}},[f,o,_]);return{state:i,send:f,stop:w,reset:pe,undoThenReset:fe,approve:he,retry:le,answer:ge,runAction:me,editQueued:ue,removeQueued:de,configured:O,toolCount:Object.keys(k).length,availableToolIds:Object.keys(k)}}