@soba-so/sdk 0.0.11 → 0.0.12

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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { type AttachParams, DEFAULT_BASE_URL, DEFAULT_WEB_BASE_URL, type DetachedRunParams, type OpKind, type OpParams, type OpResult, type RunParams, Soba, type SobaOptions, type StartedRun, type TerminalBooking, type TerminalParams, } from "./client.js";
2
2
  export { SobaError, type SobaErrorCode } from "./errors.js";
3
- export { type AgentEvent, type AgentEventType, type CostClass, type GitSnapshot, isCostClass, isMetered, isTerminal, METERED_CLASSES, type Message, type PermissionMode, type RouteRequest, type RunPolicy, type RunUsage, TOOL_SLUGS, type ToolSchema, type ToolSlug, } from "./protocol.js";
3
+ export { type AgentEvent, type AgentEventType, type CostClass, type GitSnapshot, type ImageInput, isCostClass, isMetered, isTerminal, METERED_CLASSES, type Message, type PermissionMode, type RouteRequest, type RunPolicy, type RunUsage, TOOL_SLUGS, type ToolSchema, type ToolSlug, } from "./protocol.js";
4
4
  export type { Allowance, ConnectContextResponse, ConnectedMachine, CurrentPlanItem, FeatureCheckResult, FeatureDenial, MachineListItem, MachineListState, MachineRuntime, PlanFeatureWire, PlanListItem, PlanListResponse, PlanSummary, QuantityResult, RuntimeListItem, SobaUser, TrackResult, UsagePage, UsageQuery, UsageRow, } from "./resources.js";
5
5
  export { type ApprovalDecision, type ApprovalRequest, type Approver, Run, } from "./run.js";
