@buoy-gg/ask-buoy 7.0.52 → 7.0.54
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.
- package/lib/commonjs/components/AskBuoyModal.js +2 -2
- package/lib/commonjs/components/AskBuoySettings.js +1 -1
- package/lib/commonjs/components/MessageList.js +1 -1
- package/lib/commonjs/hosted.js +1 -0
- package/lib/commonjs/index.js +1 -1
- package/lib/commonjs/store/askBuoySettings.js +1 -1
- package/lib/commonjs/store/askBuoyStore.js +2 -2
- package/lib/commonjs/store/hosted.test.js +1 -0
- package/lib/commonjs/store/useAskBuoy.js +1 -1
- package/lib/module/components/AskBuoyModal.js +2 -2
- package/lib/module/components/AskBuoySettings.js +1 -1
- package/lib/module/components/MessageList.js +1 -1
- package/lib/module/hosted.js +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/store/askBuoySettings.js +1 -1
- package/lib/module/store/askBuoyStore.js +2 -2
- package/lib/module/store/hosted.test.js +1 -0
- package/lib/module/store/useAskBuoy.js +1 -1
- package/lib/typescript/hosted.d.ts +10 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/store/askBuoySettings.d.ts +6 -0
- package/lib/typescript/store/askBuoyStore.d.ts +43 -1
- package/lib/typescript/store/hosted.test.d.ts +2 -0
- package/lib/typescript/sync/askBuoySyncAdapter.d.ts +1 -1
- package/lib/typescript/types.d.ts +15 -2
- package/lib/web/index.mjs +11 -11
- package/package.json +7 -7
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";import{idFactory as p}from"@buoy-gg/agent-core";import{realNow as i}from"@buoy-gg/shared-ui";const l=p("m");const
|
|
1
|
+
"use strict";import{idFactory as p}from"@buoy-gg/agent-core";import{realNow as i}from"@buoy-gg/shared-ui";const l=p("m");const c=400;function y(t){if(!t||Object.keys(t).length===0)return void 0;try{const e=JSON.stringify(t);return e.length>c?`${e.slice(0,c)}\u2026`:e}catch{return void 0}}const k=p("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,meta:{}};let f=null;let m=null;const d=new Set;let h=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){h=t},getLedger(){return h},holdSession(t,e){g={session:t,signature:e}},getHeldSession(){return g},setReporter(t){f=t},getReporter(){return f},setUsageReader(t){m=t},getUsageReader(){return m},setNotice(t){n({notice:t})},setConfigured(t){if(t!==s.configured)n({configured: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:k(),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(b=>b.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})`:`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}`:
|
|
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:y(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==="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}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{beforeEach as a,describe as d,expect as r,it as o}from"vitest";import{HOSTED_ERRORS as i}from"@buoy-gg/agent-core";import{askBuoyStore as e}from"./askBuoyStore";a(()=>e.reset());d("hosted credits and errors",()=>{o("shows credits from the closing frame, and keeps the last number while one is pending",()=>{const t=e.startAssistant();e.applyEvent(t,{type:"meter",frame:{phase:"start",request_id:"rq_1"}});e.applyEvent(t,{type:"meter",frame:{phase:"end",request_id:"rq_1",credits_left:1240,credits_reset:"2026-11-12",charged:1}});r(e.getState().credits).toEqual({left:1240,resetAt:"2026-11-12",pending:false});e.applyEvent(t,{type:"meter",frame:{phase:"end",request_id:"rq_2",credits_left:null,credits_reset:null,charged:null,pending:true}});r(e.getState().credits).toEqual({left:1240,resetAt:"2026-11-12",pending:true})});o("keeps a hosted error's code and request id on the message",()=>{const t=e.startAssistant();e.applyEvent(t,{type:"error",message:"You used this month's credits. They come back on Nov 12.",hosted:{code:"credits_out",message:"x",request_id:"rq_9",action:i.credits_out.action,retry_after_ms:null,reset_at:"2026-11-12T00:00:00Z",help_url:null}});const s=e.getState().messages.find(n=>n.id===t);r(s.error).toMatch(/credits/);r(s.errorInfo).toEqual({code:"credits_out",requestId:"rq_9",action:"none",resetAt:"2026-11-12T00:00:00Z",helpUrl:null})});o("an error from your own endpoint has no hosted info",()=>{const t=e.startAssistant();e.applyEvent(t,{type:"error",message:"HTTP 500"});r(e.getState().messages.find(s=>s.id===t).errorInfo).toBeUndefined()})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{getUseIsPro as ne,hasAccountAccess as k,subscribeAccountAccess as ae}from"@buoy-gg/shared-ui";import{useCallback as c,useEffect as
|
|
1
|
+
"use strict";import{getUseIsPro as ne,hasAccountAccess as k,subscribeAccountAccess as ae}from"@buoy-gg/shared-ui";import{useCallback as c,useEffect as h,useMemo as ie,useRef as m,useState as ce,useSyncExternalStore as le}from"react";import{Platform as L}from"react-native";import{dispatchToolAction as K,listSnapshotToolIds as ue,listToolActions as de,readToolSnapshot as pe,waitForToolRegistry as Q}from"@buoy-gg/shared-ui";import{answerToText as V,CATALOG as E,createAskBuoySession as he,readTargetDigest as fe,runGatedAction as M,SNAPSHOT_ACTION as F}from"@buoy-gg/agent-core";import{askBuoyStore as t,queuePauseReason as me,turnPhaseOf as ye}from"./askBuoyStore";import{clearConversation as Ae,resetConversation as be}from"./resetConversation";import{buildPayload as ge,readTranscript as ke,writeTranscript as we}from"./transcriptPersistence";import{useAskBuoySettings as ve}from"./useAskBuoySettings";const w=typeof __DEV__!=="undefined"&&!__DEV__;function Se(s){for(const n of t.getState().messages){if(!n.restored)continue;for(const y of n.blocks){const d=y.block;if(d?.id!==s)continue;const p=d.question??d.label??d.title;return typeof p==="string"&&p.trim()?p.trim():void 0}}return void 0}function G(){try{const s=de();for(const n of ue()){if(!s[n])s[n]=[];if(!s[n].includes(F)){s[n]=[F,...s[n]]}}return s}catch{return{}}}const Te=600;export function useAskBuoyState(){return le(t.subscribe,t.getState,t.getState)}export function useAskBuoy(s){const n=useAskBuoyState();const y=ne()();const d=m(y);d.current=y;const p=c((...e)=>{if(!k()||!d.current)return Promise.reject(new Error("Ask Buoy requires Pro access."));return K(...e)},[]);const l=m(null);const P=m(false);const A=m(null);const $=m(s);$.current=s;const v=!!s?.endpoint&&!!s?.model;const S=s?.persistTranscript!==false;const{bypassApprovals:T,readOnly:N}=ve();const I=m(T);I.current=T;const R=m(N);R.current=N;const O=c(()=>R.current?{...s?.policy??{},readOnly:true}:s?.policy??{},[s]);const[H,W]=ce(0);h(()=>{let e=false;void Q().then(()=>{if(!e)W(r=>r+1)});return()=>{e=true}},[]);const b=ie(()=>G(),[H]);const z=Object.keys(b).sort().join(",");h(()=>{t.setConfigured(v);t.setMeta({model:s?.model,protocol:s?.protocol??(s?"anthropic":void 0),appName:s?.appName,platform:L.OS,build:w?"release":"development"});if(!v||!s){l.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=s.protocol??"anthropic";if(w&&s.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(s.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(s.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(s.reportProblem??null);t.setUsageReader(s.readUsage??null);const r=[s.endpoint,s.protocol??"anthropic",s.model].join("|");const o=t.getHeldSession();if(o&&o.signature===r){l.current=o.session;o.session.setAvailableActions(b);o.session.setApprovalBypass(I.current);o.session.setReadOnly(R.current);t.setLedger(o.session.ledger);return}const i=he({endpoint:s.endpoint,protocol:s.protocol??"anthropic",model:s.model,headers:s.headers,apiKey:s.apiKey,maxTokens:s.maxTokens,anthropicVersion:s.anthropicVersion,requestOverrides:s.requestOverrides,hosted:s.hosted===true,catalog:s.tools?.length?[...E,...s.tools]:void 0,dispatch:p,readSnapshot:pe,availableActions:b,policy:s.policy,context:s.context,isRelease:w,appName:s.appName,platform:L.OS,requestApproval:a=>t.askApproval(a)});l.current=i;i.setApprovalBypass(I.current);i.setReadOnly(R.current);t.holdSession(i,r);t.setLedger(i.ledger);let u=false;void Q().then(a=>{if(a&&!u)void i.prime()});if(S&&!P.current){P.current=true;void ke().then(a=>{if(!a)return;const oe=t.hydrate({messages:a.messages,answers:a.answers,usage:a.usage,queued:a.queued});if(!oe)return;l.current?.restore(a.history);if(a.ledger?.length)l.current?.ledger.restore(a.ledger);if(a.pending)t.restorePending(a.pending)})}return()=>{u=true}},[v,s,b,z,S]);h(()=>{l.current?.setApprovalBypass(T)},[T]);h(()=>{l.current?.setReadOnly(N)},[N]);const q=n.messages;const x=n.answers;const _=n.usage;const C=n.busy;const D=n.pending;const U=n.queued;h(()=>{if(!S)return;if(q.length===0)return;const e=()=>{void we(ge({messages:q,history:l.current?.messages??[],answers:x,usage:_,pending:D,ledger:t.getLedger()?.list(),queued:U}))};if(!C){e();return}const r=setTimeout(e,Te);return()=>clearTimeout(r)},[S,C,q,x,_,D,U]);const B=c(e=>{if(!k()||!d.current)return;const r=l.current;if(!r)return;t.beginTurn();const o=t.startAssistant();const i=new AbortController;A.current=i;r.setAvailableActions(G());const u=$.current?.onEvent;void(async()=>{try{for await(const a of r.send(e,i.signal)){t.applyEvent(o,a);if(u){try{u(a)}catch{}}}}catch(a){if(!i.signal.aborted){t.applyEvent(o,{type:"error",message:a instanceof Error?a.message:"Something went wrong talking to your AI endpoint."})}}finally{if(i.signal.aborted)t.markStopped(o);t.finishAssistant(o);A.current=null}})()},[]);const f=c(e=>{if(!k()||!d.current)return;const r=e.trim();if(!l.current||!r)return;if(t.getState().busy){t.enqueue(r);return}const o=t.getState().awaitingBlockId;if(o)t.recordAnswer({blockId:o,kind:"skipped"});t.addUser(r);B(r)},[B]);const J=c(()=>{const{messages:e,busy:r}=t.getState();if(r||!l.current)return;const o=[...e].reverse().find(i=>i.role==="user");if(!o)return;B(o.text)},[B]);const g=c(()=>{t.requestStop();if(t.getState().pending)t.answerApproval(false);A.current?.abort();A.current=null},[]);h(()=>{const e=ae(()=>{if(!k())g()});return()=>{e();g()}},[g]);h(()=>{if(!y)g()},[y,g]);h(()=>{if(n.queued.length===0)return;if(ye(n)!=="idle")return;if(me(n)!==null)return;const e=t.shiftQueued();if(e)f(e.text)},[n,f]);const X=c(e=>t.takeQueuedToDraft(e),[]);const Y=c(e=>t.removeQueued(e),[]);const Z=c(()=>{A.current?.abort();A.current=null;Ae()},[]);const ee=c(()=>be({dispatch:K,undoFirst:true}),[]);const j=c(async e=>{const r=t.getLedger();if(!r){t.setNotice(`${e.label} couldn't run \u2014 no active Ask Buoy session.`);return}const o=s?.tools?.length?[...E,...s.tools]:void 0;if(e.targetDigest){const u=await fe({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:p,catalog:o});if(u!==void 0&&u!==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 i=await M({toolId:e.toolId,action:e.action,params:e.params??{},dispatch:p,ledger:r,policy:O(),isRelease:w,catalog:o});if(i.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 ${i.error}`)}},[s,O]);const te=c(e=>{const r=t.getState().pending;const o=typeof e==="boolean"?e:e.approved;t.answerApproval(e);if(!r?.restored)return;if(!o){t.addNote(`Skipped \u201C${r.label}\u201D. Nothing was changed.`);return}void j(r)},[j]);const se=c(e=>{t.recordAnswer(e);const r=Se(e.blockId);f(r?`${r} \u2014 ${V(e)}`:V(e))},[f]);const re=c(async(e,r)=>{if(!k()||!d.current){t.setNotice("Ask Buoy requires Pro access.");return}if(e.send){f(e.send);return}if(!e.dispatch)return;const o=`${r}:${e.label}`;const i=t.getLedger();if(!i){t.setNotice(`${e.label} failed \u2014 no active Ask Buoy session.`);return}t.setActionState(o,"running");const u=await M({toolId:e.dispatch.toolId,action:e.dispatch.action,params:e.dispatch.params??{},dispatch:p,ledger:i,policy:O(),isRelease:w,catalog:s?.tools?.length?[...E,...s.tools]:void 0});if(u.ok){t.setActionState(o,"done")}else{t.setActionState(o,"failed");t.setNotice(`${e.label} failed \u2014 ${u.error}`)}},[f,s,O]);return{state:n,send:f,stop:g,reset:Z,undoThenReset:ee,approve:te,retry:J,answer:se,runAction:re,editQueued:X,removeQueued:Y,configured:v,toolCount:Object.keys(b).length,availableToolIds:Object.keys(b)}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AskBuoyConfig } from "./types";
|
|
2
|
+
export declare const HOSTED_ENDPOINT = "https://ai.buoy.gg/v1/chat/completions";
|
|
3
|
+
/** The model the gateway serves. The gateway pins it; this is only a label. */
|
|
4
|
+
export declare const HOSTED_MODEL = "openai/gpt-6-luna";
|
|
5
|
+
export type HostedAskBuoyOptions = Omit<AskBuoyConfig, "endpoint" | "protocol" | "model" | "headers" | "apiKey" | "requestOverrides" | "anthropicVersion" | "reportProblem" | "readUsage"> & {
|
|
6
|
+
/** For local testing against `wrangler dev`. Defaults to ai.buoy.gg. */
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function hostedAskBuoy(options?: HostedAskBuoyOptions): AskBuoyConfig;
|
|
10
|
+
//# sourceMappingURL=hosted.d.ts.map
|
|
@@ -13,6 +13,7 @@ export { askBuoySyncAdapter, ASK_BUOY_ADAPTER_VERSION } from "./sync/askBuoySync
|
|
|
13
13
|
export { askBuoyStore } from "./store/askBuoyStore";
|
|
14
14
|
export { useAskBuoy, useAskBuoyState, type AskBuoyController } from "./store/useAskBuoy";
|
|
15
15
|
export type { AskBuoyConfig } from "./types";
|
|
16
|
+
export { hostedAskBuoy, HOSTED_ENDPOINT, HOSTED_MODEL, type HostedAskBuoyOptions } from "./hosted";
|
|
16
17
|
export type { ChatMessage, ChatToolCall, AskBuoyState } from "./store/askBuoyStore";
|
|
17
18
|
export { redactForWire } from "./sync/wire";
|
|
18
19
|
export type { PlacedBlock } from "./store/askBuoyStore";
|
|
@@ -32,6 +32,12 @@ export interface AskBuoySettings {
|
|
|
32
32
|
* others, which on a shared device means: check the subtitle.
|
|
33
33
|
*/
|
|
34
34
|
readOnly: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The person read the one-time hosted notice: with hosted Ask Buoy, their
|
|
37
|
+
* question and the app data needed to answer it go to OpenAI through
|
|
38
|
+
* OpenRouter. Hosted asks wait until this is true.
|
|
39
|
+
*/
|
|
40
|
+
hostedNoticeSeen: boolean;
|
|
35
41
|
}
|
|
36
42
|
export declare const DEFAULT_SETTINGS: AskBuoySettings;
|
|
37
43
|
export declare const askBuoySettings: {
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
import type { ApprovalAnswer, Block, BlockAnswer, AskBuoySession, EffectLedger, TurnEvent } from "@buoy-gg/agent-core";
|
|
1
|
+
import type { ApprovalAnswer, Block, BlockAnswer, AskBuoySession, EffectLedger, HostedErrorAction, TurnEvent } from "@buoy-gg/agent-core";
|
|
2
|
+
/** The hosted gateway's error, kept on the message so the card can show its code and id. */
|
|
3
|
+
export interface HostedErrorInfo {
|
|
4
|
+
code: string;
|
|
5
|
+
requestId: string | null;
|
|
6
|
+
action: HostedErrorAction;
|
|
7
|
+
resetAt: string | null;
|
|
8
|
+
helpUrl: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** Hosted credits this month, from the gateway's /v1/usage. */
|
|
11
|
+
export interface HostedUsage {
|
|
12
|
+
creditsLeft: number;
|
|
13
|
+
grantCredits: number;
|
|
14
|
+
spentCredits: number;
|
|
15
|
+
resetAt: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Reads hosted credits. Set by `hostedAskBuoy()`; absent for your own endpoint. */
|
|
18
|
+
export type ReadUsage = () => Promise<HostedUsage>;
|
|
19
|
+
/** What "Report problem" sends. Set by `hostedAskBuoy()`; absent for your own endpoint. */
|
|
20
|
+
export type ReportProblem = (input: {
|
|
21
|
+
requestId: string | null;
|
|
22
|
+
code: string;
|
|
23
|
+
note: string;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
reportId: string;
|
|
26
|
+
}>;
|
|
2
27
|
/** A block in a message, and (for receipts) the tool call that produced it. */
|
|
3
28
|
export interface PlacedBlock {
|
|
4
29
|
block: Block;
|
|
@@ -90,6 +115,8 @@ export interface ChatMessage {
|
|
|
90
115
|
*/
|
|
91
116
|
waiting?: string;
|
|
92
117
|
error?: string;
|
|
118
|
+
/** Set when the error came from the hosted gateway. */
|
|
119
|
+
errorInfo?: HostedErrorInfo;
|
|
93
120
|
/**
|
|
94
121
|
* The user pressed Stop.
|
|
95
122
|
*
|
|
@@ -212,6 +239,15 @@ export interface AskBuoyState {
|
|
|
212
239
|
output: number;
|
|
213
240
|
requests: number;
|
|
214
241
|
};
|
|
242
|
+
/**
|
|
243
|
+
* Hosted Ask Buoy credits, from the gateway's closing frame. Null until a
|
|
244
|
+
* hosted answer arrives, and always null for your own endpoint.
|
|
245
|
+
*/
|
|
246
|
+
credits: {
|
|
247
|
+
left: number | null;
|
|
248
|
+
resetAt: string | null;
|
|
249
|
+
pending: boolean;
|
|
250
|
+
} | null;
|
|
215
251
|
/**
|
|
216
252
|
* Which model, app and build this conversation happened against.
|
|
217
253
|
*
|
|
@@ -239,6 +275,12 @@ export declare const askBuoyStore: {
|
|
|
239
275
|
session: AskBuoySession;
|
|
240
276
|
signature: string;
|
|
241
277
|
} | null;
|
|
278
|
+
/** "Report problem" on hosted error cards. Null hides the button. */
|
|
279
|
+
setReporter(next: ReportProblem | null): void;
|
|
280
|
+
getReporter(): ReportProblem | null;
|
|
281
|
+
/** Hosted credits for the settings screen. Null hides that section. */
|
|
282
|
+
setUsageReader(next: ReadUsage | null): void;
|
|
283
|
+
getUsageReader(): ReadUsage | null;
|
|
242
284
|
setNotice(notice: string | null): void;
|
|
243
285
|
setConfigured(configured: boolean): void;
|
|
244
286
|
setMeta(meta: SessionMeta): void;
|
|
@@ -17,7 +17,7 @@ export declare const askBuoySyncAdapter: {
|
|
|
17
17
|
toolCalls: {
|
|
18
18
|
toolId: string;
|
|
19
19
|
action: string;
|
|
20
|
-
status: "running" | "
|
|
20
|
+
status: "running" | "ok" | "failed" | "refused" | "declined" | "stopped";
|
|
21
21
|
}[];
|
|
22
22
|
}[];
|
|
23
23
|
usage: {
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* pasted into app config is inlined into the JS bundle in plaintext, which is
|
|
13
13
|
* OWASP Mobile's #1 risk and not something to make convenient.
|
|
14
14
|
*/
|
|
15
|
-
import type { ContextPack, AskBuoyPolicy, ToolDescriptor, TurnEvent } from "@buoy-gg/agent-core";
|
|
15
|
+
import type { ContextPack, AskBuoyPolicy, RequestMeta, ToolDescriptor, TurnEvent } from "@buoy-gg/agent-core";
|
|
16
|
+
import type { ReadUsage, ReportProblem } from "./store/askBuoyStore";
|
|
16
17
|
export interface AskBuoyConfig {
|
|
17
18
|
/**
|
|
18
19
|
* The company's own LLM gateway. Buoy POSTs the conversation here and never
|
|
@@ -29,7 +30,7 @@ export interface AskBuoyConfig {
|
|
|
29
30
|
* the app's credential, the company's gateway, Buoy in the middle holding
|
|
30
31
|
* nothing.
|
|
31
32
|
*/
|
|
32
|
-
headers?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
33
|
+
headers?: (meta?: RequestMeta) => Record<string, string> | Promise<Record<string, string>>;
|
|
33
34
|
/**
|
|
34
35
|
* A provider key, sent straight to the provider.
|
|
35
36
|
*
|
|
@@ -138,5 +139,17 @@ export interface AskBuoyConfig {
|
|
|
138
139
|
* }]
|
|
139
140
|
*/
|
|
140
141
|
tools?: ToolDescriptor[];
|
|
142
|
+
/**
|
|
143
|
+
* What the error card's "Report problem" calls. `hostedAskBuoy()` sets it;
|
|
144
|
+
* leave it out with your own endpoint and the button is not shown.
|
|
145
|
+
*/
|
|
146
|
+
reportProblem?: ReportProblem;
|
|
147
|
+
/** Hosted credits for Ask Buoy's settings screen. `hostedAskBuoy()` sets it. */
|
|
148
|
+
readUsage?: ReadUsage;
|
|
149
|
+
/**
|
|
150
|
+
* The endpoint is Buoy's hosted gateway. Set by `hostedAskBuoy()`; only then
|
|
151
|
+
* are the gateway's error codes and credit frames trusted.
|
|
152
|
+
*/
|
|
153
|
+
hosted?: boolean;
|
|
141
154
|
}
|
|
142
155
|
//# sourceMappingURL=types.d.ts.map
|