6
6
  export { type ToolContext, type ToolDefinition, tool } from "./tools.js";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- var a=class extends Error{code;type;status;retryable;retryAfterMs;runId;constructor(e){super(e.message,e.cause===void 0?void 0:{cause:e.cause}),this.name="SobaError",this.code=e.code,this.type=e.type??e.code,this.status=e.status,this.retryable=e.retryable??B.has(e.code),this.retryAfterMs=e.retryAfterMs,this.runId=e.runId}},B=new Set(["rate_limited","server_error","connection","timeout","stream_truncated"]),G={400:"invalid_request",401:"unauthorized",402:"payment_required",403:"forbidden",404:"not_found",409:"no_compute",422:"user_required",429:"rate_limited"};function K(n){let e=G[n];return e||(n>=500?"server_error":n>=400?"invalid_request":"unknown")}async function q(n){let e=n.status,r="";try{r=await n.text()}catch{}let t=H(r);return new a({code:t?.code??K(e),type:t?.type,message:t?.message??z(e,n.statusText,r),status:e,retryAfterMs:Q(n.headers.get("retry-after")),runId:n.headers.get("soba-run-id")??void 0})}function H(n){if(!n)return null;let e;try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object")return null;let r=e.error;if(!r||typeof r!="object")return null;let t=r;return{message:typeof t.message=="string"&&t.message?t.message:void 0,type:typeof t.type=="string"&&t.type?t.type:void 0,code:J.has(t.code)?t.code:void 0}}var J=new Set(["invalid_request","user_required","unauthorized","payment_required","forbidden","not_found","no_compute","unknown_run","not_resumable","machine_gone","rate_limited","server_error","connection","timeout","stream_truncated","run_failed","canceled","unknown"]);function z(n,e,r){let t=r.trim().slice(0,200),o=t?`: ${t}`:"";return`Soba returned ${n}${e?` ${e}`:""}${o}`}function Q(n){if(!n)return;let e=Number(n);if(Number.isFinite(e)&&e>=0)return Math.round(e*1e3);let r=Date.parse(n);if(!Number.isNaN(r))return Math.max(0,r-Date.now())}function w(n){if(n instanceof a)return n;let e=n;return e?.name==="AbortError"||e?.name==="TimeoutError"?new a({code:"canceled",message:e.message||"The request was aborted.",retryable:!1,cause:n}):new a({code:"connection",message:e?.message?`Could not reach Soba: ${e.message}`:"Could not reach Soba.",cause:n})}var E="0.0.11";var Y=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN"]);function V(n){if(n.code==="rate_limited")return!0;if(n.code!=="connection")return!1;let e=n.cause;for(let r=0;e&&r<4;r++){let t=e.code;if(typeof t=="string"&&Y.has(t))return!0;e=e.cause}return!1}var X=8e3,Z=3e4;async function b(n,e){let r=ne(n.baseURL,e.path,e.query),t=e.retry===!1?1:Math.max(0,n.maxRetries)+1,o,s=i=>i.retryable&&(e.retry!=="unsent"||V(i));for(let i=0;i<t;i++){i>0&&await oe(re(i,o),e.signal);let u=new AbortController,d=A(e.signal,u),h=setTimeout(()=>{u.abort(new a({code:"timeout",message:`Soba did not answer within ${n.connectTimeoutMs}ms.`}))},n.connectTimeoutMs),l;try{l=await n.fetch(r,{method:e.method,headers:ee(n,e),body:e.body===void 0?void 0:JSON.stringify(e.body),signal:u.signal})}catch(p){if(d(),o=w(u.signal.aborted?u.signal.reason??p:p),!s(o)||i===t-1)throw o;continue}finally{clearTimeout(h)}if(l.ok)return l;if(d(),o=await q(l),!s(o)||i===t-1)throw o}throw o??new a({code:"unknown",message:"The request failed with no error."})}async function R(n,e){let r=await b(n,{accept:"application/json",...e}),t=await r.text();if(t)try{return JSON.parse(t)}catch(o){throw new a({code:"server_error",message:`Soba returned a body that is not JSON: ${t.slice(0,200)}`,status:r.status,cause:o})}}function ee(n,e){let r={authorization:`Bearer ${n.apiKey}`,accept:e.accept??"application/json","user-agent":te(),...n.headers};return e.body!==void 0&&(r["content-type"]="application/json"),r}function te(){let n=typeof process>"u"?"":` node/${process.versions?.node??"?"}`;return`soba-sdk/${E}${n}`}function ne(n,e,r){let t=n.replace(/\/+$/,""),o=new URL(`${t}${e.startsWith("/")?e:`/${e}`}`);for(let[s,i]of Object.entries(r??{}))i!==void 0&&o.searchParams.set(s,String(i));return o.toString()}function re(n,e){if(e?.retryAfterMs!==void 0)return Math.min(e.retryAfterMs,Z);let r=Math.min(X,500*2**(n-1));return Math.round(r*(.5+Math.random()/2))}function oe(n,e){return new Promise((r,t)=>{if(e?.aborted)return t(w(e.reason));let o=setTimeout(()=>{e?.removeEventListener("abort",s),r()},n);function s(){clearTimeout(o),t(w(e?.reason))}e?.addEventListener("abort",s,{once:!0})})}function A(n,e){if(!n)return()=>{};if(n.aborted)return e.abort(n.reason),()=>{};let r=()=>e.abort(n.reason);return n.addEventListener("abort",r,{once:!0}),()=>n.removeEventListener("abort",r)}var M=["open-weights","frontier"];function se(n){return M.includes(n)}function ie(n){return n==="user-hardware"||n==="user-subscription"||n==="user-api-key"||n==="open-weights"||n==="frontier"}function _(n){return n.type==="done"||n.type==="error"}var ae=["search","fetch","read","write","edit","bash","files","agent","todo","think","sources","tool"];var N=new Set(["delta","thinking","status","done","error"]);function C(n){let e;try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object"||Array.isArray(e))return null;let r=e,t=c(r.runId);if(typeof r.type=="string"&&N.has(r.type)){let o=O(r);return o?{kind:"event",runId:t,event:o}:null}switch(r.type){case"event":{if(!r.event||typeof r.event!="object")return null;let o=O(r.event);return o?{kind:"event",runId:t,event:o}:null}case"usage":{let o=$(r.usage);return o?{kind:"usage",runId:t,usage:o}:null}case"tool_call":{let o=c(r.callId),s=c(r.name);return!o||!s?null:{kind:"tool_call",runId:t,callId:o,name:s,input:r.input,...r.answered===!0?{answered:!0}:{}}}case"approval_request":{let o=c(r.approvalId),s=c(r.tool);return!o||!s?null:{kind:"approval",runId:t,approvalId:o,tool:s,input:r.input,cwd:c(r.cwd),...r.answered===!0?{answered:!0}:{}}}default:return null}}function O(n){let e=typeof n.type=="string"&&N.has(n.type)?n.type:null;if(!e)return null;let r={type:e},t=c(n.text);t!==void 0&&(r.text=t);let o=c(n.message);o!==void 0&&(r.message=o);let s=c(n.tool);s!==void 0&&(r.tool=s);let i=c(n.tier);i!==void 0&&(r.tier=i);let u=$(n.usage);u&&(r.usage=u);let d=c(n.reason);d!==void 0&&(r.reason=d),typeof n.retryable=="boolean"&&(r.retryable=n.retryable);let h=n.turn&&typeof n.turn=="object"?c(n.turn.text):void 0;h!==void 0&&(r.turn={text:h});let l=c(n.session);l!==void 0&&(r.session=l);let p=c(n.toolInput);p!==void 0&&(r.toolInput=p);let m=ue(n.git);return m&&(r.git=m),(n.state==="interrupted"||n.state==="resumed")&&(r.state=n.state),r}function ue(n){if(!n||typeof n!="object")return null;let e=n,r=c(e.branch);return!r||!Array.isArray(e.files)||!Array.isArray(e.commits)?null:{branch:r,base:c(e.base)??"",head:c(e.head)??"",ahead:y(e.ahead),behind:y(e.behind),files:e.files.filter(t=>!!t&&typeof t=="object"&&typeof t.path=="string").map(t=>({path:t.path,status:t.status==="A"||t.status==="D"||t.status==="R"?t.status:"M",added:y(t.added),removed:y(t.removed)})),commits:e.commits.filter(t=>!!t&&typeof t=="object").map(t=>({sha:c(t.sha)??"",subject:c(t.subject)??""})),dirty:e.dirty===!0}}function $(n){if(!n||typeof n!="object")return null;let e=n,r={model:c(e.model)??"unknown",inputTokens:y(e.inputTokens),outputTokens:y(e.outputTokens),cacheReadTokens:y(e.cacheReadTokens),cacheWriteTokens:y(e.cacheWriteTokens),webSearches:y(e.webSearches)},t=c(e.costClass);t!==void 0&&(r.costClass=t);let o=c(e.provider);return o!==void 0&&(r.provider=o),typeof e.costMicros=="number"&&Number.isFinite(e.costMicros)&&(r.costMicros=e.costMicros),r}function c(n){return typeof n=="string"&&n.length>0?n:void 0}function y(n){return typeof n=="number"&&Number.isFinite(n)?n:0}async function*F(n){let e=n.body;if(!e)throw new a({code:"connection",message:"The run response arrived with no body to stream.",status:n.status});let r=n.headers.get("content-type")??"",t=r.includes("ndjson")||r.includes("jsonlines"),o=e.getReader(),s=new TextDecoder,i="",u=[],d,h=l=>d===void 0?l:{...l,seq:d};try{for(;;){let p=await o.read();if(p.done)break;for(i+=s.decode(p.value,{stream:!0});;){let m=i.indexOf(`
1
+ var a=class extends Error{code;type;status;retryable;retryAfterMs;runId;constructor(e){super(e.message,e.cause===void 0?void 0:{cause:e.cause}),this.name="SobaError",this.code=e.code,this.type=e.type??e.code,this.status=e.status,this.retryable=e.retryable??B.has(e.code),this.retryAfterMs=e.retryAfterMs,this.runId=e.runId}},B=new Set(["rate_limited","server_error","connection","timeout","stream_truncated"]),G={400:"invalid_request",401:"unauthorized",402:"payment_required",403:"forbidden",404:"not_found",409:"no_compute",422:"user_required",429:"rate_limited"};function K(n){let e=G[n];return e||(n>=500?"server_error":n>=400?"invalid_request":"unknown")}async function q(n){let e=n.status,r="";try{r=await n.text()}catch{}let t=H(r);return new a({code:t?.code??K(e),type:t?.type,message:t?.message??z(e,n.statusText,r),status:e,retryAfterMs:Q(n.headers.get("retry-after")),runId:n.headers.get("soba-run-id")??void 0})}function H(n){if(!n)return null;let e;try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object")return null;let r=e.error;if(!r||typeof r!="object")return null;let t=r;return{message:typeof t.message=="string"&&t.message?t.message:void 0,type:typeof t.type=="string"&&t.type?t.type:void 0,code:J.has(t.code)?t.code:void 0}}var J=new Set(["invalid_request","user_required","unauthorized","payment_required","forbidden","not_found","no_compute","unknown_run","not_resumable","machine_gone","rate_limited","server_error","connection","timeout","stream_truncated","run_failed","canceled","unknown"]);function z(n,e,r){let t=r.trim().slice(0,200),o=t?`: ${t}`:"";return`Soba returned ${n}${e?` ${e}`:""}${o}`}function Q(n){if(!n)return;let e=Number(n);if(Number.isFinite(e)&&e>=0)return Math.round(e*1e3);let r=Date.parse(n);if(!Number.isNaN(r))return Math.max(0,r-Date.now())}function w(n){if(n instanceof a)return n;let e=n;return e?.name==="AbortError"||e?.name==="TimeoutError"?new a({code:"canceled",message:e.message||"The request was aborted.",retryable:!1,cause:n}):new a({code:"connection",message:e?.message?`Could not reach Soba: ${e.message}`:"Could not reach Soba.",cause:n})}var E="0.0.12";var Y=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN"]);function V(n){if(n.code==="rate_limited")return!0;if(n.code!=="connection")return!1;let e=n.cause;for(let r=0;e&&r<4;r++){let t=e.code;if(typeof t=="string"&&Y.has(t))return!0;e=e.cause}return!1}var X=8e3,Z=3e4;async function b(n,e){let r=ne(n.baseURL,e.path,e.query),t=e.retry===!1?1:Math.max(0,n.maxRetries)+1,o,s=i=>i.retryable&&(e.retry!=="unsent"||V(i));for(let i=0;i<t;i++){i>0&&await oe(re(i,o),e.signal);let u=new AbortController,d=A(e.signal,u),h=setTimeout(()=>{u.abort(new a({code:"timeout",message:`Soba did not answer within ${n.connectTimeoutMs}ms.`}))},n.connectTimeoutMs),l;try{l=await n.fetch(r,{method:e.method,headers:ee(n,e),body:e.body===void 0?void 0:JSON.stringify(e.body),signal:u.signal})}catch(p){if(d(),o=w(u.signal.aborted?u.signal.reason??p:p),!s(o)||i===t-1)throw o;continue}finally{clearTimeout(h)}if(l.ok)return l;if(d(),o=await q(l),!s(o)||i===t-1)throw o}throw o??new a({code:"unknown",message:"The request failed with no error."})}async function R(n,e){let r=await b(n,{accept:"application/json",...e}),t=await r.text();if(t)try{return JSON.parse(t)}catch(o){throw new a({code:"server_error",message:`Soba returned a body that is not JSON: ${t.slice(0,200)}`,status:r.status,cause:o})}}function ee(n,e){let r={authorization:`Bearer ${n.apiKey}`,accept:e.accept??"application/json","user-agent":te(),...n.headers};return e.body!==void 0&&(r["content-type"]="application/json"),r}function te(){let n=typeof process>"u"?"":` node/${process.versions?.node??"?"}`;return`soba-sdk/${E}${n}`}function ne(n,e,r){let t=n.replace(/\/+$/,""),o=new URL(`${t}${e.startsWith("/")?e:`/${e}`}`);for(let[s,i]of Object.entries(r??{}))i!==void 0&&o.searchParams.set(s,String(i));return o.toString()}function re(n,e){if(e?.retryAfterMs!==void 0)return Math.min(e.retryAfterMs,Z);let r=Math.min(X,500*2**(n-1));return Math.round(r*(.5+Math.random()/2))}function oe(n,e){return new Promise((r,t)=>{if(e?.aborted)return t(w(e.reason));let o=setTimeout(()=>{e?.removeEventListener("abort",s),r()},n);function s(){clearTimeout(o),t(w(e?.reason))}e?.addEventListener("abort",s,{once:!0})})}function A(n,e){if(!n)return()=>{};if(n.aborted)return e.abort(n.reason),()=>{};let r=()=>e.abort(n.reason);return n.addEventListener("abort",r,{once:!0}),()=>n.removeEventListener("abort",r)}var M=["open-weights","frontier"];function se(n){return M.includes(n)}function ie(n){return n==="user-hardware"||n==="user-subscription"||n==="user-api-key"||n==="open-weights"||n==="frontier"}function _(n){return n.type==="done"||n.type==="error"}var ae=["search","fetch","read","write","edit","bash","files","agent","todo","think","sources","tool"];var N=new Set(["delta","thinking","status","done","error"]);function C(n){let e;try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object"||Array.isArray(e))return null;let r=e,t=c(r.runId);if(typeof r.type=="string"&&N.has(r.type)){let o=O(r);return o?{kind:"event",runId:t,event:o}:null}switch(r.type){case"event":{if(!r.event||typeof r.event!="object")return null;let o=O(r.event);return o?{kind:"event",runId:t,event:o}:null}case"usage":{let o=$(r.usage);return o?{kind:"usage",runId:t,usage:o}:null}case"tool_call":{let o=c(r.callId),s=c(r.name);return!o||!s?null:{kind:"tool_call",runId:t,callId:o,name:s,input:r.input,...r.answered===!0?{answered:!0}:{}}}case"approval_request":{let o=c(r.approvalId),s=c(r.tool);return!o||!s?null:{kind:"approval",runId:t,approvalId:o,tool:s,input:r.input,cwd:c(r.cwd),...r.answered===!0?{answered:!0}:{}}}default:return null}}function O(n){let e=typeof n.type=="string"&&N.has(n.type)?n.type:null;if(!e)return null;let r={type:e},t=c(n.text);t!==void 0&&(r.text=t);let o=c(n.message);o!==void 0&&(r.message=o);let s=c(n.tool);s!==void 0&&(r.tool=s);let i=c(n.tier);i!==void 0&&(r.tier=i);let u=$(n.usage);u&&(r.usage=u);let d=c(n.reason);d!==void 0&&(r.reason=d),typeof n.retryable=="boolean"&&(r.retryable=n.retryable);let h=n.turn&&typeof n.turn=="object"?c(n.turn.text):void 0;h!==void 0&&(r.turn={text:h});let l=c(n.session);l!==void 0&&(r.session=l);let p=c(n.toolInput);p!==void 0&&(r.toolInput=p);let m=ue(n.git);return m&&(r.git=m),(n.state==="interrupted"||n.state==="resumed")&&(r.state=n.state),r}function ue(n){if(!n||typeof n!="object")return null;let e=n,r=c(e.branch);return!r||!Array.isArray(e.files)||!Array.isArray(e.commits)?null:{branch:r,base:c(e.base)??"",head:c(e.head)??"",ahead:y(e.ahead),behind:y(e.behind),files:e.files.filter(t=>!!t&&typeof t=="object"&&typeof t.path=="string").map(t=>({path:t.path,status:t.status==="A"||t.status==="D"||t.status==="R"?t.status:"M",added:y(t.added),removed:y(t.removed)})),commits:e.commits.filter(t=>!!t&&typeof t=="object").map(t=>({sha:c(t.sha)??"",subject:c(t.subject)??""})),dirty:e.dirty===!0}}function $(n){if(!n||typeof n!="object")return null;let e=n,r={model:c(e.model)??"unknown",inputTokens:y(e.inputTokens),outputTokens:y(e.outputTokens),cacheReadTokens:y(e.cacheReadTokens),cacheWriteTokens:y(e.cacheWriteTokens),webSearches:y(e.webSearches)},t=c(e.costClass);t!==void 0&&(r.costClass=t);let o=c(e.provider);return o!==void 0&&(r.provider=o),typeof e.costMicros=="number"&&Number.isFinite(e.costMicros)&&(r.costMicros=e.costMicros),r}function c(n){return typeof n=="string"&&n.length>0?n:void 0}function y(n){return typeof n=="number"&&Number.isFinite(n)?n:0}async function*F(n){let e=n.body;if(!e)throw new a({code:"connection",message:"The run response arrived with no body to stream.",status:n.status});let r=n.headers.get("content-type")??"",t=r.includes("ndjson")||r.includes("jsonlines"),o=e.getReader(),s=new TextDecoder,i="",u=[],d,h=l=>d===void 0?l:{...l,seq:d};try{for(;;){let p=await o.read();if(p.done)break;for(i+=s.decode(p.value,{stream:!0});;){let m=i.indexOf(`
2
2
  `);if(m===-1)break;let f=L(i.slice(0,m));if(i=i.slice(m+1),t){let g=f.trim()?C(f):null;g&&(yield g);continue}if(f===""){let g=u.join(`
3
3
  `);if(u=[],g==="[DONE]")return;let U=g?C(g):null;U&&(yield h(U)),d=void 0;continue}if(f.startsWith("id:")){let g=Number(f.slice(3).trim());d=Number.isInteger(g)&&g>0?g:void 0;continue}D(f,u)}}!t&&i&&D(L(i),u);let l=t?i.trim():u.join(`
4
- `);if(l&&l!=="[DONE]"){let p=C(l);p&&(yield t?p:h(p))}}finally{o.cancel().catch(()=>{})}}function D(n,e){if(n.startsWith(":"))return;let r=n.indexOf(":");if(r===-1||n.slice(0,r)!=="data")return;let t=n.slice(r+1);e.push(t.startsWith(" ")?t.slice(1):t)}function L(n){return n.endsWith("\r")?n.slice(0,-1):n}function ce(n){return n}function S(n){let e=new Map;for(let r of n??[]){if(!r?.name)throw new a({code:"invalid_request",message:"Every tool needs a name."});if(typeof r.execute!="function")throw new a({code:"invalid_request",message:`Tool "${r.name}" has no execute function. A tool Soba cannot call is not a tool.`});if(e.has(r.name))throw new a({code:"invalid_request",message:`Two tools are named "${r.name}". Tool names must be unique within a run.`});e.set(r.name,r)}return e}function I(n){return[...n.values()].map(e=>({name:e.name,description:e.description??"",inputSchema:e.inputSchema??{type:"object"}}))}function v(n){if(n===void 0)return null;if(typeof n=="string")return n;try{return JSON.parse(JSON.stringify(n))}catch{return String(n)}}function T(n){return n instanceof Error?n.message||n.name:typeof n=="string"?n:String(n)}var k=class{#e;#n;#r;#u=!1;#o;#t;#s=new AbortController;#i=new AbortController;#a=new Set;#c;constructor(e){this.#t=e,this.#e=e.response.headers.get("soba-run-id")??"",this.#o=e.after??0,this.#c=A(e.signal,this.#s)}get cursor(){return this.#o}get resumable(){return this.#t.response.headers.get("soba-resumable")==="1"}get runId(){return this.#e}get tier(){return this.#n}get usage(){return this.#r}[Symbol.asyncIterator](){return this.#l()}async text(){let e="";for await(let r of this)if(r.type==="delta"&&r.text&&(e+=r.text),r.type==="error")throw new a({code:"run_failed",message:r.message??"The run ended with an error.",runId:this.#e,retryable:!1});return e}async cancel(){if(this.#s.abort(new a({code:"canceled",message:"The run was cancelled."})),this.#i.abort(new a({code:"canceled",message:"The run was cancelled."})),!!this.#e)try{await b(this.#t.config,{method:"POST",path:`/runs/${encodeURIComponent(this.#e)}/cancel`,retry:!1})}catch(e){this.#t.onWarning("Could not tell Soba the run was cancelled.",e)}}async*#l(){if(this.#u)throw new a({code:"invalid_request",message:"This run has already been consumed. A run's events can only be read once.",runId:this.#e});this.#u=!0;let e=!1;try{for await(let r of F(this.#t.response)){if(r.runId&&!this.#e&&(this.#e=r.runId),r.seq!==void 0&&(this.#o=r.seq),r.kind==="event"){let t=r.event;if(t.tier&&(this.#n=t.tier),t.usage&&(this.#r=t.usage),e=_(t),yield t,e)return;continue}if(r.kind==="usage"){this.#r=r.usage;continue}this.#p(this.#g(r))}throw new a({code:"stream_truncated",message:"The run stream ended without a `done` or `error` event.",runId:this.#e})}finally{e||this.#i.abort(new a({code:"canceled",message:"The run ended before this tool finished."})),await Promise.allSettled([...this.#a]),this.#s.abort(),this.#c()}}#p(e){this.#a.add(e),e.finally(()=>this.#a.delete(e)).catch(()=>{})}async#g(e){if(!((e.kind==="tool_call"||e.kind==="approval")&&e.answered)){if(e.kind==="tool_call")return this.#f(e);if(e.kind==="approval")return this.#m(e)}}async#f(e){let r=this.#t.tools.get(e.name),t,o=!1;if(!r)t=`No tool named "${e.name}" is registered by this app.`,o=!0;else{let s={runId:this.#e,user:this.#t.user,callId:e.callId,signal:this.#i.signal};try{t=await r.execute(e.input,s)}catch(i){t=T(i),o=!0}}await this.#d(`/runs/${encodeURIComponent(this.#e)}/tool_result`,{callId:e.callId,result:v(t),isError:o})}async#m(e){let r="deny",t;if(!this.#t.approve)t="This app did not supply an approver, so escalations are denied.";else try{let o={runId:this.#e,approvalId:e.approvalId,tool:e.tool,input:e.input};e.cwd&&(o.cwd=e.cwd),r=await this.#t.approve(o)==="allow"?"allow":"deny"}catch(o){t=T(o)}await this.#d(`/runs/${encodeURIComponent(this.#e)}/approval`,{approvalId:e.approvalId,decision:r,...t?{reason:t}:{}})}async#d(e,r){if(!this.#e){this.#t.onWarning("Soba sent a frame before the run had an id, so it could not be answered.",r);return}try{await(await b(this.#t.config,{method:"POST",path:e,body:r})).text()}catch(t){this.#t.onWarning(`Could not deliver an answer to ${e}.`,w(t))}}};var j="https://api.soba.so/v1",W="https://soba.so/v1",de=3e4,le=2,P=class{#e;#n;#r;users;usage;connect;plans;runs;ops;terminals;constructor(e={}){let r=e.apiKey??x("SOBA_KEY")??x("SOBA_API_KEY");if(!r)throw new a({code:"unauthorized",message:"No Soba API key. Pass `apiKey`, or set SOBA_KEY. Keys are shaped sk_soba_<id>.<secret> and come from your dashboard."});if(pe()&&!e.dangerouslyAllowBrowser)throw new a({code:"invalid_request",message:"A Soba app key is a server credential and this looks like a browser. Call Soba from your server, or pass dangerouslyAllowBrowser if you are certain."});if(this.#r=e.onWarning??(()=>{}),this.#e={apiKey:r,baseURL:e.baseURL??x("SOBA_BASE_URL")??j,headers:e.headers??{},fetch:e.fetch??globalThis.fetch,connectTimeoutMs:e.connectTimeoutMs??de,maxRetries:e.maxRetries??le},this.#n={...this.#e,baseURL:e.webBaseURL??x("SOBA_WEB_URL")??W},typeof this.#e.fetch!="function")throw new a({code:"invalid_request",message:"No global fetch. Run on Node 22 or newer, or pass your own `fetch`."});this.users={get:t=>this.request("GET",`/users/${encodeURIComponent(t)}`),compute:async t=>(await this.request("GET",`/users/${encodeURIComponent(t)}/compute`)).machines,setQuantity:(t,o,s)=>R(this.#n,{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/quantity`,body:{quantity:s}}),check:(t,o,s)=>R(s?.use?{...this.#n,maxRetries:0}:this.#n,{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/check`,body:s?.use?{use:s.use}:{}}),track:(t,o,s=1)=>R({...this.#n,maxRetries:0},{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/track`,body:{amount:s}})},this.usage={list:t=>this.request("GET","/usage",{query:{...t}})},this.connect=()=>this.request("GET","/connect"),this.runs={start:async t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required."});let o=S(t.tools),s={user:t.user,model:t.model??"auto",detached:!0};t.webhook&&(s.webhook=t.webhook),t.prompt&&(s.prompt=t.prompt),t.messages?.length&&(s.messages=t.messages),t.system&&(s.system=t.system),t.runtime&&(s.runtime=t.runtime),o.size&&(s.tools=I(o)),t.route&&(s.route=t.route),t.policy&&(s.policy=t.policy),t.metadata&&(s.metadata=t.metadata),t.task&&(s.task=t.task),t.surface&&(s.surface=t.surface),t.maxDurationMs&&(s.maxDurationMs=t.maxDurationMs),t.title&&(s.title=t.title),t.worktree&&(s.worktree=t.worktree),t.resume&&(s.resume=t.resume);let i=await R(this.#e,{method:"POST",path:"/runs",body:s,retry:"unsent"}),u={id:i.id};return i.stream&&(u.stream={url:i.stream.url,token:i.stream.token,expiresAt:i.stream.expires_at}),t.answerTools==="here"&&(u.answered=(async()=>{let d=await this.runs.attach(i.id,{user:t.user,tools:t.tools??[]});for await(let h of d);})().catch(d=>this.#r("Answering this run's tool calls stopped.",d))),u},attach:async(t,o)=>{if(!o.user)throw new a({code:"user_required",status:422,message:"`user` is required."});let s=await b(this.#e,{method:"GET",path:`/runs/${encodeURIComponent(t)}/stream`,...o.after?{query:{after:o.after}}:{},accept:"text/event-stream",signal:o.signal});return new k({config:this.#e,response:s,user:o.user,tools:S(o.tools),approve:o.approve,onWarning:this.#r,signal:o.signal,after:o.after??0})},cancel:async t=>{await this.request("POST",`/runs/${encodeURIComponent(t)}/cancel`,{body:{}})}},this.ops={run:t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required \u2014 it is how Soba knows whose machine to ask."});return this.request("POST","/ops",{body:{...t}})}},this.terminals={open:async t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required \u2014 it is how Soba knows whose machine to open it on."});let o=await this.request("POST","/term",{body:{...t}}),s=new URL(this.#e.baseURL);s.protocol=s.protocol==="http:"?"ws:":"wss:";let i=new URL(o.path,`${s.protocol}//${s.host}`);return i.searchParams.set("ticket",o.ticket),{termId:o.termId,url:i.toString(),expiresAt:o.expiresAt}}},this.plans={list:t=>this.request("GET","/plans",{...t?.user?{query:{user:t.user}}:{}})}}get baseURL(){return this.#e.baseURL}async run(e){if(!e.user)throw new a({code:"user_required",status:422,message:"`user` is required. A run with no user cannot be attributed or metered, so it cannot be defaulted."});if(!e.prompt&&!e.messages?.length)throw new a({code:"invalid_request",status:400,message:"A run needs a `prompt` or a non-empty `messages`."});let r=S(e.tools),t={user:e.user,model:e.model??"auto"};e.prompt&&(t.prompt=e.prompt),e.messages?.length&&(t.messages=e.messages),e.system&&(t.system=e.system),e.runtime&&(t.runtime=e.runtime),r.size&&(t.tools=I(r)),e.route&&(t.route=e.route),e.policy&&(t.policy=e.policy),e.metadata&&(t.metadata=e.metadata),e.task&&(t.task=e.task),e.resumable&&(t.resumable=!0),e.maxDurationMs&&(t.maxDurationMs=e.maxDurationMs);let o=await b(this.#e,{method:"POST",path:"/runs",body:t,accept:"text/event-stream",signal:e.signal,retry:"unsent"});return new k({config:this.#e,response:o,user:e.user,tools:r,approve:e.approve,onWarning:this.#r,signal:e.signal})}async request(e,r,t={}){return R(this.#e,{method:e,path:r,...t})}};function x(n){if(typeof process>"u")return;let e=process.env?.[n];return e||void 0}function pe(){let n=globalThis;return typeof n.window=="object"&&typeof n.window?.document=="object"}var ge=300;function fe(n){return[...new Uint8Array(n)].map(e=>e.toString(16).padStart(2,"0")).join("")}function me(n,e){if(n.length!==e.length)return!1;let r=0;for(let t=0;t<n.length;t++)r|=n.charCodeAt(t)^e.charCodeAt(t);return r===0}async function he(n,e,r,t={}){let o=Object.fromEntries((e??"").split(",").map(f=>{let g=f.indexOf("=");return[f.slice(0,g).trim(),f.slice(g+1).trim()]})),s=Number(o.t),i=o.v1??"",u=f=>new a({code:"unauthorized",status:401,message:f});if(!Number.isFinite(s)||!i)throw u("Missing or malformed Soba-Signature.");let d=Math.floor((t.now??Date.now())/1e3);if(Math.abs(d-s)>(t.toleranceSeconds??ge))throw u("This delivery is too old to accept.");let h=await globalThis.crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=fe(await globalThis.crypto.subtle.sign("HMAC",h,new TextEncoder().encode(`${s}.${n}`)));if(!me(l,i))throw u("The Soba-Signature does not match.");let p;try{p=JSON.parse(n)}catch{throw new a({code:"invalid_request",message:"The delivery is not JSON."})}let m=p;if(m.type!=="run.event"&&m.type!=="run.tool_call"||typeof m.run_id!="string")throw new a({code:"invalid_request",message:"Not a Soba run delivery."});return m}async function ye(n,e){if(n.type!=="run.tool_call")return null;let{call_id:r,name:t,input:o}=n.data,s=S(e.tools).get(t);if(!s)return{result:`Unknown tool: ${t}`,is_error:!0};try{let i=await s.execute(o??{},{runId:n.run_id,user:e.user??"",callId:r,signal:AbortSignal.timeout(25e3)});return{result:v(i)}}catch(i){return{result:T(i),is_error:!0}}}export{j as DEFAULT_BASE_URL,W as DEFAULT_WEB_BASE_URL,M as METERED_CLASSES,k as Run,P as Soba,a as SobaError,ae as TOOL_SLUGS,E as VERSION,ye as handleRunWebhook,ie as isCostClass,se as isMetered,_ as isTerminal,ce as tool,he as verifyWebhook};
4
+ `);if(l&&l!=="[DONE]"){let p=C(l);p&&(yield t?p:h(p))}}finally{o.cancel().catch(()=>{})}}function D(n,e){if(n.startsWith(":"))return;let r=n.indexOf(":");if(r===-1||n.slice(0,r)!=="data")return;let t=n.slice(r+1);e.push(t.startsWith(" ")?t.slice(1):t)}function L(n){return n.endsWith("\r")?n.slice(0,-1):n}function ce(n){return n}function S(n){let e=new Map;for(let r of n??[]){if(!r?.name)throw new a({code:"invalid_request",message:"Every tool needs a name."});if(typeof r.execute!="function")throw new a({code:"invalid_request",message:`Tool "${r.name}" has no execute function. A tool Soba cannot call is not a tool.`});if(e.has(r.name))throw new a({code:"invalid_request",message:`Two tools are named "${r.name}". Tool names must be unique within a run.`});e.set(r.name,r)}return e}function I(n){return[...n.values()].map(e=>({name:e.name,description:e.description??"",inputSchema:e.inputSchema??{type:"object"}}))}function x(n){if(n===void 0)return null;if(typeof n=="string")return n;try{return JSON.parse(JSON.stringify(n))}catch{return String(n)}}function T(n){return n instanceof Error?n.message||n.name:typeof n=="string"?n:String(n)}var k=class{#e;#n;#r;#u=!1;#o;#t;#s=new AbortController;#i=new AbortController;#a=new Set;#c;constructor(e){this.#t=e,this.#e=e.response.headers.get("soba-run-id")??"",this.#o=e.after??0,this.#c=A(e.signal,this.#s)}get cursor(){return this.#o}get resumable(){return this.#t.response.headers.get("soba-resumable")==="1"}get runId(){return this.#e}get tier(){return this.#n}get usage(){return this.#r}[Symbol.asyncIterator](){return this.#l()}async text(){let e="";for await(let r of this)if(r.type==="delta"&&r.text&&(e+=r.text),r.type==="error")throw new a({code:"run_failed",message:r.message??"The run ended with an error.",runId:this.#e,retryable:!1});return e}async cancel(){if(this.#s.abort(new a({code:"canceled",message:"The run was cancelled."})),this.#i.abort(new a({code:"canceled",message:"The run was cancelled."})),!!this.#e)try{await b(this.#t.config,{method:"POST",path:`/runs/${encodeURIComponent(this.#e)}/cancel`,retry:!1})}catch(e){this.#t.onWarning("Could not tell Soba the run was cancelled.",e)}}async*#l(){if(this.#u)throw new a({code:"invalid_request",message:"This run has already been consumed. A run's events can only be read once.",runId:this.#e});this.#u=!0;let e=!1;try{for await(let r of F(this.#t.response)){if(r.runId&&!this.#e&&(this.#e=r.runId),r.seq!==void 0&&(this.#o=r.seq),r.kind==="event"){let t=r.event;if(t.tier&&(this.#n=t.tier),t.usage&&(this.#r=t.usage),e=_(t),yield t,e)return;continue}if(r.kind==="usage"){this.#r=r.usage;continue}this.#p(this.#g(r))}throw new a({code:"stream_truncated",message:"The run stream ended without a `done` or `error` event.",runId:this.#e})}finally{e||this.#i.abort(new a({code:"canceled",message:"The run ended before this tool finished."})),await Promise.allSettled([...this.#a]),this.#s.abort(),this.#c()}}#p(e){this.#a.add(e),e.finally(()=>this.#a.delete(e)).catch(()=>{})}async#g(e){if(!((e.kind==="tool_call"||e.kind==="approval")&&e.answered)){if(e.kind==="tool_call")return this.#f(e);if(e.kind==="approval")return this.#m(e)}}async#f(e){let r=this.#t.tools.get(e.name),t,o=!1;if(!r)t=`No tool named "${e.name}" is registered by this app.`,o=!0;else{let s={runId:this.#e,user:this.#t.user,callId:e.callId,signal:this.#i.signal};try{t=await r.execute(e.input,s)}catch(i){t=T(i),o=!0}}await this.#d(`/runs/${encodeURIComponent(this.#e)}/tool_result`,{callId:e.callId,result:x(t),isError:o})}async#m(e){let r="deny",t;if(!this.#t.approve)t="This app did not supply an approver, so escalations are denied.";else try{let o={runId:this.#e,approvalId:e.approvalId,tool:e.tool,input:e.input};e.cwd&&(o.cwd=e.cwd),r=await this.#t.approve(o)==="allow"?"allow":"deny"}catch(o){t=T(o)}await this.#d(`/runs/${encodeURIComponent(this.#e)}/approval`,{approvalId:e.approvalId,decision:r,...t?{reason:t}:{}})}async#d(e,r){if(!this.#e){this.#t.onWarning("Soba sent a frame before the run had an id, so it could not be answered.",r);return}try{await(await b(this.#t.config,{method:"POST",path:e,body:r})).text()}catch(t){this.#t.onWarning(`Could not deliver an answer to ${e}.`,w(t))}}};var j="https://api.soba.so/v1",W="https://soba.so/v1",de=3e4,le=2,P=class{#e;#n;#r;users;usage;connect;plans;runs;ops;terminals;constructor(e={}){let r=e.apiKey??v("SOBA_KEY")??v("SOBA_API_KEY");if(!r)throw new a({code:"unauthorized",message:"No Soba API key. Pass `apiKey`, or set SOBA_KEY. Keys are shaped sk_soba_<id>.<secret> and come from your dashboard."});if(pe()&&!e.dangerouslyAllowBrowser)throw new a({code:"invalid_request",message:"A Soba app key is a server credential and this looks like a browser. Call Soba from your server, or pass dangerouslyAllowBrowser if you are certain."});if(this.#r=e.onWarning??(()=>{}),this.#e={apiKey:r,baseURL:e.baseURL??v("SOBA_BASE_URL")??j,headers:e.headers??{},fetch:e.fetch??globalThis.fetch,connectTimeoutMs:e.connectTimeoutMs??de,maxRetries:e.maxRetries??le},this.#n={...this.#e,baseURL:e.webBaseURL??v("SOBA_WEB_URL")??W},typeof this.#e.fetch!="function")throw new a({code:"invalid_request",message:"No global fetch. Run on Node 22 or newer, or pass your own `fetch`."});this.users={get:t=>this.request("GET",`/users/${encodeURIComponent(t)}`),compute:async t=>(await this.request("GET",`/users/${encodeURIComponent(t)}/compute`)).machines,setQuantity:(t,o,s)=>R(this.#n,{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/quantity`,body:{quantity:s}}),check:(t,o,s)=>R(s?.use?{...this.#n,maxRetries:0}:this.#n,{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/check`,body:s?.use?{use:s.use}:{}}),track:(t,o,s=1)=>R({...this.#n,maxRetries:0},{method:"POST",path:`/end_users/${encodeURIComponent(t)}/features/${encodeURIComponent(o)}/track`,body:{amount:s}})},this.usage={list:t=>this.request("GET","/usage",{query:{...t}})},this.connect=()=>this.request("GET","/connect"),this.runs={start:async t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required."});let o=S(t.tools),s={user:t.user,model:t.model??"auto",detached:!0};t.webhook&&(s.webhook=t.webhook),t.prompt&&(s.prompt=t.prompt),t.messages?.length&&(s.messages=t.messages),t.system&&(s.system=t.system),t.runtime&&(s.runtime=t.runtime),o.size&&(s.tools=I(o)),t.route&&(s.route=t.route),t.policy&&(s.policy=t.policy),t.metadata&&(s.metadata=t.metadata),t.task&&(s.task=t.task),t.surface&&(s.surface=t.surface),t.maxDurationMs&&(s.maxDurationMs=t.maxDurationMs),t.title&&(s.title=t.title),t.worktree&&(s.worktree=t.worktree),t.resume&&(s.resume=t.resume);let i=await R(this.#e,{method:"POST",path:"/runs",body:s,retry:"unsent"}),u={id:i.id};return i.stream&&(u.stream={url:i.stream.url,token:i.stream.token,expiresAt:i.stream.expires_at}),t.answerTools==="here"&&(u.answered=(async()=>{let d=await this.runs.attach(i.id,{user:t.user,tools:t.tools??[]});for await(let h of d);})().catch(d=>this.#r("Answering this run's tool calls stopped.",d))),u},attach:async(t,o)=>{if(!o.user)throw new a({code:"user_required",status:422,message:"`user` is required."});let s=await b(this.#e,{method:"GET",path:`/runs/${encodeURIComponent(t)}/stream`,...o.after?{query:{after:o.after}}:{},accept:"text/event-stream",signal:o.signal});return new k({config:this.#e,response:s,user:o.user,tools:S(o.tools),approve:o.approve,onWarning:this.#r,signal:o.signal,after:o.after??0})},cancel:async t=>{await this.request("POST",`/runs/${encodeURIComponent(t)}/cancel`,{body:{}})}},this.ops={run:t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required \u2014 it is how Soba knows whose machine to ask."});return this.request("POST","/ops",{body:{...t}})}},this.terminals={open:async t=>{if(!t.user)throw new a({code:"user_required",status:422,message:"`user` is required \u2014 it is how Soba knows whose machine to open it on."});let o=await this.request("POST","/term",{body:{...t}}),s=new URL(this.#e.baseURL);s.protocol=s.protocol==="http:"?"ws:":"wss:";let i=new URL(o.path,`${s.protocol}//${s.host}`);return i.searchParams.set("ticket",o.ticket),{termId:o.termId,url:i.toString(),expiresAt:o.expiresAt}}},this.plans={list:t=>this.request("GET","/plans",{...t?.user?{query:{user:t.user}}:{}})}}get baseURL(){return this.#e.baseURL}async run(e){if(!e.user)throw new a({code:"user_required",status:422,message:"`user` is required. A run with no user cannot be attributed or metered, so it cannot be defaulted."});if(!e.prompt&&!e.messages?.length)throw new a({code:"invalid_request",status:400,message:"A run needs a `prompt` or a non-empty `messages`."});let r=S(e.tools),t={user:e.user,model:e.model??"auto"};e.prompt&&(t.prompt=e.prompt),e.messages?.length&&(t.messages=e.messages),e.system&&(t.system=e.system),e.runtime&&(t.runtime=e.runtime),r.size&&(t.tools=I(r)),e.route&&(t.route=e.route),e.policy&&(t.policy=e.policy),e.metadata&&(t.metadata=e.metadata),e.task&&(t.task=e.task),e.resumable&&(t.resumable=!0),e.maxDurationMs&&(t.maxDurationMs=e.maxDurationMs);let o=await b(this.#e,{method:"POST",path:"/runs",body:t,accept:"text/event-stream",signal:e.signal,retry:"unsent"});return new k({config:this.#e,response:o,user:e.user,tools:r,approve:e.approve,onWarning:this.#r,signal:e.signal})}async request(e,r,t={}){return R(this.#e,{method:e,path:r,...t})}};function v(n){if(typeof process>"u")return;let e=process.env?.[n];return e||void 0}function pe(){let n=globalThis;return typeof n.window=="object"&&typeof n.window?.document=="object"}var ge=300;function fe(n){return[...new Uint8Array(n)].map(e=>e.toString(16).padStart(2,"0")).join("")}function me(n,e){if(n.length!==e.length)return!1;let r=0;for(let t=0;t<n.length;t++)r|=n.charCodeAt(t)^e.charCodeAt(t);return r===0}async function he(n,e,r,t={}){let o=Object.fromEntries((e??"").split(",").map(f=>{let g=f.indexOf("=");return[f.slice(0,g).trim(),f.slice(g+1).trim()]})),s=Number(o.t),i=o.v1??"",u=f=>new a({code:"unauthorized",status:401,message:f});if(!Number.isFinite(s)||!i)throw u("Missing or malformed Soba-Signature.");let d=Math.floor((t.now??Date.now())/1e3);if(Math.abs(d-s)>(t.toleranceSeconds??ge))throw u("This delivery is too old to accept.");let h=await globalThis.crypto.subtle.importKey("raw",new TextEncoder().encode(r),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),l=fe(await globalThis.crypto.subtle.sign("HMAC",h,new TextEncoder().encode(`${s}.${n}`)));if(!me(l,i))throw u("The Soba-Signature does not match.");let p;try{p=JSON.parse(n)}catch{throw new a({code:"invalid_request",message:"The delivery is not JSON."})}let m=p;if(m.type!=="run.event"&&m.type!=="run.tool_call"||typeof m.run_id!="string")throw new a({code:"invalid_request",message:"Not a Soba run delivery."});return m}async function ye(n,e){if(n.type!=="run.tool_call")return null;let{call_id:r,name:t,input:o}=n.data,s=S(e.tools).get(t);if(!s)return{result:`Unknown tool: ${t}`,is_error:!0};try{let i=await s.execute(o??{},{runId:n.run_id,user:e.user??"",callId:r,signal:AbortSignal.timeout(25e3)});return{result:x(i)}}catch(i){return{result:T(i),is_error:!0}}}export{j as DEFAULT_BASE_URL,W as DEFAULT_WEB_BASE_URL,M as METERED_CLASSES,k as Run,P as Soba,a as SobaError,ae as TOOL_SLUGS,E as VERSION,ye as handleRunWebhook,ie as isCostClass,se as isMetered,_ as isTerminal,ce as tool,he as verifyWebhook};
@@ -93,6 +93,14 @@ export type ToolSlug = (typeof TOOL_SLUGS)[number];
93
93
  export interface Message {
94
94
  role: "user" | "assistant";
95
95
  content: string;
96
+ /** Pictures attached to this turn, in order. A runtime that cannot see
97
+ * images gets `content` alone and says so in a status line. */
98
+ images?: ImageInput[];
99
+ }
100
+ /** One attached picture, inline: base64 without the `data:` prefix. */
101
+ export interface ImageInput {
102
+ mediaType: "image/png" | "image/jpeg" | "image/gif" | "image/webp";
103
+ data: string;
96
104
  }
97
105
  /** An app-defined tool as it travels on the wire: schema only, no implementation. */
98
106
  export interface ToolSchema {
@@ -110,6 +110,10 @@ export interface PlanListItem {
110
110
  /** Runs a month on the user's own AI, counted apart from `included_units`.
111
111
  * Null is unlimited. Optional so an older server that omits it parses. */
112
112
  own_included_units?: number | null;
113
+ /** What `own_included_units` counts: `unit` unless the plan splits —
114
+ * "200 runs on your own AI, 2,000 credits on ours". Optional for older
115
+ * servers, which mean `unit`. */
116
+ own_unit?: string;
113
117
  /** What happens past the allowance: `stop` or `meter`. The difference
114
118
  * between a wall and a bill, and not guessable from the price. */
115
119
  overage: string;
@@ -163,6 +167,8 @@ export interface CurrentPlanItem {
163
167
  units_used: number;
164
168
  remaining: number | null;
165
169
  own_included_units: number | null;
170
+ /** What the own_* figures count. Optional for older servers: `unit`. */
171
+ own_unit?: string;
166
172
  own_units_used: number;
167
173
  own_remaining: number | null;
168
174
  resets_at: string | null;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.0.11";
1
+ export declare const VERSION = "0.0.12";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soba-so/sdk",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "One request that comes back finished, executed on compute the end user owns.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://soba.so/docs/runs",