Mike0021 commited on
Commit
6b71b4b
·
verified ·
1 Parent(s): b8f94aa

Ship chat interface and model gate

Browse files
README.md CHANGED
@@ -52,7 +52,7 @@ Start the dev server, then run:
52
  npm run smoke:web
53
  ```
54
 
55
- The smoke test opens Chromium, confirms `crossOriginIsolated`, boots the WebContainer sandbox, runs the pi agent in deterministic mode, writes `hello.js`, spawns `node hello.js`, and checks for `pi sandbox result: 42` in the transcript.
56
 
57
  For the heavier end-to-end check with the real MiniCPM5 ONNX model in browser WASM mode:
58
 
@@ -62,6 +62,8 @@ npm run smoke:local-model
62
 
63
  This downloads/loads the q4 ONNX artifact in Chromium, runs the same pi/WebContainer task, and checks that the model reaches `Model ready` before the sandbox result is accepted.
64
 
 
 
65
  ## Verify the Published Artifact
66
 
67
  ```bash
 
52
  npm run smoke:web
53
  ```
54
 
55
+ The smoke test opens Chromium, confirms `crossOriginIsolated`, submits the chat prompt in deterministic mode, boots the WebContainer sandbox, writes `hello.js`, spawns `node hello.js`, and checks for `pi sandbox result: 42` in the chat transcript.
56
 
57
  For the heavier end-to-end check with the real MiniCPM5 ONNX model in browser WASM mode:
58
 
 
62
 
63
  This downloads/loads the q4 ONNX artifact in Chromium, runs the same pi/WebContainer task, and checks that the model reaches `Model ready` before the sandbox result is accepted.
64
 
65
+ The sandbox can also install and use Node packages through the same `run_command` tool, for example `npm install is-number@7.0.0` followed by `node check-package.mjs`.
66
+
67
  ## Verify the Published Artifact
68
 
69
  ```bash
dist/assets/anthropic-BY6_w10V.js ADDED
The diff for this file is too large to render. See raw diff
 
dist/assets/azure-openai-responses-DBDjwtao.js ADDED
@@ -0,0 +1 @@
 
 
1
+ import{O as h,a as c,r as m,i as _,b as R}from"./client-Sq9UPDIz.js";import{A as y,g as l,a as I}from"./index-x-2U2tSW.js";import{h as g}from"./headers-CgnjaPPL.js";import{c as v}from"./openai-prompt-cache-h_V5PL7P.js";import{p as O,c as N,a as b}from"./openai-responses-shared-Co2Tgb08.js";import{b as P}from"./transform-messages-Bxe2XUr4.js";import"./hash-DMDecQcg.js";import"./json-parse-rAW8WjEh.js";import"./sanitize-unicode-B62XMGgN.js";var w={};class U extends h{constructor({baseURL:t=m("OPENAI_BASE_URL"),apiKey:e=m("AZURE_OPENAI_API_KEY"),apiVersion:s=m("OPENAI_API_VERSION"),endpoint:n,deployment:a,azureADTokenProvider:o,dangerouslyAllowBrowser:i,...u}={}){if(!s)throw new c("The OPENAI_API_VERSION environment variable is missing or empty; either provide it, or instantiate the AzureOpenAI client with an apiVersion option, like new AzureOpenAI({ apiVersion: 'My API Version' }).");if(typeof o=="function"&&(i=!0),!o&&!e)throw new c("Missing credentials. Please pass one of `apiKey` and `azureADTokenProvider`, or set the `AZURE_OPENAI_API_KEY` environment variable.");if(o&&e)throw new c("The `apiKey` and `azureADTokenProvider` arguments are mutually exclusive; only one can be passed at a time.");if(u.defaultQuery={...u.defaultQuery,"api-version":s},t){if(n)throw new c("baseURL and endpoint are mutually exclusive")}else{if(n||(n=w.AZURE_OPENAI_ENDPOINT),!n)throw new c("Must provide one of the `baseURL` or `endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable");t=`${n}/openai`}super({apiKey:o??e,baseURL:t,...u,...i!==void 0?{dangerouslyAllowBrowser:i}:{}}),this.apiVersion="",this.apiVersion=s,this.deploymentName=a}async buildRequest(t,e={}){if(z.has(t.path)&&t.method==="post"&&t.body!==void 0){if(!_(t.body))throw new Error("Expected request body to be an object");const s=this.deploymentName||t.body.model||t.__metadata?.model;s!==void 0&&!this.baseURL.includes("/deployments")&&(t.path=`/deployments/${s}${t.path}`)}return super.buildRequest(t,e)}async authHeaders(t){return typeof this._options.apiKey=="string"?R([{"api-key":this.apiKey}]):super.authHeaders(t)}}const z=new Set(["/completions","/chat/completions","/embeddings","/audio/transcriptions","/audio/translations","/audio/speech","/images/generations","/batches","/images/edits"]);var p={};const S="v1",M=new Set(["openai","openai-codex","opencode","azure-openai-responses"]);function L(r){const t=new Map;if(!r)return t;for(const e of r.split(",")){const s=e.trim();if(!s)continue;const[n,a]=s.split("=",2);!n||!a||t.set(n.trim(),a.trim())}return t}function Z(r,t){return t?.azureDeploymentName?t.azureDeploymentName:L(p.AZURE_OPENAI_DEPLOYMENT_NAME_MAP).get(r.id)||r.id}function k(r){if(r instanceof Error){const t=r.status,e=typeof t=="number"?t:void 0;return e!==void 0?`Azure OpenAI API error (${e}): ${r.message}`:r.message}try{return JSON.stringify(r)}catch{return String(r)}}const D=(r,t,e)=>{const s=new y;return(async()=>{const n=Z(r,e),a={role:"assistant",content:[],api:"azure-openai-responses",provider:r.provider,model:r.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const o=e?.apiKey||l(r.provider)||"",i=x(r,o,e);let u=V(r,t,e,n);const d=await e?.onPayload?.(u,r);d!==void 0&&(u=d);const A={...e?.signal?{signal:e.signal}:{},...e?.timeoutMs!==void 0?{timeout:e.timeoutMs}:{},...e?.maxRetries!==void 0?{maxRetries:e.maxRetries}:{}},{data:E,response:f}=await i.responses.create(u,A).withResponse();if(await e?.onResponse?.({status:f.status,headers:g(f.headers)},r),s.push({type:"start",partial:a}),await O(E,a,s,r),e?.signal?.aborted)throw new Error("Request was aborted");if(a.stopReason==="aborted"||a.stopReason==="error")throw new Error("An unknown error occurred");s.push({type:"done",reason:a.stopReason,message:a}),s.end()}catch(o){for(const i of a.content)delete i.index,delete i.partialJson;a.stopReason=e?.signal?.aborted?"aborted":"error",a.errorMessage=k(o),s.push({type:"error",reason:a.stopReason,error:a}),s.end()}})(),s},F=(r,t,e)=>{const s=e?.apiKey||l(r.provider);if(!s)throw new Error(`No API key for provider: ${r.provider}`);const n=P(r,e,s),a=e?.reasoning?I(r,e.reasoning):void 0;return D(r,t,{...n,reasoningEffort:a==="off"?void 0:a})};function K(r){const t=r.trim().replace(/\/+$/,"");let e;try{e=new URL(t)}catch{throw new Error(`Invalid Azure OpenAI base URL: ${r}`)}const s=e.hostname.endsWith(".openai.azure.com")||e.hostname.endsWith(".cognitiveservices.azure.com"),n=e.pathname.replace(/\/+$/,"");return s&&(n===""||n==="/"||n==="/openai")&&(e.pathname="/openai/v1",e.search=""),e.toString().replace(/\/+$/,"")}function T(r){return`https://${r}.openai.azure.com/openai/v1`}function $(r,t){const e=t?.azureApiVersion||p.AZURE_OPENAI_API_VERSION||S,s=t?.azureBaseUrl?.trim()||p.AZURE_OPENAI_BASE_URL?.trim()||void 0,n=t?.azureResourceName||p.AZURE_OPENAI_RESOURCE_NAME;let a=s;if(!a&&n&&(a=T(n)),!a&&r.baseUrl&&(a=r.baseUrl),!a)throw new Error("Azure OpenAI base URL is required. Set AZURE_OPENAI_BASE_URL or AZURE_OPENAI_RESOURCE_NAME, or pass azureBaseUrl, azureResourceName, or model.baseUrl.");return{baseUrl:K(a),apiVersion:e}}function x(r,t,e){if(!t){if(!p.AZURE_OPENAI_API_KEY)throw new Error("Azure OpenAI API key is required. Set AZURE_OPENAI_API_KEY environment variable or pass it as an argument.");t=p.AZURE_OPENAI_API_KEY}const s={...r.headers};e?.headers&&Object.assign(s,e.headers);const{baseUrl:n,apiVersion:a}=$(r,e);return new U({apiKey:t,apiVersion:a,dangerouslyAllowBrowser:!0,defaultHeaders:s,baseURL:n})}function V(r,t,e,s){const n=N(r,t,M),a={model:s,input:n,stream:!0,prompt_cache_key:v(e?.sessionId)};if(e?.maxTokens&&(a.max_output_tokens=e?.maxTokens),e?.temperature!==void 0&&(a.temperature=e?.temperature),t.tools&&t.tools.length>0&&(a.tools=b(t.tools)),r.reasoning)if(e?.reasoningEffort||e?.reasoningSummary){const o=e?.reasoningEffort?r.thinkingLevelMap?.[e.reasoningEffort]??e.reasoningEffort:"medium";a.reasoning={effort:o,summary:e?.reasoningSummary||"auto"},a.include=["reasoning.encrypted_content"]}else r.thinkingLevelMap?.off!==null&&(a.reasoning={effort:r.thinkingLevelMap?.off??"none"});return a}export{D as streamAzureOpenAIResponses,F as streamSimpleAzureOpenAIResponses};
dist/assets/google-Buofg9N6.js ADDED
@@ -0,0 +1 @@
 
 
1
+ import{i as T,r as b,m as v,G as S,c as R,a as L,b as _}from"./google-shared-CZIKa0Kf.js";import{A as G,g as x,c as O,a as A}from"./index-x-2U2tSW.js";import{s as N}from"./sanitize-unicode-B62XMGgN.js";import{b as E}from"./transform-messages-Bxe2XUr4.js";let P=0;const f=(e,o,n)=>{const i=new G;return(async()=>{const t={role:"assistant",content:[],api:"google-generative-ai",provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const r=n?.apiKey||x(e.provider)||"",c=W(e,r,n?.headers);let u=B(e,o,n);const y=await n?.onPayload?.(u,e);y!==void 0&&(u=y);const M=await c.models.generateContentStream(u);i.push({type:"start",partial:t});let a=null;const C=t.content,l=()=>C.length-1;for await(const g of M){t.responseId||=g.responseId;const d=g.candidates?.[0];if(d?.content?.parts)for(const s of d.content.parts){if(s.text!==void 0){const h=T(s);(!a||h&&a.type!=="thinking"||!h&&a.type!=="text")&&(a&&(a.type==="text"?i.push({type:"text_end",contentIndex:C.length-1,content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t})),h?(a={type:"thinking",thinking:"",thinkingSignature:void 0},t.content.push(a),i.push({type:"thinking_start",contentIndex:l(),partial:t})):(a={type:"text",text:""},t.content.push(a),i.push({type:"text_start",contentIndex:l(),partial:t}))),a.type==="thinking"?(a.thinking+=s.text,a.thinkingSignature=b(a.thinkingSignature,s.thoughtSignature),i.push({type:"thinking_delta",contentIndex:l(),delta:s.text,partial:t})):(a.text+=s.text,a.textSignature=b(a.textSignature,s.thoughtSignature),i.push({type:"text_delta",contentIndex:l(),delta:s.text,partial:t}))}if(s.functionCall){a&&(a.type==="text"?i.push({type:"text_end",contentIndex:l(),content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t}),a=null);const h=s.functionCall.id,p={type:"toolCall",id:!h||t.content.some(I=>I.type==="toolCall"&&I.id===h)?`${s.functionCall.name}_${Date.now()}_${++P}`:h,name:s.functionCall.name||"",arguments:s.functionCall.args??{},...s.thoughtSignature&&{thoughtSignature:s.thoughtSignature}};t.content.push(p),i.push({type:"toolcall_start",contentIndex:l(),partial:t}),i.push({type:"toolcall_delta",contentIndex:l(),delta:JSON.stringify(p.arguments),partial:t}),i.push({type:"toolcall_end",contentIndex:l(),toolCall:p,partial:t})}}d?.finishReason&&(t.stopReason=v(d.finishReason),t.content.some(s=>s.type==="toolCall")&&(t.stopReason="toolUse")),g.usageMetadata&&(t.usage={input:(g.usageMetadata.promptTokenCount||0)-(g.usageMetadata.cachedContentTokenCount||0),output:(g.usageMetadata.candidatesTokenCount||0)+(g.usageMetadata.thoughtsTokenCount||0),cacheRead:g.usageMetadata.cachedContentTokenCount||0,cacheWrite:0,totalTokens:g.usageMetadata.totalTokenCount||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},O(e,t.usage))}if(a&&(a.type==="text"?i.push({type:"text_end",contentIndex:l(),content:a.text,partial:t}):i.push({type:"thinking_end",contentIndex:l(),content:a.thinking,partial:t})),n?.signal?.aborted)throw new Error("Request was aborted");if(t.stopReason==="aborted"||t.stopReason==="error")throw new Error("An unknown error occurred");i.push({type:"done",reason:t.stopReason,message:t}),i.end()}catch(r){for(const c of t.content)"index"in c&&delete c.index;t.stopReason=n?.signal?.aborted?"aborted":"error",t.errorMessage=r instanceof Error?r.message:JSON.stringify(r),i.push({type:"error",reason:t.stopReason,error:t}),i.end()}})(),i},F=(e,o,n)=>{const i=n?.apiKey||x(e.provider);if(!i)throw new Error(`No API key for provider: ${e.provider}`);const t=E(e,n,i);if(!n?.reasoning)return f(e,o,{...t,thinking:{enabled:!1}});const r=A(e,n.reasoning),c=r==="off"?"high":r,u=e;return m(u)||w(u)||k(u)?f(e,o,{...t,thinking:{enabled:!0,level:K(c,u)}}):f(e,o,{...t,thinking:{enabled:!0,budgetTokens:U(u,c,n.thinkingBudgets)}})};function W(e,o,n){const i={};return e.baseUrl&&(i.baseUrl=e.baseUrl,i.apiVersion=""),(e.headers||n)&&(i.headers={...e.headers,...n}),new S({apiKey:o,httpOptions:Object.keys(i).length>0?i:void 0})}function B(e,o,n={}){const i=R(e,o),t={};n.temperature!==void 0&&(t.temperature=n.temperature),n.maxTokens!==void 0&&(t.maxOutputTokens=n.maxTokens);const r={...Object.keys(t).length>0&&t,...o.systemPrompt&&{systemInstruction:N(o.systemPrompt)},...o.tools&&o.tools.length>0&&{tools:L(o.tools)}};if(o.tools&&o.tools.length>0&&n.toolChoice?r.toolConfig={functionCallingConfig:{mode:_(n.toolChoice)}}:r.toolConfig=void 0,n.thinking?.enabled&&e.reasoning){const u={includeThoughts:!0};n.thinking.level!==void 0?u.thinkingLevel=n.thinking.level:n.thinking.budgetTokens!==void 0&&(u.thinkingBudget=n.thinking.budgetTokens),r.thinkingConfig=u}else e.reasoning&&n.thinking&&!n.thinking.enabled&&(r.thinkingConfig=H(e));if(n.signal){if(n.signal.aborted)throw new Error("Request aborted");r.abortSignal=n.signal}return{model:e.id,contents:i,config:r}}function k(e){return/gemma-?4/.test(e.id.toLowerCase())}function m(e){return/gemini-3(?:\.\d+)?-pro/.test(e.id.toLowerCase())}function w(e){return/gemini-3(?:\.\d+)?-flash/.test(e.id.toLowerCase())}function H(e){return m(e)?{thinkingLevel:"LOW"}:w(e)?{thinkingLevel:"MINIMAL"}:k(e)?{thinkingLevel:"MINIMAL"}:{thinkingBudget:0}}function K(e,o){if(m(o))switch(e){case"minimal":case"low":return"LOW";case"medium":case"high":return"HIGH"}if(k(o))switch(e){case"minimal":case"low":return"MINIMAL";case"medium":case"high":return"HIGH"}switch(e){case"minimal":return"MINIMAL";case"low":return"LOW";case"medium":return"MEDIUM";case"high":return"HIGH"}}function U(e,o,n){return n?.[o]!==void 0?n[o]:e.id.includes("2.5-pro")?{minimal:128,low:2048,medium:8192,high:32768}[o]:e.id.includes("2.5-flash-lite")?{minimal:512,low:2048,medium:8192,high:24576}[o]:e.id.includes("2.5-flash")?{minimal:128,low:2048,medium:8192,high:24576}[o]:-1}export{f as streamGoogle,F as streamSimpleGoogle};
dist/assets/google-vertex-CpAHxVwR.js ADDED
@@ -0,0 +1 @@
 
 
1
+ import{i as T,r as x,m as w,G as L,c as R,a as M,b as G,T as d,R as S}from"./google-shared-CZIKa0Kf.js";import{A,c as N,a as P}from"./index-x-2U2tSW.js";import{s as U}from"./sanitize-unicode-B62XMGgN.js";import{b as D}from"./transform-messages-Bxe2XUr4.js";var f={};const v="v1",V="gcp-vertex-credentials",H={THINKING_LEVEL_UNSPECIFIED:d.THINKING_LEVEL_UNSPECIFIED,MINIMAL:d.MINIMAL,LOW:d.LOW,MEDIUM:d.MEDIUM,HIGH:d.HIGH};let K=0;const k=(t,e,n)=>{const a=new A;return(async()=>{const i={role:"assistant",content:[],api:"google-vertex",provider:t.provider,model:t.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const s=$(n),l=s?j(t,s,n?.headers):W(t,F(n),z(n),n?.headers);let g=X(t,e,n);const I=await n?.onPayload?.(g,t);I!==void 0&&(g=I);const E=await l.models.generateContentStream(g);a.push({type:"start",partial:i});let o=null;const y=i.content,c=()=>y.length-1;for await(const u of E){i.responseId||=u.responseId;const p=u.candidates?.[0];if(p?.content?.parts)for(const r of p.content.parts){if(r.text!==void 0){const h=T(r);(!o||h&&o.type!=="thinking"||!h&&o.type!=="text")&&(o&&(o.type==="text"?a.push({type:"text_end",contentIndex:y.length-1,content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i})),h?(o={type:"thinking",thinking:"",thinkingSignature:void 0},i.content.push(o),a.push({type:"thinking_start",contentIndex:c(),partial:i})):(o={type:"text",text:""},i.content.push(o),a.push({type:"text_start",contentIndex:c(),partial:i}))),o.type==="thinking"?(o.thinking+=r.text,o.thinkingSignature=x(o.thinkingSignature,r.thoughtSignature),a.push({type:"thinking_delta",contentIndex:c(),delta:r.text,partial:i})):(o.text+=r.text,o.textSignature=x(o.textSignature,r.thoughtSignature),a.push({type:"text_delta",contentIndex:c(),delta:r.text,partial:i}))}if(r.functionCall){o&&(o.type==="text"?a.push({type:"text_end",contentIndex:c(),content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i}),o=null);const h=r.functionCall.id,m={type:"toolCall",id:!h||i.content.some(O=>O.type==="toolCall"&&O.id===h)?`${r.functionCall.name}_${Date.now()}_${++K}`:h,name:r.functionCall.name||"",arguments:r.functionCall.args??{},...r.thoughtSignature&&{thoughtSignature:r.thoughtSignature}};i.content.push(m),a.push({type:"toolcall_start",contentIndex:c(),partial:i}),a.push({type:"toolcall_delta",contentIndex:c(),delta:JSON.stringify(m.arguments),partial:i}),a.push({type:"toolcall_end",contentIndex:c(),toolCall:m,partial:i})}}p?.finishReason&&(i.stopReason=w(p.finishReason),i.content.some(r=>r.type==="toolCall")&&(i.stopReason="toolUse")),u.usageMetadata&&(i.usage={input:(u.usageMetadata.promptTokenCount||0)-(u.usageMetadata.cachedContentTokenCount||0),output:(u.usageMetadata.candidatesTokenCount||0)+(u.usageMetadata.thoughtsTokenCount||0),cacheRead:u.usageMetadata.cachedContentTokenCount||0,cacheWrite:0,totalTokens:u.usageMetadata.totalTokenCount||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},N(t,i.usage))}if(o&&(o.type==="text"?a.push({type:"text_end",contentIndex:c(),content:o.text,partial:i}):a.push({type:"thinking_end",contentIndex:c(),content:o.thinking,partial:i})),n?.signal?.aborted)throw new Error("Request was aborted");if(i.stopReason==="aborted"||i.stopReason==="error")throw new Error("An unknown error occurred");a.push({type:"done",reason:i.stopReason,message:i}),a.end()}catch(s){for(const l of i.content)"index"in l&&delete l.index;i.stopReason=n?.signal?.aborted?"aborted":"error",i.errorMessage=s instanceof Error?s.message:JSON.stringify(s),a.push({type:"error",reason:i.stopReason,error:i}),a.end()}})(),a},rt=(t,e,n)=>{const a=D(t,n,void 0);if(!n?.reasoning)return k(t,e,{...a,thinking:{enabled:!1}});const i=P(t,n.reasoning),s=i==="off"?"high":i,l=t;return C(l)||b(l)?k(t,e,{...a,thinking:{enabled:!0,level:Q(s,l)}}):k(t,e,{...a,thinking:{enabled:!0,budgetTokens:Z(l,s,n.thinkingBudgets)}})};function W(t,e,n,a){return new L({vertexai:!0,project:e,location:n,apiVersion:v,httpOptions:_(t,a)})}function j(t,e,n){return new L({vertexai:!0,apiKey:e,apiVersion:v,httpOptions:_(t,n)})}function _(t,e){const n={},a=B(t.baseUrl);return a&&(n.baseUrl=a,n.baseUrlResourceScope=S.COLLECTION,J(a)&&(n.apiVersion="")),(t.headers||e)&&(n.headers={...t.headers,...e}),Object.keys(n).length>0?n:void 0}function B(t){const e=t.trim();if(!(!e||e.includes("{location}")))return e}function J(t){try{return new URL(t).pathname.split("/").some(n=>/^v\d+(?:beta\d*)?$/.test(n))}catch{return/(?:^|\/)v\d+(?:beta\d*)?(?:\/|$)/.test(t)}}function $(t){const e=t?.apiKey?.trim()||f.GOOGLE_CLOUD_API_KEY?.trim();if(!(!e||e===V||q(e)))return e}function q(t){return/^<[^>]+>$/.test(t)}function F(t){const e=t?.project||f.GOOGLE_CLOUD_PROJECT||f.GCLOUD_PROJECT;if(!e)throw new Error("Vertex AI requires a project ID. Set GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT or pass project in options.");return e}function z(t){const e=t?.location||f.GOOGLE_CLOUD_LOCATION;if(!e)throw new Error("Vertex AI requires a location. Set GOOGLE_CLOUD_LOCATION or pass location in options.");return e}function X(t,e,n={}){const a=R(t,e),i={};n.temperature!==void 0&&(i.temperature=n.temperature),n.maxTokens!==void 0&&(i.maxOutputTokens=n.maxTokens);const s={...Object.keys(i).length>0&&i,...e.systemPrompt&&{systemInstruction:U(e.systemPrompt)},...e.tools&&e.tools.length>0&&{tools:M(e.tools)}};if(e.tools&&e.tools.length>0&&n.toolChoice?s.toolConfig={functionCallingConfig:{mode:G(n.toolChoice)}}:s.toolConfig=void 0,n.thinking?.enabled&&t.reasoning){const g={includeThoughts:!0};n.thinking.level!==void 0?g.thinkingLevel=H[n.thinking.level]:n.thinking.budgetTokens!==void 0&&(g.thinkingBudget=n.thinking.budgetTokens),s.thinkingConfig=g}else t.reasoning&&n.thinking&&!n.thinking.enabled&&(s.thinkingConfig=Y(t));if(n.signal){if(n.signal.aborted)throw new Error("Request aborted");s.abortSignal=n.signal}return{model:t.id,contents:a,config:s}}function C(t){return/gemini-3(?:\.\d+)?-pro/.test(t.id.toLowerCase())}function b(t){return/gemini-3(?:\.\d+)?-flash/.test(t.id.toLowerCase())}function Y(t){const e=t;return C(e)?{thinkingLevel:d.LOW}:b(e)?{thinkingLevel:d.MINIMAL}:{thinkingBudget:0}}function Q(t,e){if(C(e))switch(t){case"minimal":case"low":return"LOW";case"medium":case"high":return"HIGH"}switch(t){case"minimal":return"MINIMAL";case"low":return"LOW";case"medium":return"MEDIUM";case"high":return"HIGH"}}function Z(t,e,n){return n?.[e]!==void 0?n[e]:t.id.includes("2.5-pro")?{minimal:128,low:2048,medium:8192,high:32768}[e]:t.id.includes("2.5-flash")?{minimal:128,low:2048,medium:8192,high:24576}[e]:-1}export{k as streamGoogleVertex,rt as streamSimpleGoogleVertex};
dist/assets/index-4NYd9uAS.css ADDED
@@ -0,0 +1 @@
 
 
1
+ :root{color:#182233;background:#eef2f6;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}body{margin:0;min-width:320px}button,input,select,textarea{font:inherit}button{min-height:40px;padding:0 15px;border:0;border-radius:6px;background:#176b6c;color:#fff;font-weight:750;cursor:pointer}button:disabled{cursor:wait;opacity:.6}#app{min-height:100vh;padding:14px}.app-shell{display:grid;grid-template-rows:auto minmax(0,1fr);gap:12px;width:min(1360px,100%);min-height:calc(100vh - 28px);margin:0 auto}.topbar{display:flex;align-items:end;justify-content:space-between;gap:16px;padding:8px 0 14px;border-bottom:1px solid #c7d2df}h1,h2,p{margin:0}h1{font-size:34px;line-height:1;letter-spacing:0}h2{font-size:15px;line-height:1.2;color:#3f5064}#model-label{margin-top:7px;color:#5d6c7f;overflow-wrap:anywhere}.status-stack{display:flex;flex-wrap:wrap;justify-content:end;gap:8px;max-width:680px}.status{min-height:34px;max-width:260px;padding:7px 10px;border:1px solid #b7c4d3;border-radius:6px;background:#fff;color:#314155;overflow-wrap:anywhere}.layout{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:12px;min-height:0}.chat-panel,.side-panel{min-height:0}.chat-panel{display:grid;grid-template-rows:minmax(0,1fr) auto;gap:12px}.chat-scroll{min-height:420px;max-height:calc(100vh - 214px);overflow:auto;padding:18px;border:1px solid #c1ccda;border-radius:8px;background:#fbfcfe}.empty-chat{display:grid;align-content:center;gap:10px;min-height:320px;color:#4c5d72;text-align:center}.empty-chat h2{font-size:24px;color:#182233}.message{display:grid;gap:7px;width:min(760px,92%);margin-bottom:14px;padding:12px 14px;border:1px solid #c4ceda;border-radius:8px;background:#fff}.message.user{margin-left:auto;border-color:#99bfbd;background:#e9f7f4}.message.assistant{border-color:#c4ceda}.message.tool{width:min(820px,96%);margin-left:24px;background:#f5f7fa}.message.error{border-color:#d99a9a;background:#fff4f4}.message-label{color:#5e6e82;font-size:12px;font-weight:800;text-transform:uppercase}.message-body{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.48}.message-code,pre{margin:0;white-space:pre-wrap;overflow:auto;overflow-wrap:anywhere;line-height:1.45}.message-code{max-height:240px;padding:10px;border-radius:6px;background:#182233;color:#e9eef5}.tool-call{display:grid;gap:6px;padding:10px;border:1px solid #d1dae5;border-radius:6px;background:#f7f9fc}.tool-name{color:#315f63;font-weight:800}.tool-call code{white-space:pre-wrap;overflow-wrap:anywhere}.composer{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:end;padding:10px;border:1px solid #c1ccda;border-radius:8px;background:#fff}textarea{width:100%;min-height:64px;max-height:180px;resize:vertical;padding:12px;border:1px solid #b8c5d4;border-radius:6px;background:#fff;color:#152033;line-height:1.45}.composer-actions{display:flex;gap:8px}#demo-prompt{background:#4f5f73}.side-panel{display:grid;align-content:start;gap:12px}.control-group{display:grid;gap:10px;padding:12px;border:1px solid #c1ccda;border-radius:8px;background:#fff}label{display:grid;gap:6px;color:#526173;font-size:13px;font-weight:700}select,input{width:100%;height:40px;padding:0 10px;border:1px solid #b8c5d4;border-radius:6px;background:#fff;color:#152033}.compact-grid,.button-row{display:grid;grid-template-columns:1fr 1fr;gap:8px}#reset-sandbox,#use-test-model{background:#4f5f73}#files,#event-log{min-height:150px;max-height:230px;padding:10px;border:1px solid #d1dae5;border-radius:6px;background:#f7f9fc;color:#243248}#event-log{max-height:270px}.model-gate{position:fixed;inset:0;display:grid;place-items:center;padding:18px;background:#141f2d75;z-index:20}.model-gate.hidden{display:none}.model-dialog{display:grid;gap:16px;width:min(540px,100%);padding:22px;border-radius:8px;background:#fff;box-shadow:0 24px 80px #111b2a38}.eyebrow{margin-bottom:7px;color:#176b6c;font-size:12px;font-weight:850;text-transform:uppercase}.model-dialog h2{font-size:24px;color:#172033}.dialog-copy{margin-top:8px;color:#526173;line-height:1.5}.dialog-actions{display:flex;flex-wrap:wrap;gap:10px}.dialog-options{display:grid;gap:10px;color:#526173}#gate-status{min-height:22px;overflow-wrap:anywhere}@media(max-width:900px){#app{padding:10px}.topbar{align-items:stretch;flex-direction:column}.status-stack{justify-content:start}.layout{grid-template-columns:1fr}.chat-scroll{min-height:360px;max-height:none}.composer{grid-template-columns:1fr}.composer-actions{justify-content:end}}
dist/assets/index-x-2U2tSW.js ADDED
The diff for this file is too large to render. See raw diff
 
dist/assets/json-parse-rAW8WjEh.js ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ import{d as o}from"./index-x-2U2tSW.js";const a=new Set(['"',"\\","/","b","f","n","r","t","u"]);function f(t){const r=t.codePointAt(0);return r!==void 0&&r>=0&&r<=31}function d(t){switch(t){case"\b":return"\\b";case"\f":return"\\f";case`
2
+ `:return"\\n";case"\r":return"\\r";case" ":return"\\t";default:return`\\u${t.codePointAt(0)?.toString(16).padStart(4,"0")??"0000"}`}}function s(t){let r="",c=!1;for(let e=0;e<t.length;e++){const n=t[e];if(!c){r+=n,n==='"'&&(c=!0);continue}if(n==='"'){r+=n,c=!1;continue}if(n==="\\"){const i=t[e+1];if(i===void 0){r+="\\\\";continue}if(i==="u"){const u=t.slice(e+2,e+6);if(/^[0-9a-fA-F]{4}$/.test(u)){r+=`\\u${u}`,e+=5;continue}}if(a.has(i)){r+=`\\${i}`,e+=1;continue}r+="\\\\";continue}r+=f(n)?d(n):n}return r}function h(t){try{return JSON.parse(t)}catch(r){const c=s(t);if(c!==t)return JSON.parse(c);throw r}}function l(t){if(!t||t.trim()==="")return{};try{return h(t)}catch{try{return o.parse(t)??{}}catch{try{return o.parse(s(t))??{}}catch{return{}}}}}export{l as a,h as p};
dist/assets/mistral-DalHcdf1.js ADDED
The diff for this file is too large to render. See raw diff
 
dist/assets/openai-codex-responses-6mDVsjvH.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import{A as ae,g as j,a as ie}from"./index-x-2U2tSW.js";import{h as X}from"./headers-CgnjaPPL.js";import{c as ce}from"./openai-prompt-cache-h_V5PL7P.js";import{c as V,a as ue,p as Y}from"./openai-responses-shared-Co2Tgb08.js";import{b as le}from"./transform-messages-Bxe2XUr4.js";import"./hash-DMDecQcg.js";import"./json-parse-rAW8WjEh.js";import"./sanitize-unicode-B62XMGgN.js";const $=new Set;function de(e){return $.add(e),()=>{$.delete(e)}}function A(e){return e instanceof Error?e.message||e.name:typeof e=="string"?e:String(e)}function fe(e){if(!(e instanceof Error))return{name:"ThrownValue",message:A(e)};const r=e.code;return{name:e.name||void 0,message:e.message||e.name,stack:e.stack,code:typeof r=="string"||typeof r=="number"?r:void 0}}function pe(e,r,t){return{type:e,timestamp:Date.now(),error:fe(r),details:t}}function ye(e,r){e.diagnostics=[...e.diagnostics??[],r]}var R={},me=function(e,r){return typeof e=="string"&&/^\.\.?\//.test(e)?e.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(t,s,o,n,i){return s?".js":o&&(!n||!i)?t:o+n+"."+i.toLowerCase()+"js"}):e};let v=null;const z=e=>import(me(e)),be="node:os";typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&z(be).then(e=>{v=e});const we="https://chatgpt.com/backend-api",he="https://api.openai.com/auth",W=3,F=1e3,G=new Set(["openai","openai-codex","opencode"]),ge=1009,Se=new Set(["completed","incomplete","failed","cancelled","queued","in_progress"]);function Ee(e,r){return e===429||e===500||e===502||e===503||e===504?!0:/rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(r)}function J(e,r){return new Promise((t,s)=>{if(r?.aborted){s(new Error("Request was aborted"));return}const o=setTimeout(t,e);r?.addEventListener("abort",()=>{clearTimeout(o),s(new Error("Request was aborted"))})})}const ve=(e,r,t)=>{const s=new ae;return(async()=>{const o={role:"assistant",content:[],api:"openai-codex-responses",provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const n=t?.apiKey||j(e.provider)||"";if(!n)throw new Error(`No API key for provider: ${e.provider}`);const i=Xe(n);let a=ke(e,r,t);const u=await t?.onPayload?.(a,e);u!==void 0&&(a=u);const l=t?.sessionId||Ve(),w=Ye(e.headers,t?.headers,i,n,t?.sessionId),g=ze(e.headers,t?.headers,i,n,l),d=JSON.stringify(a),b=t?.transport||"auto",f=b!=="sse"&&re(t?.sessionId);if(f&&U(t?.sessionId),b!=="sse"&&!f){let m=!1;try{if(await Ke(xe(e.baseUrl),a,g,o,s,e,()=>{m=!0},t),t?.signal?.aborted)throw new Error("Request was aborted");s.push({type:"done",reason:o.stopReason,message:o}),s.end();return}catch(p){if(t?.signal?.aborted||Te(p)||(ye(o,pe("provider_transport_failure",p,{configuredTransport:b,fallbackTransport:m?void 0:"sse",eventsEmitted:m,phase:m?"after_message_stream_start":"before_message_stream_start",requestBytes:new TextEncoder().encode(d).byteLength})),Le(t?.sessionId,p),m))throw p;U(t?.sessionId)}}let c,y;for(let m=0;m<=W;m++){if(t?.signal?.aborted)throw new Error("Request was aborted");try{if(c=await fetch(ee(e.baseUrl),{method:"POST",headers:w,body:d,signal:t?.signal}),await t?.onResponse?.({status:c.status,headers:X(c.headers)},e),c.ok)break;const p=await c.text();if(m<W&&Ee(c.status,p)){let x=F*2**m;const P=c.headers.get("retry-after-ms");if(P!==null){const E=Number(P);Number.isFinite(E)&&(x=Math.max(0,E))}else{const E=c.headers.get("retry-after");if(E){const B=Number(E);if(Number.isFinite(B))x=Math.max(0,B*1e3);else{const M=Date.parse(E);Number.isNaN(M)||(x=Math.max(0,M-Date.now()))}}}await J(x,t?.signal);continue}const _=new Response(p,{status:c.status,statusText:c.statusText}),N=await je(_);throw new Error(N.friendlyMessage||N.message)}catch(p){if(p instanceof Error&&(p.name==="AbortError"||p.message==="Request was aborted"))throw new Error("Request was aborted");if(y=p instanceof Error?p:new Error(String(p)),m<W&&!y.message.includes("usage limit")){const _=F*2**m;await J(_,t?.signal);continue}throw y}}if(!c?.ok)throw y??new Error("Failed after retries");if(!c.body)throw new Error("No response body");if(s.push({type:"start",partial:o}),await Re(c,o,s,e,t),t?.signal?.aborted)throw new Error("Request was aborted");s.push({type:"done",reason:o.stopReason,message:o}),s.end()}catch(n){for(const i of o.content)delete i.partialJson;o.stopReason=t?.signal?.aborted?"aborted":"error",o.errorMessage=n instanceof Error?n.message:String(n),s.push({type:"error",reason:o.stopReason,error:o}),s.end()}})(),s},nt=(e,r,t)=>{const s=t?.apiKey||j(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const o=le(e,t,s),n=t?.reasoning?ie(e,t.reasoning):void 0;return ve(e,r,{...o,reasoningEffort:n==="off"?void 0:n})};function ke(e,r,t){const s=V(e,r,G,{includeSystemPrompt:!1}),o={model:e.id,store:!1,stream:!0,instructions:r.systemPrompt||"You are a helpful assistant.",input:s,text:{verbosity:t?.textVerbosity||"low"},include:["reasoning.encrypted_content"],prompt_cache_key:ce(t?.sessionId),tool_choice:"auto",parallel_tool_calls:!0};if(t?.temperature!==void 0&&(o.temperature=t.temperature),t?.serviceTier!==void 0&&(o.service_tier=t.serviceTier),r.tools&&r.tools.length>0&&(o.tools=ue(r.tools,{strict:null})),t?.reasoningEffort!==void 0){const n=t.reasoningEffort==="none"?e.thinkingLevelMap?.off??"none":e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort;n!==null&&(o.reasoning={effort:n,summary:t.reasoningSummary??"auto"})}return o}function _e(e,r){switch(r){case"flex":return .5;case"priority":return e.id==="gpt-5.5"?2.5:2;default:return 1}}function Q(e,r,t){const s=_e(t,r);s!==1&&(e.cost.input*=s,e.cost.output*=s,e.cost.cacheRead*=s,e.cost.cacheWrite*=s,e.cost.total=e.cost.input+e.cost.output+e.cost.cacheRead+e.cost.cacheWrite)}function Z(e,r){return e==="default"&&(r==="flex"||r==="priority")?r:e??r}function ee(e){const t=(e&&e.trim().length>0?e:we).replace(/\/+$/,"");return t.endsWith("/codex/responses")?t:t.endsWith("/codex")?`${t}/responses`:`${t}/codex/responses`}function xe(e){const r=new URL(ee(e));return r.protocol==="https:"&&(r.protocol="wss:"),r.protocol==="http:"&&(r.protocol="ws:"),r.toString()}async function Re(e,r,t,s,o){await Y(te(Oe(e)),r,t,s,{serviceTier:o?.serviceTier,resolveServiceTier:Z,applyServiceTierPricing:(n,i)=>Q(n,i,s)})}class L extends Error{code;payload;constructor(r,t){super(r),this.name="CodexApiError",this.code=t?.code,this.payload=t?.payload,this.cause=t?.cause}}class q extends Error{payload;constructor(r,t){super(r),this.name="CodexProtocolError",this.payload=t?.payload,this.cause=t?.cause}}function Te(e){return e instanceof L||e instanceof q}async function*te(e){for await(const r of e){const t=typeof r.type=="string"?r.type:void 0;if(t){if(t==="error"){const s=r.code||"",o=r.message||"";throw new L(`Codex error: ${o||s||JSON.stringify(r)}`,{code:s||void 0,payload:r})}if(t==="response.failed"){const s=r.response,o=s?.error?.code,n=s?.error?.message;throw new L(n||"Codex response failed",{code:o,payload:r})}if(t==="response.done"||t==="response.completed"||t==="response.incomplete"){const s=r.response,o=s&&{...s,status:Ce(s.status)};yield{...r,type:"response.completed",response:o};return}yield r}}}function Ce(e){if(typeof e=="string")return Se.has(e)?e:void 0}async function*Oe(e){if(!e.body)return;const r=e.body.getReader(),t=new TextDecoder;let s="";try{for(;;){const{done:o,value:n}=await r.read();if(o)break;s+=t.decode(n,{stream:!0});let i=s.indexOf(`
2
+
3
+ `);for(;i!==-1;){const a=s.slice(0,i);s=s.slice(i+2);const u=a.split(`
4
+ `).filter(l=>l.startsWith("data:")).map(l=>l.slice(5).trim());if(u.length>0){const l=u.join(`
5
+ `).trim();if(l&&l!=="[DONE]")try{yield JSON.parse(l)}catch(w){throw new q(`Invalid Codex SSE JSON: ${A(w)}`,{cause:w,payload:l})}}i=s.indexOf(`
6
+
7
+ `)}}}finally{try{await r.cancel()}catch{}try{r.releaseLock()}catch{}}}const Ae="responses_websockets=2026-02-06",We=300*1e3,h=new Map,k=new Map,O=new Set;function D(e){let r=k.get(e);return r||(r={requests:0,connectionsCreated:0,connectionsReused:0,cachedContextRequests:0,storeTrueRequests:0,fullContextRequests:0,deltaRequests:0,lastInputItems:0,websocketFailures:0,sseFallbacks:0},k.set(e,r)),r}function at(e){const r=k.get(e);return r?{...r}:void 0}function it(e){if(e){k.delete(e),O.delete(e);return}k.clear(),O.clear()}function Ie(e){const r=t=>{t.idleTimer&&clearTimeout(t.idleTimer),S(t.socket,1e3,"debug_close")};if(e){const t=h.get(e);t&&r(t),h.delete(e);return}for(const t of h.values())r(t);h.clear()}de(Ie);function re(e){return e?O.has(e):!1}function U(e){if(!e)return;const r=D(e);r.sseFallbacks++,r.websocketFallbackActive=re(e)}function Le(e,r){if(!e)return;O.add(e);const t=D(e);t.websocketFailures++,t.lastWebSocketError=A(r),t.websocketFallbackActive=!0}let T=null;async function qe(){if(T)return T;if(process?.versions?.bun&&(R.HTTP_PROXY||R.HTTPS_PROXY||R.http_proxy||R.https_proxy)){const t=(await z("proxy-from-env")).getProxyForUrl;return T=class extends WebSocket{constructor(s,o){let n={};Array.isArray(o)||typeof o=="string"?n={protocols:o}:n={...o};const i=t(s.toString().replace(/^wss:/,"https:").replace(/^ws:/,"http:"));super(s,{...n,...i?{proxy:i}:{}})}},T}const e=globalThis.WebSocket;return typeof e!="function"?null:e}class De extends Error{code;reason;wasClean;constructor(r,t){super(r),this.name="WebSocketCloseError",this.code=t?.code,this.reason=t?.reason,this.wasClean=t?.wasClean}}function Ne(e){const r=e.readyState;return typeof r=="number"?r:void 0}function C(e){const r=Ne(e);return r===void 0||r===1}function S(e,r=1e3,t="done"){try{e.close(r,t)}catch{}}function H(e,r){r.idleTimer&&clearTimeout(r.idleTimer),r.idleTimer=setTimeout(()=>{r.busy||(S(r.socket,1e3,"idle_timeout"),h.delete(e))},We)}async function I(e,r,t){const s=await qe();if(!s)throw new Error("WebSocket transport is not available in this runtime");const o=X(r);return delete o["OpenAI-Beta"],new Promise((n,i)=>{let a=!1,u;try{u=new s(e,{headers:o})}catch(f){i(f instanceof Error?f:new Error(String(f)));return}const l=()=>{a||(a=!0,b(),n(u))},w=f=>{const c=se(f);a||(a=!0,b(),i(c))},g=f=>{const c=oe(f);a||(a=!0,b(),i(c))},d=()=>{a||(a=!0,b(),u.close(1e3,"aborted"),i(new Error("Request was aborted")))},b=()=>{u.removeEventListener("open",l),u.removeEventListener("error",w),u.removeEventListener("close",g),t?.removeEventListener("abort",d)};u.addEventListener("open",l),u.addEventListener("error",w),u.addEventListener("close",g),t?.addEventListener("abort",d)})}async function Pe(e,r,t,s){if(!t){const a=await I(e,r,s);return{socket:a,reused:!1,release:({keep:u}={})=>{if(u===!1){S(a);return}S(a)}}}const o=h.get(t);if(o){if(o.idleTimer&&(clearTimeout(o.idleTimer),o.idleTimer=void 0),!o.busy&&C(o.socket))return o.busy=!0,{socket:o.socket,entry:o,reused:!0,release:({keep:a}={})=>{if(!a||!C(o.socket)){S(o.socket),h.delete(t);return}o.busy=!1,H(t,o)}};if(o.busy){const a=await I(e,r,s);return{socket:a,reused:!1,release:()=>{S(a)}}}C(o.socket)||(S(o.socket),h.delete(t))}const n=await I(e,r,s),i={socket:n,busy:!0};return h.set(t,i),{socket:n,entry:i,reused:!1,release:({keep:a}={})=>{if(!a||!C(i.socket)){S(i.socket),i.idleTimer&&clearTimeout(i.idleTimer),h.get(t)===i&&h.delete(t);return}i.busy=!1,H(t,i)}}}function se(e){if(e&&typeof e=="object"){const r="message"in e?e.message:void 0;if(typeof r=="string"&&r.length>0)return new Error(r);const t="error"in e?e.error:void 0;if(t instanceof Error&&t.message.length>0)return t;if(t&&typeof t=="object"&&"message"in t){const s=t.message;if(typeof s=="string"&&s.length>0)return new Error(s)}}return new Error("WebSocket error")}function oe(e){if(e&&typeof e=="object"){const r="code"in e?e.code:void 0,t="reason"in e?e.reason:void 0,s="wasClean"in e?e.wasClean:void 0,o=typeof r=="number"?` ${r}`:"";let n=typeof t=="string"&&t.length>0?` ${t}`:"";return!n&&r===ge&&(n=" message too big"),new De(`WebSocket closed${o}${n}`.trim(),{code:typeof r=="number"?r:void 0,reason:typeof t=="string"&&t.length>0?t:void 0,wasClean:typeof s=="boolean"?s:void 0})}return new Error("WebSocket closed")}async function Be(e){if(typeof e=="string")return e;if(e instanceof ArrayBuffer)return new TextDecoder().decode(new Uint8Array(e));if(ArrayBuffer.isView(e)){const r=e;return new TextDecoder().decode(new Uint8Array(r.buffer,r.byteOffset,r.byteLength))}if(e&&typeof e=="object"&&"arrayBuffer"in e){const t=await e.arrayBuffer();return new TextDecoder().decode(new Uint8Array(t))}return null}async function*Me(e,r){const t=[];let s=null,o=!1,n=null,i=!1;const a=()=>{if(!s)return;const d=s;s=null,d()},u=d=>{(async()=>{let b=null;try{if(!d||typeof d!="object"||!("data"in d)||(b=await Be(d.data),!b))return;const f=JSON.parse(b),c=typeof f.type=="string"?f.type:"";(c==="response.completed"||c==="response.done"||c==="response.incomplete")&&(i=!0,o=!0),t.push(f),a()}catch(f){n=new q(`Invalid Codex WebSocket JSON: ${A(f)}`,{cause:f,payload:b}),o=!0,a()}})()},l=d=>{n=se(d),o=!0,a()},w=d=>{if(i){o=!0,a();return}n||(n=oe(d)),o=!0,a()},g=()=>{n=new Error("Request was aborted"),o=!0,a()};e.addEventListener("message",u),e.addEventListener("error",l),e.addEventListener("close",w),r?.addEventListener("abort",g);try{for(;;){if(r?.aborted)throw new Error("Request was aborted");if(t.length>0){yield t.shift();continue}if(o)break;await new Promise(d=>{s=d})}if(n)throw n;if(!i)throw new Error("WebSocket stream closed before response.completed")}finally{e.removeEventListener("message",u),e.removeEventListener("error",l),e.removeEventListener("close",w),r?.removeEventListener("abort",g)}}function K(e){const{input:r,previous_response_id:t,...s}=e;return s}function $e(e,r){return JSON.stringify(e??[])===JSON.stringify(r??[])}function Fe(e,r){return JSON.stringify(K(e))===JSON.stringify(K(r))}function Je(e,r){if(!Fe(e,r.lastRequestBody))return;const t=e.input??[],s=[...r.lastRequestBody.input??[],...r.lastResponseItems];if(t.length<s.length)return;const o=t.slice(0,s.length);if($e(o,s))return t.slice(s.length)}function Ue(e,r){const t=e.continuation;if(!t)return r;const s=Je(r,t);return!s||!t.lastResponseId?(e.continuation=void 0,r):{...r,previous_response_id:t.lastResponseId,input:s}}async function*He(e,r,t,s){let o=!1;for await(const n of e)o||(o=!0,s(),t.push({type:"start",partial:r})),yield n}async function Ke(e,r,t,s,o,n,i,a){const{socket:u,entry:l,reused:w,release:g}=await Pe(e,t,a?.sessionId,a?.signal);let d=!0;const b=a?.transport==="websocket-cached"||a?.transport==="auto",f=r,c=b&&l?Ue(l,f):f,y=a?.sessionId?D(a.sessionId):void 0;y&&(y.requests++,w?y.connectionsReused++:y.connectionsCreated++,b&&y.cachedContextRequests++,c.store===!0&&y.storeTrueRequests++,y.lastInputItems=c.input?.length??0,c.previous_response_id?(y.deltaRequests++,y.lastDeltaInputItems=c.input?.length??0,y.lastPreviousResponseId=c.previous_response_id):(y.fullContextRequests++,y.lastDeltaInputItems=void 0,y.lastPreviousResponseId=void 0));try{if(u.send(JSON.stringify({type:"response.create",...c})),await Y(He(te(Me(u,a?.signal)),s,o,i),s,o,n,{serviceTier:a?.serviceTier,resolveServiceTier:Z,applyServiceTierPricing:(m,p)=>Q(m,p,n)}),a?.signal?.aborted)d=!1;else if(b&&l&&s.responseId){const m=V(n,{messages:[s]},G,{includeSystemPrompt:!1}).filter(p=>p.type!=="function_call_output");l.continuation={lastRequestBody:f,lastResponseId:s.responseId,lastResponseItems:m}}}catch(m){throw l&&(l.continuation=void 0),d=!1,m}finally{g({keep:d})}}async function je(e){const r=await e.text();let t=r||e.statusText||"Request failed",s;try{const n=JSON.parse(r)?.error;if(n){const i=n.code||n.type||"";if(/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(i)||e.status===429){const a=n.plan_type?` (${n.plan_type.toLowerCase()} plan)`:"",u=n.resets_at?Math.max(0,Math.round((n.resets_at*1e3-Date.now())/6e4)):void 0,l=u!==void 0?` Try again in ~${u} min.`:"";s=`You have hit your ChatGPT usage limit${a}.${l}`.trim()}t=n.message||s||t}}catch{}return{message:t,friendlyMessage:s}}function Xe(e){try{const r=e.split(".");if(r.length!==3)throw new Error("Invalid token");const s=JSON.parse(atob(r[1]))?.[he]?.chatgpt_account_id;if(!s)throw new Error("No account ID in token");return s}catch{throw new Error("Failed to extract accountId from token")}}function Ve(){return typeof globalThis.crypto?.randomUUID=="function"?globalThis.crypto.randomUUID():`codex_${Date.now()}_${Math.random().toString(36).slice(2,10)}`}function ne(e,r,t,s){const o=new Headers(e);for(const[i,a]of Object.entries(r||{}))o.set(i,a);o.set("Authorization",`Bearer ${s}`),o.set("chatgpt-account-id",t),o.set("originator","pi");const n=v?`pi (${v.platform()} ${v.release()}; ${v.arch()})`:"pi (browser)";return o.set("User-Agent",n),o}function Ye(e,r,t,s,o){const n=ne(e,r,t,s);return n.set("OpenAI-Beta","responses=experimental"),n.set("accept","text/event-stream"),n.set("content-type","application/json"),o&&(n.set("session_id",o),n.set("x-client-request-id",o)),n}function ze(e,r,t,s,o){const n=ne(e,r,t,s);return n.delete("accept"),n.delete("content-type"),n.delete("OpenAI-Beta"),n.delete("openai-beta"),n.set("OpenAI-Beta",Ae),n.set("x-client-request-id",o),n.set("session_id",o),n}export{Ie as closeOpenAICodexWebSocketSessions,at as getOpenAICodexWebSocketDebugStats,it as resetOpenAICodexWebSocketDebugStats,ve as streamOpenAICodexResponses,nt as streamSimpleOpenAICodexResponses};
dist/assets/openai-completions-m7jhuP4p.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import{O as G}from"./client-Sq9UPDIz.js";import{A as $,g as L,a as W,c as K}from"./index-x-2U2tSW.js";import{h as J}from"./headers-CgnjaPPL.js";import{a as P}from"./json-parse-rAW8WjEh.js";import{s as C}from"./sanitize-unicode-B62XMGgN.js";import{h as Y,b as V,i as Z,r as Q}from"./github-copilot-headers-CVWOSrQr.js";import{c as X}from"./openai-prompt-cache-h_V5PL7P.js";import{t as ee,b as te}from"./transform-messages-Bxe2XUr4.js";var b={};function ne(e){for(const n of e)if(n.role==="toolResult"||n.role==="assistant"&&n.content.some(t=>t.type==="toolCall"))return!0;return!1}function F(e){return e.type==="text"}function se(e){return e.type==="thinking"}function oe(e){return e.type==="toolCall"}function re(e){return e.type==="image"}function N(e){return e||(typeof process<"u"&&b.PI_CACHE_RETENTION==="long"?"long":"short")}const ae=(e,n,t)=>{const s=new $;return(async()=>{const r={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const c=t?.apiKey||L(e.provider)||"",u=q(e),o=N(t?.cacheRetention),i=o==="none"?void 0:t?.sessionId,p=ie(e,n,c,t?.headers,i,u);let h=le(e,n,t,u,o);const y=await t?.onPayload?.(h,e);y!==void 0&&(h=y);const R={...t?.signal?{signal:t.signal}:{},...t?.timeoutMs!==void 0?{timeout:t.timeoutMs}:{},...t?.maxRetries!==void 0?{maxRetries:t.maxRetries}:{}},{data:T,response:_}=await p.chat.completions.create(h,R).withResponse();await t?.onResponse?.({status:_.status,headers:J(_.headers)},e),s.push({type:"start",partial:r});let v=null,l=null,k=!1;const E=new Map,S=new Map,I=r.content,A=a=>I.indexOf(a),D=a=>{const f=A(a);f!==-1&&(a.type==="text"?s.push({type:"text_end",contentIndex:f,content:a.text,partial:r}):a.type==="thinking"?s.push({type:"thinking_end",contentIndex:f,content:a.thinking,partial:r}):a.type==="toolCall"&&(a.arguments=P(a.partialArgs),delete a.partialArgs,delete a.streamIndex,s.push({type:"toolcall_end",contentIndex:f,toolCall:a,partial:r})))},U=()=>(v||(v={type:"text",text:""},I.push(v),s.push({type:"text_start",contentIndex:A(v),partial:r})),v),H=a=>(l||(l={type:"thinking",thinking:"",thinkingSignature:a},I.push(l),s.push({type:"thinking_start",contentIndex:A(l),partial:r})),l),j=a=>{const f=typeof a.index=="number"?a.index:void 0;let d=f!==void 0?E.get(f):void 0;return!d&&a.id&&(d=S.get(a.id)),d||(d={type:"toolCall",id:a.id||"",name:a.function?.name||"",arguments:{},partialArgs:"",streamIndex:f},f!==void 0&&E.set(f,d),a.id&&S.set(a.id,d),I.push(d),s.push({type:"toolcall_start",contentIndex:A(d),partial:r})),f!==void 0&&d.streamIndex===void 0&&(d.streamIndex=f,E.set(f,d)),a.id&&S.set(a.id,d),d};for await(const a of T){if(!a||typeof a!="object")continue;r.responseId||=a.id,typeof a.model=="string"&&a.model.length>0&&a.model!==e.id&&(r.responseModel||=a.model),a.usage&&(r.usage=B(a.usage,e));const f=Array.isArray(a.choices)?a.choices[0]:void 0;if(f){if(!a.usage&&f.usage&&(r.usage=B(f.usage,e)),f.finish_reason){const d=ke(f.finish_reason);r.stopReason=d.stopReason,d.errorMessage&&(r.errorMessage=d.errorMessage),k=!0}if(f.delta){if(f.delta.content!==null&&f.delta.content!==void 0&&f.delta.content.length>0){const g=U();g.text+=f.delta.content,s.push({type:"text_delta",contentIndex:A(g),delta:f.delta.content,partial:r})}const d=["reasoning_content","reasoning","reasoning_text"],O=f.delta;let w=null;for(const g of d){const m=O[g];if(typeof m=="string"&&m.length>0){w=g;break}}if(w){const g=O[w];if(typeof g=="string"&&g.length>0){const m=e.provider==="opencode-go"&&w==="reasoning"?"reasoning_content":w,x=H(m);x.thinking+=g,s.push({type:"thinking_delta",contentIndex:A(x),delta:g,partial:r})}}if(f?.delta?.tool_calls)for(const g of f.delta.tool_calls){const m=j(g);!m.id&&g.id&&(m.id=g.id,S.set(g.id,m)),!m.name&&g.function?.name&&(m.name=g.function.name);let x="";g.function?.arguments&&(x=g.function.arguments,m.partialArgs=(m.partialArgs??"")+g.function.arguments,m.arguments=P(m.partialArgs)),s.push({type:"toolcall_delta",contentIndex:A(m),delta:x,partial:r})}const M=f.delta.reasoning_details;if(M&&Array.isArray(M)){for(const g of M)if(g.type==="reasoning.encrypted"&&g.id&&g.data){const m=r.content.find(x=>x.type==="toolCall"&&x.id===g.id);m&&(m.thoughtSignature=JSON.stringify(g))}}}}}for(const a of I)D(a);if(t?.signal?.aborted)throw new Error("Request was aborted");if(r.stopReason==="aborted")throw new Error("Request was aborted");if(r.stopReason==="error")throw new Error(r.errorMessage||"Provider returned an error stop reason");if(!k)throw new Error("Stream ended without finish_reason");s.push({type:"done",reason:r.stopReason,message:r}),s.end()}catch(c){for(const o of r.content)delete o.index,delete o.partialArgs,delete o.streamIndex;r.stopReason=t?.signal?.aborted?"aborted":"error",r.errorMessage=c instanceof Error?c.message:JSON.stringify(c);const u=c?.error?.metadata?.raw;u&&(r.errorMessage+=`
2
+ ${u}`),s.push({type:"error",reason:r.stopReason,error:r}),s.end()}})(),s},Se=(e,n,t)=>{const s=t?.apiKey||L(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const r=te(e,t,s),c=t?.reasoning?W(e,t.reasoning):void 0,u=c==="off"?void 0:c,o=t?.toolChoice;return ae(e,n,{...r,reasoningEffort:u,toolChoice:o})};function ie(e,n,t,s,r,c=q(e)){if(!t){if(!b.OPENAI_API_KEY)throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");t=b.OPENAI_API_KEY}const u={...e.headers};if(e.provider==="github-copilot"){const i=Y(n.messages),p=V({messages:n.messages,hasImages:i});Object.assign(u,p)}r&&c.sendSessionAffinityHeaders&&(u.session_id=r,u["x-client-request-id"]=r,u["x-session-affinity"]=r),s&&Object.assign(u,s);const o=e.provider==="cloudflare-ai-gateway"?{...u,Authorization:u.Authorization??null,"cf-aig-authorization":`Bearer ${t}`}:u;return new G({apiKey:t,baseURL:Z(e.provider)?Q(e):e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:o})}function le(e,n,t,s=q(e),r=N(t?.cacheRetention)){const c=me(e,n,s),u=ce(s,r),o={model:e.id,messages:c,stream:!0,prompt_cache_key:e.baseUrl.includes("api.openai.com")&&r!=="none"||r==="long"&&s.supportsLongCacheRetention?X(t?.sessionId):void 0,prompt_cache_retention:r==="long"&&s.supportsLongCacheRetention?"24h":void 0};if(s.supportsUsageInStreaming!==!1&&(o.stream_options={include_usage:!0}),s.supportsStore&&(o.store=!1),t?.maxTokens&&(s.maxTokensField==="max_tokens"?o.max_tokens=t.maxTokens:o.max_completion_tokens=t.maxTokens),t?.temperature!==void 0&&(o.temperature=t.temperature),n.tools&&n.tools.length>0?(o.tools=ye(n.tools,s),s.zaiToolStream&&(o.tool_stream=!0)):ne(n.messages)&&(o.tools=[]),u&&ue(c,o.tools,u),t?.toolChoice&&(o.tool_choice=t.toolChoice),s.thinkingFormat==="zai"&&e.reasoning)o.enable_thinking=!!t?.reasoningEffort;else if(s.thinkingFormat==="qwen"&&e.reasoning)o.enable_thinking=!!t?.reasoningEffort;else if(s.thinkingFormat==="qwen-chat-template"&&e.reasoning)o.chat_template_kwargs={enable_thinking:!!t?.reasoningEffort,preserve_thinking:!0};else if(s.thinkingFormat==="deepseek"&&e.reasoning)o.thinking={type:t?.reasoningEffort?"enabled":"disabled"},t?.reasoningEffort&&(o.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort);else if(s.thinkingFormat==="openrouter"&&e.reasoning){const i=o;t?.reasoningEffort?i.reasoning={effort:e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort}:e.thinkingLevelMap?.off!==null&&(i.reasoning={effort:e.thinkingLevelMap?.off??"none"})}else if(s.thinkingFormat==="together"&&e.reasoning){const i=o;i.reasoning={enabled:!!t?.reasoningEffort},t?.reasoningEffort&&s.supportsReasoningEffort&&(i.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort)}else if(t?.reasoningEffort&&e.reasoning&&s.supportsReasoningEffort)o.reasoning_effort=e.thinkingLevelMap?.[t.reasoningEffort]??t.reasoningEffort;else if(!t?.reasoningEffort&&e.reasoning&&s.supportsReasoningEffort){const i=e.thinkingLevelMap?.off;typeof i=="string"&&(o.reasoning_effort=i)}if(e.baseUrl.includes("openrouter.ai")&&e.compat?.openRouterRouting&&(o.provider=e.compat.openRouterRouting),e.baseUrl.includes("ai-gateway.vercel.sh")&&e.compat?.vercelGatewayRouting){const i=e.compat.vercelGatewayRouting;if(i.only||i.order){const p={};i.only&&(p.only=i.only),i.order&&(p.order=i.order),o.providerOptions={gateway:p}}}return o}function ce(e,n){if(e.cacheControlFormat!=="anthropic"||n==="none")return;const t=n==="long"&&e.supportsLongCacheRetention?"1h":void 0;return{type:"ephemeral",...t?{ttl:t}:{}}}function ue(e,n,t){fe(e,t),ge(n,t),pe(e,t)}function fe(e,n){for(const t of e)if(t.role==="system"||t.role==="developer"){he(t,n);return}}function pe(e,n){for(let t=e.length-1;t>=0;t--){const s=e[t];if((s.role==="user"||s.role==="assistant")&&de(s,n))return}}function ge(e,n){if(!e||e.length===0)return;const t=e[e.length-1];t.cache_control=n}function he(e,n){return z(e,n)}function de(e,n){return e.role==="user"||e.role==="assistant"?z(e,n):!1}function z(e,n){const t=e.content;if(typeof t=="string")return t.length===0?!1:(e.content=[{type:"text",text:t,cache_control:n}],!0);if(!Array.isArray(t))return!1;for(let s=t.length-1;s>=0;s--){const r=t[s];if(r?.type==="text"){const c=r;return c.cache_control=n,!0}}return!1}function me(e,n,t){const s=[],r=o=>{if(o.includes("|")){const[i]=o.split("|");return i.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,40)}return e.provider==="openai"&&o.length>40?o.slice(0,40):o},c=ee(n.messages,e,o=>r(o));if(n.systemPrompt){const i=e.reasoning&&t.supportsDeveloperRole?"developer":"system";s.push({role:i,content:C(n.systemPrompt)})}let u=null;for(let o=0;o<c.length;o++){const i=c[o];if(t.requiresAssistantAfterToolResult&&u==="toolResult"&&i.role==="user"&&s.push({role:"assistant",content:"I have processed the tool results."}),i.role==="user")if(typeof i.content=="string")s.push({role:"user",content:C(i.content)});else{const p=i.content.map(h=>h.type==="text"?{type:"text",text:C(h.text)}:{type:"image_url",image_url:{url:`data:${h.mimeType};base64,${h.data}`}});if(p.length===0)continue;s.push({role:"user",content:p})}else if(i.role==="assistant"){const p={role:"assistant",content:t.requiresAssistantAfterToolResult?"":null},h=i.content.filter(F).filter(l=>l.text.trim().length>0).map(l=>({type:"text",text:C(l.text)})),y=h.map(l=>l.text).join(""),R=i.content.filter(se).filter(l=>l.thinking.trim().length>0);if(R.length>0)if(t.requiresThinkingAsText){const l=R.map(k=>C(k.thinking)).join(`
3
+
4
+ `);p.content=[{type:"text",text:l},...h]}else{y.length>0&&(p.content=y);let l=R[0].thinkingSignature;e.provider==="opencode-go"&&l==="reasoning"&&(l="reasoning_content"),l&&l.length>0&&(p[l]=R.map(k=>k.thinking).join(`
5
+ `))}else y.length>0&&(p.content=y);const T=i.content.filter(oe);if(T.length>0){p.tool_calls=T.map(k=>({id:k.id,type:"function",function:{name:k.name,arguments:JSON.stringify(k.arguments)}}));const l=T.filter(k=>k.thoughtSignature).map(k=>{try{return JSON.parse(k.thoughtSignature)}catch{return null}}).filter(Boolean);l.length>0&&(p.reasoning_details=l)}t.requiresReasoningContentOnAssistantMessages&&e.reasoning&&p.reasoning_content===void 0&&(p.reasoning_content="");const _=p.content;if(!(_!=null&&_.length>0)&&!p.tool_calls)continue;s.push(p)}else if(i.role==="toolResult"){const p=[];let h=o;for(;h<c.length&&c[h].role==="toolResult";h++){const y=c[h],R=y.content.filter(F).map(l=>l.text).join(`
6
+ `),T=y.content.some(l=>l.type==="image"),_=R.length>0,v={role:"tool",content:C(_?R:"(see attached image)"),tool_call_id:y.toolCallId};if(t.requiresToolResultName&&y.toolName&&(v.name=y.toolName),s.push(v),T&&e.input.includes("image"))for(const l of y.content)re(l)&&p.push({type:"image_url",image_url:{url:`data:${l.mimeType};base64,${l.data}`}})}o=h-1,p.length>0?(t.requiresAssistantAfterToolResult&&s.push({role:"assistant",content:"I have processed the tool results."}),s.push({role:"user",content:[{type:"text",text:"Attached image(s) from tool result:"},...p]}),u="user"):u="toolResult";continue}u=i.role}return s}function ye(e,n){return e.map(t=>({type:"function",function:{name:t.name,description:t.description,parameters:t.parameters,...n.supportsStrictMode!==!1&&{strict:!1}}}))}function B(e,n){const t=e.prompt_tokens||0,s=e.prompt_tokens_details?.cached_tokens??e.prompt_cache_hit_tokens??0,r=e.prompt_tokens_details?.cache_write_tokens||0,c=Math.max(0,t-s-r),u=e.completion_tokens||0,o={input:c,output:u,cacheRead:s,cacheWrite:r,totalTokens:c+u+s+r,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}};return K(n,o),o}function ke(e){if(e===null)return{stopReason:"stop"};switch(e){case"stop":case"end":return{stopReason:"stop"};case"length":return{stopReason:"length"};case"function_call":case"tool_calls":return{stopReason:"toolUse"};case"content_filter":return{stopReason:"error",errorMessage:"Provider finish_reason: content_filter"};case"network_error":return{stopReason:"error",errorMessage:"Provider finish_reason: network_error"};default:return{stopReason:"error",errorMessage:`Provider finish_reason: ${e}`}}}function Re(e){const n=e.provider,t=e.baseUrl,s=n==="zai"||t.includes("api.z.ai"),r=n==="together"||t.includes("api.together.ai")||t.includes("api.together.xyz"),c=n==="moonshotai"||n==="moonshotai-cn"||t.includes("api.moonshot."),u=n==="cloudflare-workers-ai"||t.includes("api.cloudflare.com"),o=n==="cloudflare-ai-gateway"||t.includes("gateway.ai.cloudflare.com"),i=n==="cerebras"||t.includes("cerebras.ai")||n==="xai"||t.includes("api.x.ai")||r||t.includes("chutes.ai")||t.includes("deepseek.com")||s||c||n==="opencode"||t.includes("opencode.ai")||u||o,p=t.includes("chutes.ai")||c||o||r,h=n==="xai"||t.includes("api.x.ai"),y=n==="deepseek"||t.includes("deepseek.com"),R=n==="openrouter"&&e.id.startsWith("anthropic/")?"anthropic":void 0;return{supportsStore:!i,supportsDeveloperRole:!i,supportsReasoningEffort:!h&&!s&&!c&&!r&&!o,supportsUsageInStreaming:!0,maxTokensField:p?"max_tokens":"max_completion_tokens",requiresToolResultName:!1,requiresAssistantAfterToolResult:!1,requiresThinkingAsText:!1,requiresReasoningContentOnAssistantMessages:y,thinkingFormat:y?"deepseek":s?"zai":r?"together":n==="openrouter"||t.includes("openrouter.ai")?"openrouter":"openai",openRouterRouting:{},vercelGatewayRouting:{},zaiToolStream:!1,supportsStrictMode:!c&&!r&&!o,cacheControlFormat:R,sendSessionAffinityHeaders:!1,supportsLongCacheRetention:!(r||u||o)}}function q(e){const n=Re(e);return e.compat?{supportsStore:e.compat.supportsStore??n.supportsStore,supportsDeveloperRole:e.compat.supportsDeveloperRole??n.supportsDeveloperRole,supportsReasoningEffort:e.compat.supportsReasoningEffort??n.supportsReasoningEffort,supportsUsageInStreaming:e.compat.supportsUsageInStreaming??n.supportsUsageInStreaming,maxTokensField:e.compat.maxTokensField??n.maxTokensField,requiresToolResultName:e.compat.requiresToolResultName??n.requiresToolResultName,requiresAssistantAfterToolResult:e.compat.requiresAssistantAfterToolResult??n.requiresAssistantAfterToolResult,requiresThinkingAsText:e.compat.requiresThinkingAsText??n.requiresThinkingAsText,requiresReasoningContentOnAssistantMessages:e.compat.requiresReasoningContentOnAssistantMessages??n.requiresReasoningContentOnAssistantMessages,thinkingFormat:e.compat.thinkingFormat??n.thinkingFormat,openRouterRouting:e.compat.openRouterRouting??{},vercelGatewayRouting:e.compat.vercelGatewayRouting??n.vercelGatewayRouting,zaiToolStream:e.compat.zaiToolStream??n.zaiToolStream,supportsStrictMode:e.compat.supportsStrictMode??n.supportsStrictMode,cacheControlFormat:e.compat.cacheControlFormat??n.cacheControlFormat,sendSessionAffinityHeaders:e.compat.sendSessionAffinityHeaders??n.sendSessionAffinityHeaders,supportsLongCacheRetention:e.compat.supportsLongCacheRetention??n.supportsLongCacheRetention}:n}export{me as convertMessages,ae as streamOpenAICompletions,Se as streamSimpleOpenAICompletions};
dist/assets/openai-responses-BRdYfEKb.js ADDED
@@ -0,0 +1 @@
 
 
1
+ import{O as A}from"./client-Sq9UPDIz.js";import{A as I,g as h,a as E}from"./index-x-2U2tSW.js";import{h as w}from"./headers-CgnjaPPL.js";import{h as _,b as O,i as b,r as P}from"./github-copilot-headers-CVWOSrQr.js";import{c as S}from"./openai-prompt-cache-h_V5PL7P.js";import{p as C,c as T,a as k}from"./openai-responses-shared-Co2Tgb08.js";import{b as L}from"./transform-messages-Bxe2XUr4.js";import"./hash-DMDecQcg.js";import"./json-parse-rAW8WjEh.js";import"./sanitize-unicode-B62XMGgN.js";var u={};const M=new Set(["openai","openai-codex","opencode"]);function g(e){return e||(typeof process<"u"&&u.PI_CACHE_RETENTION==="long"?"long":"short")}function m(e){return{sendSessionIdHeader:e.compat?.sendSessionIdHeader??!0,supportsLongCacheRetention:e.compat?.supportsLongCacheRetention??!0}}function N(e,t){return t==="long"&&e.supportsLongCacheRetention?"24h":void 0}function x(e){if(e instanceof Error){const t=e.status,r=typeof t=="number"?t:void 0;return r!==void 0?`OpenAI API error (${r}): ${e.message}`:e.message}try{return JSON.stringify(e)}catch{return String(e)}}const H=(e,t,r)=>{const s=new I;return(async()=>{const a={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()};try{const o=r?.apiKey||h(e.provider)||"",i=g(r?.cacheRetention)==="none"?void 0:r?.sessionId,p=K(e,t,o,r?.headers,i);let c=q(e,t,r);const f=await r?.onPayload?.(c,e);f!==void 0&&(c=f);const l={...r?.signal?{signal:r.signal}:{},...r?.timeoutMs!==void 0?{timeout:r.timeoutMs}:{},...r?.maxRetries!==void 0?{maxRetries:r.maxRetries}:{}},{data:v,response:d}=await p.responses.create(c,l).withResponse();if(await r?.onResponse?.({status:d.status,headers:w(d.headers)},e),s.push({type:"start",partial:a}),await C(v,a,s,e,{serviceTier:r?.serviceTier,applyServiceTierPricing:(R,y)=>W(R,y,e)}),r?.signal?.aborted)throw new Error("Request was aborted");if(a.stopReason==="aborted"||a.stopReason==="error")throw new Error("An unknown error occurred");s.push({type:"done",reason:a.stopReason,message:a}),s.end()}catch(o){for(const n of a.content)delete n.index,delete n.partialJson;a.stopReason=r?.signal?.aborted?"aborted":"error",a.errorMessage=x(o),s.push({type:"error",reason:a.stopReason,error:a}),s.end()}})(),s},Q=(e,t,r)=>{const s=r?.apiKey||h(e.provider);if(!s)throw new Error(`No API key for provider: ${e.provider}`);const a=L(e,r,s),o=r?.reasoning?E(e,r.reasoning):void 0;return H(e,t,{...a,reasoningEffort:o==="off"?void 0:o})};function K(e,t,r,s,a){if(!r){if(!u.OPENAI_API_KEY)throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");r=u.OPENAI_API_KEY}const o=m(e),n={...e.headers};if(e.provider==="github-copilot"){const p=_(t.messages),c=O({messages:t.messages,hasImages:p});Object.assign(n,c)}a&&(o.sendSessionIdHeader&&(n.session_id=a),n["x-client-request-id"]=a),s&&Object.assign(n,s);const i=e.provider==="cloudflare-ai-gateway"?{...n,Authorization:n.Authorization??null,"cf-aig-authorization":`Bearer ${r}`}:n;return new A({apiKey:r,baseURL:b(e.provider)?P(e):e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:i})}function q(e,t,r){const s=T(e,t,M),a=g(r?.cacheRetention),o=m(e),n={model:e.id,input:s,stream:!0,prompt_cache_key:a==="none"?void 0:S(r?.sessionId),prompt_cache_retention:N(o,a),store:!1};if(r?.maxTokens&&(n.max_output_tokens=r?.maxTokens),r?.temperature!==void 0&&(n.temperature=r?.temperature),r?.serviceTier!==void 0&&(n.service_tier=r.serviceTier),t.tools&&t.tools.length>0&&(n.tools=k(t.tools)),e.reasoning)if(r?.reasoningEffort||r?.reasoningSummary){const i=r?.reasoningEffort?e.thinkingLevelMap?.[r.reasoningEffort]??r.reasoningEffort:"medium";n.reasoning={effort:i,summary:r?.reasoningSummary||"auto"},n.include=["reasoning.encrypted_content"]}else e.provider!=="github-copilot"&&e.thinkingLevelMap?.off!==null&&(n.reasoning={effort:e.thinkingLevelMap?.off??"none"});return n}function B(e,t){switch(t){case"flex":return .5;case"priority":return e.id==="gpt-5.5"?2.5:2;default:return 1}}function W(e,t,r){const s=B(r,t);s!==1&&(e.cost.input*=s,e.cost.output*=s,e.cost.cacheRead*=s,e.cost.cacheWrite*=s,e.cost.total=e.cost.input+e.cost.output+e.cost.cacheRead+e.cost.cacheWrite)}export{H as streamOpenAIResponses,Q as streamSimpleOpenAIResponses};
dist/assets/openai-responses-shared-Co2Tgb08.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import{c as S}from"./index-x-2U2tSW.js";import{s as v}from"./hash-DMDecQcg.js";import{a as x}from"./json-parse-rAW8WjEh.js";import{s as _}from"./sanitize-unicode-B62XMGgN.js";import{t as $}from"./transform-messages-Bxe2XUr4.js";function T(r,t){const l={v:1,id:r};return t&&(l.phase=t),JSON.stringify(l)}function C(r){if(r){if(r.startsWith("{"))try{const t=JSON.parse(r);if(t.v===1&&typeof t.id=="string")return t.phase==="commentary"||t.phase==="final_answer"?{id:t.id,phase:t.phase}:{id:t.id}}catch{}return{id:r}}}function M(r,t,l,y){const m=[],i=s=>{const o=s.replace(/[^a-zA-Z0-9_-]/g,"_");return(o.length>64?o.slice(0,64):o).replace(/_+$/,"")},n=s=>{const o=`fc_${v(s)}`;return o.length>64?o.slice(0,64):o},k=(s,o,p)=>{if(!l.has(r.provider)||!s.includes("|"))return i(s);const[h,g]=s.split("|"),f=i(h);let c=p.provider!==r.provider||p.api!==r.api?n(g):i(g);return c.startsWith("fc_")||(c=i(`fc_${c}`)),`${f}|${c}`},d=$(t.messages,r,k);if((y?.includeSystemPrompt??!0)&&t.systemPrompt){const s=r.reasoning?"developer":"system";m.push({role:s,content:_(t.systemPrompt)})}let e=0;for(const s of d){if(s.role==="user")if(typeof s.content=="string")m.push({role:"user",content:[{type:"input_text",text:_(s.content)}]});else{const o=s.content.map(p=>p.type==="text"?{type:"input_text",text:_(p.text)}:{type:"input_image",detail:"auto",image_url:`data:${p.mimeType};base64,${p.data}`});if(o.length===0)continue;m.push({role:"user",content:o})}else if(s.role==="assistant"){const o=[],p=s,h=p.model!==r.id&&p.provider===r.provider&&p.api===r.api;for(const g of s.content)if(g.type==="thinking"){if(g.thinkingSignature){const f=JSON.parse(g.thinkingSignature);o.push(f)}}else if(g.type==="text"){const f=g,u=C(f.textSignature);let c=u?.id;c?c.length>64&&(c=`msg_${v(c)}`):c=`msg_${e}`,o.push({type:"message",role:"assistant",content:[{type:"output_text",text:_(f.text),annotations:[]}],status:"completed",id:c,phase:u?.phase})}else if(g.type==="toolCall"){const f=g,[u,c]=f.id.split("|");let I=c;h&&I?.startsWith("fc_")&&(I=void 0),o.push({type:"function_call",id:I,call_id:u,name:f.name,arguments:JSON.stringify(f.arguments)})}if(o.length===0)continue;m.push(...o)}else if(s.role==="toolResult"){const o=s.content.filter(u=>u.type==="text").map(u=>u.text).join(`
2
+ `),p=s.content.some(u=>u.type==="image"),h=o.length>0,[g]=s.toolCallId.split("|");let f;if(p&&r.input.includes("image")){const u=[];h&&u.push({type:"input_text",text:_(o)});for(const c of s.content)c.type==="image"&&u.push({type:"input_image",detail:"auto",image_url:`data:${c.mimeType};base64,${c.data}`});f=u}else f=_(h?o:"(see attached image)");m.push({type:"function_call_output",call_id:g,output:f})}e++}return m}function W(r,t){const l=t?.strict===void 0?!1:t.strict;return r.map(y=>({type:"function",name:y.name,description:y.description,parameters:y.parameters,strict:l}))}async function N(r,t,l,y,m){let i=null,n=null;const k=t.content,d=()=>k.length-1;for await(const a of r)if(a.type==="response.created")t.responseId=a.response.id;else if(a.type==="response.output_item.added"){const e=a.item;e.type==="reasoning"?(i=e,n={type:"thinking",thinking:""},t.content.push(n),l.push({type:"thinking_start",contentIndex:d(),partial:t})):e.type==="message"?(i=e,n={type:"text",text:""},t.content.push(n),l.push({type:"text_start",contentIndex:d(),partial:t})):e.type==="function_call"&&(i=e,n={type:"toolCall",id:`${e.call_id}|${e.id}`,name:e.name,arguments:{},partialJson:e.arguments||""},t.content.push(n),l.push({type:"toolcall_start",contentIndex:d(),partial:t}))}else if(a.type==="response.reasoning_summary_part.added")i&&i.type==="reasoning"&&(i.summary=i.summary||[],i.summary.push(a.part));else if(a.type==="response.reasoning_summary_text.delta"){if(i?.type==="reasoning"&&n?.type==="thinking"){i.summary=i.summary||[];const e=i.summary[i.summary.length-1];e&&(n.thinking+=a.delta,e.text+=a.delta,l.push({type:"thinking_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.reasoning_summary_part.done"){if(i?.type==="reasoning"&&n?.type==="thinking"){i.summary=i.summary||[];const e=i.summary[i.summary.length-1];e&&(n.thinking+=`
3
+
4
+ `,e.text+=`
5
+
6
+ `,l.push({type:"thinking_delta",contentIndex:d(),delta:`
7
+
8
+ `,partial:t}))}}else if(a.type==="response.reasoning_text.delta")i?.type==="reasoning"&&n?.type==="thinking"&&(n.thinking+=a.delta,l.push({type:"thinking_delta",contentIndex:d(),delta:a.delta,partial:t}));else if(a.type==="response.content_part.added")i?.type==="message"&&(i.content=i.content||[],(a.part.type==="output_text"||a.part.type==="refusal")&&i.content.push(a.part));else if(a.type==="response.output_text.delta"){if(i?.type==="message"&&n?.type==="text"){if(!i.content||i.content.length===0)continue;const e=i.content[i.content.length-1];e?.type==="output_text"&&(n.text+=a.delta,e.text+=a.delta,l.push({type:"text_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.refusal.delta"){if(i?.type==="message"&&n?.type==="text"){if(!i.content||i.content.length===0)continue;const e=i.content[i.content.length-1];e?.type==="refusal"&&(n.text+=a.delta,e.refusal+=a.delta,l.push({type:"text_delta",contentIndex:d(),delta:a.delta,partial:t}))}}else if(a.type==="response.function_call_arguments.delta")i?.type==="function_call"&&n?.type==="toolCall"&&(n.partialJson+=a.delta,n.arguments=x(n.partialJson),l.push({type:"toolcall_delta",contentIndex:d(),delta:a.delta,partial:t}));else if(a.type==="response.function_call_arguments.done"){if(i?.type==="function_call"&&n?.type==="toolCall"){const e=n.partialJson;if(n.partialJson=a.arguments,n.arguments=x(n.partialJson),a.arguments.startsWith(e)){const s=a.arguments.slice(e.length);s.length>0&&l.push({type:"toolcall_delta",contentIndex:d(),delta:s,partial:t})}}}else if(a.type==="response.output_item.done"){const e=a.item;if(e.type==="reasoning"&&n?.type==="thinking"){const s=e.summary?.map(p=>p.text).join(`
9
+
10
+ `)||"",o=e.content?.map(p=>p.text).join(`
11
+
12
+ `)||"";n.thinking=s||o||n.thinking,n.thinkingSignature=JSON.stringify(e),l.push({type:"thinking_end",contentIndex:d(),content:n.thinking,partial:t}),n=null}else if(e.type==="message"&&n?.type==="text")n.text=e.content.map(s=>s.type==="output_text"?s.text:s.refusal).join(""),n.textSignature=T(e.id,e.phase??void 0),l.push({type:"text_end",contentIndex:d(),content:n.text,partial:t}),n=null;else if(e.type==="function_call"){const s=n?.type==="toolCall"&&n.partialJson?x(n.partialJson):x(e.arguments||"{}");let o;n?.type==="toolCall"?(n.arguments=s,delete n.partialJson,o=n):o={type:"toolCall",id:`${e.call_id}|${e.id}`,name:e.name,arguments:s},n=null,l.push({type:"toolcall_end",contentIndex:d(),toolCall:o,partial:t})}}else if(a.type==="response.completed"){const e=a.response;if(e?.id&&(t.responseId=e.id),e?.usage){const s=e.usage.input_tokens_details?.cached_tokens||0;t.usage={input:(e.usage.input_tokens||0)-s,output:e.usage.output_tokens||0,cacheRead:s,cacheWrite:0,totalTokens:e.usage.total_tokens||0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}}}if(S(y,t.usage),m?.applyServiceTierPricing){const s=m.resolveServiceTier?m.resolveServiceTier(e?.service_tier,m.serviceTier):e?.service_tier??m.serviceTier;m.applyServiceTierPricing(t.usage,s)}t.stopReason=J(e?.status),t.content.some(s=>s.type==="toolCall")&&t.stopReason==="stop"&&(t.stopReason="toolUse")}else{if(a.type==="error")throw new Error(`Error Code ${a.code}: ${a.message}`||"Unknown error");if(a.type==="response.failed"){const e=a.response?.error,s=a.response?.incomplete_details,o=e?`${e.code||"unknown"}: ${e.message||"no message"}`:s?.reason?`incomplete: ${s.reason}`:"Unknown error (no error details in response)";throw new Error(o)}}}function J(r){if(!r)return"stop";switch(r){case"completed":return"stop";case"incomplete":return"length";case"failed":case"cancelled":return"error";case"in_progress":case"queued":return"stop";default:{const t=r;throw new Error(`Unhandled stop reason: ${t}`)}}}export{W as a,M as c,N as p};
dist/assets/openrouter-BX3fS61e.js ADDED
@@ -0,0 +1 @@
 
 
1
+ import{O as y}from"./client-Sq9UPDIz.js";import{g as R}from"./index-x-2U2tSW.js";import{h as x}from"./headers-CgnjaPPL.js";import{s as _}from"./sanitize-unicode-B62XMGgN.js";const O=async(e,r,t)=>{const a={api:e.api,provider:e.provider,model:e.id,output:[],stopReason:"stop",timestamp:Date.now()};try{const s=t?.apiKey||R(e.provider);if(!s)throw new Error(`No API key available for provider: ${e.provider}`);const c=k(e,s,t?.headers);let n=w(e,r);const i=await t?.onPayload?.(n,e);i!==void 0&&(n=i);const o={...t?.signal?{signal:t.signal}:{},...t?.timeoutMs!==void 0?{timeout:t.timeoutMs}:{},...t?.maxRetries!==void 0?{maxRetries:t.maxRetries}:{}},{data:l,response:h}=await c.chat.completions.create(n,o).withResponse();await t?.onResponse?.({status:h.status,headers:x(h.headers)},e);const u=l;a.responseId=u.id,u.usage&&(a.usage=b(u.usage,e));const p=u.choices[0];if(p){const m=p.message.content;typeof m=="string"&&m.length>0&&a.output.push({type:"text",text:m});for(const d of p.message.images??[]){const f=typeof d.image_url=="string"?d.image_url:d.image_url?.url;if(!f?.startsWith("data:"))continue;const g=f.match(/^data:([^;]+);base64,(.+)$/);g&&a.output.push({type:"image",mimeType:g[1],data:g[2]})}}return a}catch(s){return a.stopReason=t?.signal?.aborted?"aborted":"error",a.errorMessage=s instanceof Error?s.message:JSON.stringify(s),a}};function k(e,r,t){return new y({apiKey:r,baseURL:e.baseUrl,dangerouslyAllowBrowser:!0,defaultHeaders:{...e.headers,...t}})}function w(e,r){const t=r.input.map(a=>a.type==="text"?{type:"text",text:_(a.text)}:{type:"image_url",image_url:{url:`data:${a.mimeType};base64,${a.data}`}});return{model:e.id,messages:[{role:"user",content:t}],stream:!1,modalities:e.output.includes("text")?["image","text"]:["image"]}}function b(e,r){const t=e.prompt_tokens||0,a=e.prompt_tokens_details?.cached_tokens||0,s=e.prompt_tokens_details?.cache_write_tokens||0,c=s>0?Math.max(0,a-s):a,n=Math.max(0,t-c-s),i=e.completion_tokens||0,o={input:n,output:i,cacheRead:c,cacheWrite:s,totalTokens:n+i+c+s,cost:{input:r.cost.input/1e6*n,output:r.cost.output/1e6*i,cacheRead:r.cost.cacheRead/1e6*c,cacheWrite:r.cost.cacheWrite/1e6*s,total:0}};return o.cost.total=o.cost.input+o.cost.output+o.cost.cacheRead+o.cost.cacheWrite,o}export{O as generateImagesOpenRouter};
dist/index.html CHANGED
@@ -4,12 +4,12 @@
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>MiniCPM5 Pi Web Agent</title>
7
- <script type="module" crossorigin src="/assets/index-BCicI0Zo.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-CRS_ZGq-.css">
9
  </head>
10
  <body>
11
  <main id="app">
12
- <section class="shell">
13
  <header class="topbar">
14
  <div>
15
  <h1>Pi Web Agent</h1>
@@ -22,61 +22,91 @@
22
  </div>
23
  </header>
24
 
25
- <section class="controls" aria-label="Agent controls">
26
- <label>
27
- Model
28
- <select id="mode">
29
- <option value="local">MiniCPM5 q4</option>
30
- <option value="mock">Deterministic test</option>
31
- </select>
32
- </label>
33
- <label>
34
- Device
35
- <select id="device">
36
- <option value="webgpu">WebGPU</option>
37
- <option value="wasm">WASM</option>
38
- </select>
39
- </label>
40
- <label>
41
- Max tokens
42
- <input id="max-new-tokens" type="number" min="16" max="512" step="1" value="160" />
43
- </label>
44
- <label>
45
- Temperature
46
- <input id="temperature" type="number" min="0" max="1.5" step="0.05" value="0" />
47
- </label>
48
- </section>
49
-
50
- <section class="command-row" aria-label="Sandbox actions">
51
- <button id="boot-sandbox" type="button">Boot Sandbox</button>
52
- <button id="reset-sandbox" type="button">Reset</button>
53
- <button id="demo-prompt" type="button">Demo Prompt</button>
54
- </section>
55
 
56
- <section class="workspace">
57
- <div class="prompt-pane">
58
- <label class="prompt-label" for="prompt">Task</label>
59
- <textarea id="prompt" spellcheck="true">Create hello.js that prints the result of 21 * 2, run it with Node, and tell me the command output.</textarea>
60
- <button id="run" type="button">Run Agent</button>
61
- </div>
 
 
62
 
63
- <div class="panes">
64
- <section class="pane">
65
- <h2>Transcript</h2>
66
- <pre id="transcript"></pre>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  </section>
68
- <section class="pane">
69
- <h2>Sandbox Files</h2>
 
 
 
 
 
70
  <pre id="files"></pre>
71
  </section>
72
- <section class="pane wide">
 
73
  <h2>Events</h2>
74
  <pre id="event-log"></pre>
75
  </section>
76
- </div>
77
  </section>
78
  </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </main>
80
  </body>
81
  </html>
82
-
 
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>MiniCPM5 Pi Web Agent</title>
7
+ <script type="module" crossorigin src="/assets/index-x-2U2tSW.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-4NYd9uAS.css">
9
  </head>
10
  <body>
11
  <main id="app">
12
+ <section class="app-shell">
13
  <header class="topbar">
14
  <div>
15
  <h1>Pi Web Agent</h1>
 
22
  </div>
23
  </header>
24
 
25
+ <section class="layout">
26
+ <section class="chat-panel" aria-label="Conversation">
27
+ <div class="chat-scroll" id="chat"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ <form class="composer" id="composer">
30
+ <textarea id="prompt" spellcheck="true" rows="2">Create hello.js, run 21 * 2 with Node, and report the output.</textarea>
31
+ <div class="composer-actions">
32
+ <button id="demo-prompt" type="button">Demo</button>
33
+ <button id="send" type="submit">Send</button>
34
+ </div>
35
+ </form>
36
+ </section>
37
 
38
+ <aside class="side-panel" aria-label="Agent controls and sandbox">
39
+ <section class="control-group">
40
+ <h2>Runtime</h2>
41
+ <label>
42
+ Model
43
+ <select id="mode">
44
+ <option value="local">MiniCPM5 q4</option>
45
+ <option value="mock">Deterministic test</option>
46
+ </select>
47
+ </label>
48
+ <label>
49
+ Device
50
+ <select id="device">
51
+ <option value="webgpu">WebGPU</option>
52
+ <option value="wasm">WASM</option>
53
+ </select>
54
+ </label>
55
+ <div class="compact-grid">
56
+ <label>
57
+ Max tokens
58
+ <input id="max-new-tokens" type="number" min="16" max="512" step="1" value="160" />
59
+ </label>
60
+ <label>
61
+ Temperature
62
+ <input id="temperature" type="number" min="0" max="1.5" step="0.05" value="0" />
63
+ </label>
64
+ </div>
65
+ <button id="load-model" type="button">Download Model</button>
66
  </section>
67
+
68
+ <section class="control-group">
69
+ <h2>Sandbox</h2>
70
+ <div class="button-row">
71
+ <button id="boot-sandbox" type="button">Boot</button>
72
+ <button id="reset-sandbox" type="button">Reset</button>
73
+ </div>
74
  <pre id="files"></pre>
75
  </section>
76
+
77
+ <section class="control-group">
78
  <h2>Events</h2>
79
  <pre id="event-log"></pre>
80
  </section>
81
+ </aside>
82
  </section>
83
  </section>
84
+
85
+ <section class="model-gate" id="model-gate" aria-labelledby="model-gate-title" aria-modal="true" role="dialog">
86
+ <div class="model-dialog">
87
+ <div>
88
+ <p class="eyebrow">Local model setup</p>
89
+ <h2 id="model-gate-title">Download MiniCPM5 to this browser?</h2>
90
+ <p class="dialog-copy">
91
+ The agent can run fully locally with the q4 ONNX model in Transformers.js. The download is large and stays in the browser cache when possible.
92
+ </p>
93
+ </div>
94
+ <div class="dialog-actions">
95
+ <button id="confirm-load-model" type="button">Download MiniCPM5</button>
96
+ <button id="use-test-model" type="button">Use Test Model</button>
97
+ </div>
98
+ <div class="dialog-options">
99
+ <label>
100
+ Device
101
+ <select id="gate-device">
102
+ <option value="webgpu">WebGPU</option>
103
+ <option value="wasm">WASM</option>
104
+ </select>
105
+ </label>
106
+ <p id="gate-status">Ready.</p>
107
+ </div>
108
+ </div>
109
+ </section>
110
  </main>
111
  </body>
112
  </html>
 
index.html CHANGED
@@ -7,7 +7,7 @@
7
  </head>
8
  <body>
9
  <main id="app">
10
- <section class="shell">
11
  <header class="topbar">
12
  <div>
13
  <h1>Pi Web Agent</h1>
@@ -20,62 +20,92 @@
20
  </div>
21
  </header>
22
 
23
- <section class="controls" aria-label="Agent controls">
24
- <label>
25
- Model
26
- <select id="mode">
27
- <option value="local">MiniCPM5 q4</option>
28
- <option value="mock">Deterministic test</option>
29
- </select>
30
- </label>
31
- <label>
32
- Device
33
- <select id="device">
34
- <option value="webgpu">WebGPU</option>
35
- <option value="wasm">WASM</option>
36
- </select>
37
- </label>
38
- <label>
39
- Max tokens
40
- <input id="max-new-tokens" type="number" min="16" max="512" step="1" value="160" />
41
- </label>
42
- <label>
43
- Temperature
44
- <input id="temperature" type="number" min="0" max="1.5" step="0.05" value="0" />
45
- </label>
46
- </section>
47
-
48
- <section class="command-row" aria-label="Sandbox actions">
49
- <button id="boot-sandbox" type="button">Boot Sandbox</button>
50
- <button id="reset-sandbox" type="button">Reset</button>
51
- <button id="demo-prompt" type="button">Demo Prompt</button>
52
- </section>
53
 
54
- <section class="workspace">
55
- <div class="prompt-pane">
56
- <label class="prompt-label" for="prompt">Task</label>
57
- <textarea id="prompt" spellcheck="true">Create hello.js that prints the result of 21 * 2, run it with Node, and tell me the command output.</textarea>
58
- <button id="run" type="button">Run Agent</button>
59
- </div>
 
 
60
 
61
- <div class="panes">
62
- <section class="pane">
63
- <h2>Transcript</h2>
64
- <pre id="transcript"></pre>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  </section>
66
- <section class="pane">
67
- <h2>Sandbox Files</h2>
 
 
 
 
 
68
  <pre id="files"></pre>
69
  </section>
70
- <section class="pane wide">
 
71
  <h2>Events</h2>
72
  <pre id="event-log"></pre>
73
  </section>
74
- </div>
75
  </section>
76
  </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  </main>
78
  <script type="module" src="/src/main.js"></script>
79
  </body>
80
  </html>
81
-
 
7
  </head>
8
  <body>
9
  <main id="app">
10
+ <section class="app-shell">
11
  <header class="topbar">
12
  <div>
13
  <h1>Pi Web Agent</h1>
 
20
  </div>
21
  </header>
22
 
23
+ <section class="layout">
24
+ <section class="chat-panel" aria-label="Conversation">
25
+ <div class="chat-scroll" id="chat"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ <form class="composer" id="composer">
28
+ <textarea id="prompt" spellcheck="true" rows="2">Create hello.js, run 21 * 2 with Node, and report the output.</textarea>
29
+ <div class="composer-actions">
30
+ <button id="demo-prompt" type="button">Demo</button>
31
+ <button id="send" type="submit">Send</button>
32
+ </div>
33
+ </form>
34
+ </section>
35
 
36
+ <aside class="side-panel" aria-label="Agent controls and sandbox">
37
+ <section class="control-group">
38
+ <h2>Runtime</h2>
39
+ <label>
40
+ Model
41
+ <select id="mode">
42
+ <option value="local">MiniCPM5 q4</option>
43
+ <option value="mock">Deterministic test</option>
44
+ </select>
45
+ </label>
46
+ <label>
47
+ Device
48
+ <select id="device">
49
+ <option value="webgpu">WebGPU</option>
50
+ <option value="wasm">WASM</option>
51
+ </select>
52
+ </label>
53
+ <div class="compact-grid">
54
+ <label>
55
+ Max tokens
56
+ <input id="max-new-tokens" type="number" min="16" max="512" step="1" value="160" />
57
+ </label>
58
+ <label>
59
+ Temperature
60
+ <input id="temperature" type="number" min="0" max="1.5" step="0.05" value="0" />
61
+ </label>
62
+ </div>
63
+ <button id="load-model" type="button">Download Model</button>
64
  </section>
65
+
66
+ <section class="control-group">
67
+ <h2>Sandbox</h2>
68
+ <div class="button-row">
69
+ <button id="boot-sandbox" type="button">Boot</button>
70
+ <button id="reset-sandbox" type="button">Reset</button>
71
+ </div>
72
  <pre id="files"></pre>
73
  </section>
74
+
75
+ <section class="control-group">
76
  <h2>Events</h2>
77
  <pre id="event-log"></pre>
78
  </section>
79
+ </aside>
80
  </section>
81
  </section>
82
+
83
+ <section class="model-gate" id="model-gate" aria-labelledby="model-gate-title" aria-modal="true" role="dialog">
84
+ <div class="model-dialog">
85
+ <div>
86
+ <p class="eyebrow">Local model setup</p>
87
+ <h2 id="model-gate-title">Download MiniCPM5 to this browser?</h2>
88
+ <p class="dialog-copy">
89
+ The agent can run fully locally with the q4 ONNX model in Transformers.js. The download is large and stays in the browser cache when possible.
90
+ </p>
91
+ </div>
92
+ <div class="dialog-actions">
93
+ <button id="confirm-load-model" type="button">Download MiniCPM5</button>
94
+ <button id="use-test-model" type="button">Use Test Model</button>
95
+ </div>
96
+ <div class="dialog-options">
97
+ <label>
98
+ Device
99
+ <select id="gate-device">
100
+ <option value="webgpu">WebGPU</option>
101
+ <option value="wasm">WASM</option>
102
+ </select>
103
+ </label>
104
+ <p id="gate-status">Ready.</p>
105
+ </div>
106
+ </div>
107
+ </section>
108
  </main>
109
  <script type="module" src="/src/main.js"></script>
110
  </body>
111
  </html>
 
scripts/smoke_local_model_web_agent.mjs CHANGED
@@ -28,21 +28,31 @@ try {
28
 
29
  await page.fill("#max-new-tokens", "1");
30
  await page.fill("#temperature", "0");
31
- await page.click("#run");
 
 
 
 
 
 
32
  await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Agent running", null, {
33
  timeout: 120000,
34
  });
35
  await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Ready", null, {
36
  timeout: 600000,
37
  });
 
 
 
38
 
39
- const transcript = await page.textContent("#transcript");
 
40
  const files = await page.textContent("#files");
41
  const events = await page.textContent("#event-log");
42
  const modelStatus = await page.textContent("#model-status");
43
 
44
- if (!transcript?.includes("pi sandbox result: 42")) {
45
- throw new Error(`Expected command output in transcript.\n\nTranscript:\n${transcript}\n\nEvents:\n${events}`);
46
  }
47
  if (!files?.includes("hello.js")) {
48
  throw new Error(`Expected hello.js in file listing.\n\nFiles:\n${files}`);
 
28
 
29
  await page.fill("#max-new-tokens", "1");
30
  await page.fill("#temperature", "0");
31
+ await page.selectOption("#gate-device", "wasm");
32
+ await page.click("#confirm-load-model");
33
+ await page.waitForFunction(() => window.__piWebAgent?.modelReady === true, null, {
34
+ timeout: 600000,
35
+ });
36
+
37
+ await page.click("#send");
38
  await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Agent running", null, {
39
  timeout: 120000,
40
  });
41
  await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Ready", null, {
42
  timeout: 600000,
43
  });
44
+ await page.waitForFunction(() => window.__piWebAgent?.transcript?.includes("pi sandbox result: 42"), null, {
45
+ timeout: 120000,
46
+ });
47
 
48
+ const transcript = await page.evaluate(() => window.__piWebAgent?.transcript || "");
49
+ const chatText = await page.textContent("#chat");
50
  const files = await page.textContent("#files");
51
  const events = await page.textContent("#event-log");
52
  const modelStatus = await page.textContent("#model-status");
53
 
54
+ if (!transcript.includes("pi sandbox result: 42") || !chatText?.includes("pi sandbox result: 42")) {
55
+ throw new Error(`Expected command output in chat transcript.\n\nTranscript:\n${transcript}\n\nChat:\n${chatText}\n\nEvents:\n${events}`);
56
  }
57
  if (!files?.includes("hello.js")) {
58
  throw new Error(`Expected hello.js in file listing.\n\nFiles:\n${files}`);
scripts/smoke_web_agent.mjs CHANGED
@@ -27,21 +27,23 @@ try {
27
  throw new Error("Page is not cross-origin isolated.");
28
  }
29
 
30
- await page.click("#boot-sandbox");
31
- await page.waitForFunction(() => document.querySelector("#sandbox-status")?.textContent === "Sandbox ready");
32
- await page.waitForFunction(() => document.querySelector("#files")?.textContent?.includes("hello.js"));
33
-
34
- await page.click("#run");
35
- await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Ready", null, {
36
  timeout: 120000,
37
  });
 
38
 
39
- const transcript = await page.textContent("#transcript");
 
40
  const files = await page.textContent("#files");
41
  const events = await page.textContent("#event-log");
42
 
43
- if (!transcript?.includes("pi sandbox result: 42")) {
44
- throw new Error(`Expected command output in transcript.\n\nTranscript:\n${transcript}\n\nEvents:\n${events}`);
45
  }
46
  if (!files?.includes("hello.js")) {
47
  throw new Error(`Expected hello.js in file listing.\n\nFiles:\n${files}`);
@@ -57,4 +59,3 @@ try {
57
  } finally {
58
  await browser.close();
59
  }
60
-
 
27
  throw new Error("Page is not cross-origin isolated.");
28
  }
29
 
30
+ await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Ready");
31
+ await page.click("#send");
32
+ await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Agent running", null, {
33
+ timeout: 120000,
34
+ });
35
+ await page.waitForFunction(() => window.__piWebAgent?.transcript?.includes("pi sandbox result: 42"), null, {
36
  timeout: 120000,
37
  });
38
+ await page.waitForFunction(() => document.querySelector("#status")?.textContent === "Ready");
39
 
40
+ const transcript = await page.evaluate(() => window.__piWebAgent?.transcript || "");
41
+ const chatText = await page.textContent("#chat");
42
  const files = await page.textContent("#files");
43
  const events = await page.textContent("#event-log");
44
 
45
+ if (!transcript.includes("pi sandbox result: 42") || !chatText?.includes("pi sandbox result: 42")) {
46
+ throw new Error(`Expected command output in chat transcript.\n\nTranscript:\n${transcript}\n\nChat:\n${chatText}\n\nEvents:\n${events}`);
47
  }
48
  if (!files?.includes("hello.js")) {
49
  throw new Error(`Expected hello.js in file listing.\n\nFiles:\n${files}`);
 
59
  } finally {
60
  await browser.close();
61
  }
 
src/main.js CHANGED
@@ -6,27 +6,42 @@ const nodes = {
6
  status: document.querySelector("#status"),
7
  modelStatus: document.querySelector("#model-status"),
8
  sandboxStatus: document.querySelector("#sandbox-status"),
9
- transcript: document.querySelector("#transcript"),
10
  eventLog: document.querySelector("#event-log"),
11
  files: document.querySelector("#files"),
12
  prompt: document.querySelector("#prompt"),
13
- run: document.querySelector("#run"),
 
14
  boot: document.querySelector("#boot-sandbox"),
15
  reset: document.querySelector("#reset-sandbox"),
16
  demo: document.querySelector("#demo-prompt"),
17
  mode: document.querySelector("#mode"),
18
  device: document.querySelector("#device"),
 
19
  maxTokens: document.querySelector("#max-new-tokens"),
20
  temperature: document.querySelector("#temperature"),
21
  modelLabel: document.querySelector("#model-label"),
 
 
 
 
 
22
  };
23
 
24
- nodes.modelLabel.textContent = MODEL_ID;
25
- if (!navigator.gpu) nodes.device.value = "wasm";
26
-
27
  const params = new URLSearchParams(window.location.search);
 
 
 
 
 
 
 
 
 
 
 
 
28
  if (params.get("mode") === "mock") nodes.mode.value = "mock";
29
- if (params.get("device")) nodes.device.value = params.get("device");
30
 
31
  const sandbox = createSandbox({
32
  onStatus: (text) => setSandboxStatus(text),
@@ -56,6 +71,13 @@ function setModelStatus(text) {
56
  nodes.modelStatus.textContent = text;
57
  }
58
 
 
 
 
 
 
 
 
59
  function logEvent(kind, text) {
60
  const line = `[${new Date().toLocaleTimeString()}] ${kind}: ${text}`;
61
  nodes.eventLog.textContent = `${nodes.eventLog.textContent}${line}\n`;
@@ -75,10 +97,11 @@ function createAgent() {
75
  switch (event.type) {
76
  case "agent_start":
77
  setStatus("Agent running");
 
78
  logEvent("agent", "start");
79
  break;
80
  case "message_end":
81
- renderTranscript();
82
  break;
83
  case "tool_execution_start":
84
  logEvent("tool", `${event.toolName} started`);
@@ -88,7 +111,8 @@ function createAgent() {
88
  break;
89
  case "agent_end":
90
  setStatus("Ready");
91
- renderTranscript();
 
92
  refreshFiles().catch((error) => logEvent("files", error.message));
93
  break;
94
  default:
@@ -101,28 +125,67 @@ function createAgent() {
101
  function resetAgent() {
102
  agent.abort();
103
  agent = createAgent();
104
- renderTranscript();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
 
107
- function renderTranscript() {
108
- const rendered = agent.state.messages
 
 
109
  .map((message) => {
110
- if (message.role === "toolResult") {
111
- return `TOOL ${message.toolName}${message.isError ? " ERROR" : ""}\n${textFromContent(message.content)}`;
112
- }
113
- const heading = message.role.toUpperCase();
114
- const toolCalls =
115
- message.role === "assistant"
116
- ? message.content
117
- .filter((part) => part.type === "toolCall")
118
- .map((part) => `\nTOOL CALL ${part.name} ${JSON.stringify(part.arguments)}`)
119
- .join("")
120
- : "";
121
- return `${heading}\n${textFromContent(message.content)}${toolCalls}`;
122
  })
123
  .join("\n\n");
124
- nodes.transcript.textContent = rendered || "No messages yet.";
125
- nodes.transcript.scrollTop = nodes.transcript.scrollHeight;
 
 
 
 
 
 
 
 
 
 
126
  }
127
 
128
  async function refreshFiles() {
@@ -146,6 +209,61 @@ async function bootSandbox() {
146
  }
147
  }
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  nodes.boot.addEventListener("click", bootSandbox);
150
 
151
  nodes.reset.addEventListener("click", async () => {
@@ -162,31 +280,77 @@ nodes.reset.addEventListener("click", async () => {
162
  });
163
 
164
  nodes.demo.addEventListener("click", () => {
165
- nodes.prompt.value =
166
- "Create hello.js that prints the result of 21 * 2, run it with Node, and tell me the command output.";
167
  });
168
 
169
- nodes.run.addEventListener("click", async () => {
170
- nodes.run.disabled = true;
171
- try {
172
- await bootSandbox();
173
- await agent.prompt(nodes.prompt.value);
174
- } catch (error) {
175
- setStatus("Error");
176
- logEvent("agent", error.stack || error.message || String(error));
177
- } finally {
178
- nodes.run.disabled = false;
179
  }
180
  });
181
 
182
  nodes.mode.addEventListener("change", () => {
 
183
  resetAgent();
184
  setModelStatus(nodes.mode.value === "mock" ? "Deterministic test model" : "Model idle");
185
  });
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  setStatus("Ready");
188
  setSandboxStatus("Not booted");
189
- setModelStatus(nodes.mode.value === "mock" ? "Deterministic test model" : "Model idle");
190
- renderTranscript();
 
191
  refreshFiles().catch(() => {});
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  status: document.querySelector("#status"),
7
  modelStatus: document.querySelector("#model-status"),
8
  sandboxStatus: document.querySelector("#sandbox-status"),
9
+ chat: document.querySelector("#chat"),
10
  eventLog: document.querySelector("#event-log"),
11
  files: document.querySelector("#files"),
12
  prompt: document.querySelector("#prompt"),
13
+ composer: document.querySelector("#composer"),
14
+ send: document.querySelector("#send"),
15
  boot: document.querySelector("#boot-sandbox"),
16
  reset: document.querySelector("#reset-sandbox"),
17
  demo: document.querySelector("#demo-prompt"),
18
  mode: document.querySelector("#mode"),
19
  device: document.querySelector("#device"),
20
+ gateDevice: document.querySelector("#gate-device"),
21
  maxTokens: document.querySelector("#max-new-tokens"),
22
  temperature: document.querySelector("#temperature"),
23
  modelLabel: document.querySelector("#model-label"),
24
+ loadModel: document.querySelector("#load-model"),
25
+ confirmLoadModel: document.querySelector("#confirm-load-model"),
26
+ useTestModel: document.querySelector("#use-test-model"),
27
+ modelGate: document.querySelector("#model-gate"),
28
+ gateStatus: document.querySelector("#gate-status"),
29
  };
30
 
 
 
 
31
  const params = new URLSearchParams(window.location.search);
32
+ let modelReady = false;
33
+ let transcriptText = "";
34
+
35
+ nodes.modelLabel.textContent = MODEL_ID;
36
+ if (!navigator.gpu) {
37
+ nodes.device.value = "wasm";
38
+ nodes.gateDevice.value = "wasm";
39
+ }
40
+ if (params.get("device")) {
41
+ nodes.device.value = params.get("device");
42
+ nodes.gateDevice.value = params.get("device");
43
+ }
44
  if (params.get("mode") === "mock") nodes.mode.value = "mock";
 
45
 
46
  const sandbox = createSandbox({
47
  onStatus: (text) => setSandboxStatus(text),
 
71
  nodes.modelStatus.textContent = text;
72
  }
73
 
74
+ function setBusy(isBusy) {
75
+ nodes.send.disabled = isBusy;
76
+ nodes.loadModel.disabled = isBusy;
77
+ nodes.confirmLoadModel.disabled = isBusy;
78
+ nodes.useTestModel.disabled = isBusy;
79
+ }
80
+
81
  function logEvent(kind, text) {
82
  const line = `[${new Date().toLocaleTimeString()}] ${kind}: ${text}`;
83
  nodes.eventLog.textContent = `${nodes.eventLog.textContent}${line}\n`;
 
97
  switch (event.type) {
98
  case "agent_start":
99
  setStatus("Agent running");
100
+ setBusy(true);
101
  logEvent("agent", "start");
102
  break;
103
  case "message_end":
104
+ renderChat();
105
  break;
106
  case "tool_execution_start":
107
  logEvent("tool", `${event.toolName} started`);
 
111
  break;
112
  case "agent_end":
113
  setStatus("Ready");
114
+ setBusy(false);
115
+ renderChat();
116
  refreshFiles().catch((error) => logEvent("files", error.message));
117
  break;
118
  default:
 
125
  function resetAgent() {
126
  agent.abort();
127
  agent = createAgent();
128
+ renderChat();
129
+ }
130
+
131
+ function makeElement(tag, className, text) {
132
+ const element = document.createElement(tag);
133
+ if (className) element.className = className;
134
+ if (text !== undefined) element.textContent = text;
135
+ return element;
136
+ }
137
+
138
+ function renderMessage(message) {
139
+ if (message.role === "user") {
140
+ const bubble = makeElement("article", "message user");
141
+ bubble.append(makeElement("div", "message-label", "You"));
142
+ bubble.append(makeElement("div", "message-body", textFromContent(message.content)));
143
+ return bubble;
144
+ }
145
+
146
+ if (message.role === "toolResult") {
147
+ const bubble = makeElement("article", `message tool${message.isError ? " error" : ""}`);
148
+ bubble.append(makeElement("div", "message-label", message.isError ? `Tool error: ${message.toolName}` : `Tool: ${message.toolName}`));
149
+ const body = makeElement("pre", "message-code", textFromContent(message.content));
150
+ bubble.append(body);
151
+ return bubble;
152
+ }
153
+
154
+ const bubble = makeElement("article", "message assistant");
155
+ bubble.append(makeElement("div", "message-label", "Pi"));
156
+ const text = textFromContent(message.content);
157
+ if (text) bubble.append(makeElement("div", "message-body", text));
158
+ const toolCalls = Array.isArray(message.content) ? message.content.filter((part) => part.type === "toolCall") : [];
159
+ for (const call of toolCalls) {
160
+ const tool = makeElement("div", "tool-call");
161
+ tool.append(makeElement("span", "tool-name", call.name));
162
+ tool.append(makeElement("code", "", JSON.stringify(call.arguments)));
163
+ bubble.append(tool);
164
+ }
165
+ return bubble;
166
  }
167
 
168
+ function renderChat() {
169
+ nodes.chat.textContent = "";
170
+ const messages = agent.state.messages;
171
+ transcriptText = messages
172
  .map((message) => {
173
+ if (message.role === "toolResult") return `TOOL ${message.toolName}\n${textFromContent(message.content)}`;
174
+ return `${message.role.toUpperCase()}\n${textFromContent(message.content)}`;
 
 
 
 
 
 
 
 
 
 
175
  })
176
  .join("\n\n");
177
+
178
+ if (messages.length === 0) {
179
+ const empty = makeElement("section", "empty-chat");
180
+ empty.append(makeElement("h2", "", "What should Pi do in the sandbox?"));
181
+ empty.append(makeElement("p", "", "Ready when you are."));
182
+ nodes.chat.append(empty);
183
+ } else {
184
+ for (const message of messages) {
185
+ nodes.chat.append(renderMessage(message));
186
+ }
187
+ }
188
+ nodes.chat.scrollTop = nodes.chat.scrollHeight;
189
  }
190
 
191
  async function refreshFiles() {
 
209
  }
210
  }
211
 
212
+ function hideGate() {
213
+ nodes.modelGate.classList.add("hidden");
214
+ }
215
+
216
+ function showGate(text = "Ready.") {
217
+ nodes.gateStatus.textContent = text;
218
+ nodes.modelGate.classList.remove("hidden");
219
+ }
220
+
221
+ async function loadModelFromControls() {
222
+ if (nodes.mode.value === "mock") {
223
+ modelReady = true;
224
+ setModelStatus("Deterministic test model");
225
+ hideGate();
226
+ return;
227
+ }
228
+
229
+ setBusy(true);
230
+ nodes.gateStatus.textContent = "Downloading model...";
231
+ try {
232
+ await agent.preloadModel();
233
+ modelReady = true;
234
+ setModelStatus("Model ready");
235
+ nodes.gateStatus.textContent = "Model ready.";
236
+ hideGate();
237
+ } catch (error) {
238
+ const message = error.stack || error.message || String(error);
239
+ setModelStatus("Model error");
240
+ nodes.gateStatus.textContent = message;
241
+ logEvent("model", message);
242
+ } finally {
243
+ setBusy(false);
244
+ }
245
+ }
246
+
247
+ async function sendPrompt() {
248
+ const prompt = nodes.prompt.value.trim();
249
+ if (!prompt) return;
250
+ if (nodes.mode.value === "local" && !modelReady) {
251
+ showGate("Download the model before sending, or use the test model.");
252
+ return;
253
+ }
254
+
255
+ setBusy(true);
256
+ try {
257
+ await bootSandbox();
258
+ nodes.prompt.value = "";
259
+ await agent.prompt(prompt);
260
+ } catch (error) {
261
+ setStatus("Error");
262
+ setBusy(false);
263
+ logEvent("agent", error.stack || error.message || String(error));
264
+ }
265
+ }
266
+
267
  nodes.boot.addEventListener("click", bootSandbox);
268
 
269
  nodes.reset.addEventListener("click", async () => {
 
280
  });
281
 
282
  nodes.demo.addEventListener("click", () => {
283
+ nodes.prompt.value = "Create hello.js, run 21 * 2 with Node, and report the output.";
284
+ nodes.prompt.focus();
285
  });
286
 
287
+ nodes.composer.addEventListener("submit", async (event) => {
288
+ event.preventDefault();
289
+ await sendPrompt();
290
+ });
291
+
292
+ nodes.prompt.addEventListener("keydown", async (event) => {
293
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
294
+ event.preventDefault();
295
+ await sendPrompt();
 
296
  }
297
  });
298
 
299
  nodes.mode.addEventListener("change", () => {
300
+ modelReady = nodes.mode.value === "mock";
301
  resetAgent();
302
  setModelStatus(nodes.mode.value === "mock" ? "Deterministic test model" : "Model idle");
303
  });
304
 
305
+ nodes.device.addEventListener("change", () => {
306
+ nodes.gateDevice.value = nodes.device.value;
307
+ modelReady = nodes.mode.value === "mock";
308
+ if (nodes.mode.value === "local") setModelStatus("Model idle");
309
+ resetAgent();
310
+ });
311
+
312
+ nodes.gateDevice.addEventListener("change", () => {
313
+ nodes.device.value = nodes.gateDevice.value;
314
+ });
315
+
316
+ nodes.loadModel.addEventListener("click", () => {
317
+ nodes.mode.value = "local";
318
+ showGate("Ready.");
319
+ });
320
+
321
+ nodes.confirmLoadModel.addEventListener("click", async () => {
322
+ nodes.mode.value = "local";
323
+ nodes.device.value = nodes.gateDevice.value;
324
+ resetAgent();
325
+ await loadModelFromControls();
326
+ });
327
+
328
+ nodes.useTestModel.addEventListener("click", () => {
329
+ nodes.mode.value = "mock";
330
+ modelReady = true;
331
+ resetAgent();
332
+ setModelStatus("Deterministic test model");
333
+ hideGate();
334
+ });
335
+
336
  setStatus("Ready");
337
  setSandboxStatus("Not booted");
338
+ modelReady = nodes.mode.value === "mock";
339
+ setModelStatus(modelReady ? "Deterministic test model" : "Model idle");
340
+ renderChat();
341
  refreshFiles().catch(() => {});
342
 
343
+ if (params.get("setup") === "skip" || nodes.mode.value === "mock") {
344
+ hideGate();
345
+ } else {
346
+ showGate("Ready.");
347
+ }
348
+
349
+ window.__piWebAgent = {
350
+ get transcript() {
351
+ return transcriptText;
352
+ },
353
+ get modelReady() {
354
+ return modelReady;
355
+ },
356
+ };
src/piAgent.js CHANGED
@@ -164,6 +164,34 @@ function mockPlan(context) {
164
  toolCalls: [{ tool: "list_files", args: { path: "." } }],
165
  };
166
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  return {
168
  toolCalls: [
169
  {
@@ -333,16 +361,27 @@ export function createPiAgent({ sandbox, modelMode, device, maxTokens, temperatu
333
  },
334
  ];
335
 
336
- return new Agent({
337
  initialState: {
338
  model: LOCAL_MODEL,
339
  systemPrompt:
340
- "You are Pi Web Agent. Use the sandbox tools for filesystem or command tasks, then give concise results.",
341
  tools,
342
  },
343
  streamFn,
344
  toolExecution: "sequential",
345
  });
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
 
348
  export { MODEL_ID };
 
164
  toolCalls: [{ tool: "list_files", args: { path: "." } }],
165
  };
166
  }
167
+ if (userText.includes("install") || userText.includes("dependency") || userText.includes("package")) {
168
+ return {
169
+ toolCalls: [
170
+ {
171
+ tool: "write_file",
172
+ args: {
173
+ path: "check-package.mjs",
174
+ content: 'import isNumber from "is-number";\nconsole.log(`dependency check: ${isNumber(42)}`);\n',
175
+ },
176
+ },
177
+ {
178
+ tool: "run_command",
179
+ args: {
180
+ command: "npm",
181
+ args: ["install", "is-number@7.0.0"],
182
+ timeoutMs: 120000,
183
+ },
184
+ },
185
+ {
186
+ tool: "run_command",
187
+ args: {
188
+ command: "node",
189
+ args: ["check-package.mjs"],
190
+ },
191
+ },
192
+ ],
193
+ };
194
+ }
195
  return {
196
  toolCalls: [
197
  {
 
361
  },
362
  ];
363
 
364
+ const agent = new Agent({
365
  initialState: {
366
  model: LOCAL_MODEL,
367
  systemPrompt:
368
+ "You are Pi Web Agent. Use the sandbox tools for filesystem, npm dependency, or command tasks, then give concise results.",
369
  tools,
370
  },
371
  streamFn,
372
  toolExecution: "sequential",
373
  });
374
+
375
+ agent.preloadModel = async () => {
376
+ if (modelMode() === "mock") {
377
+ onModelStatus("Deterministic test model");
378
+ return;
379
+ }
380
+ await getGenerator();
381
+ onModelStatus("Model ready");
382
+ };
383
+
384
+ return agent;
385
  }
386
 
387
  export { MODEL_ID };
src/styles.css CHANGED
@@ -1,6 +1,6 @@
1
  :root {
2
- color: #172033;
3
- background: #f3f6f9;
4
  font-family:
5
  Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
6
  }
@@ -21,13 +21,33 @@ textarea {
21
  font: inherit;
22
  }
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  #app {
25
  min-height: 100vh;
26
- padding: 18px;
27
  }
28
 
29
- .shell {
30
- width: min(1280px, 100%);
 
 
 
 
31
  margin: 0 auto;
32
  }
33
 
@@ -36,7 +56,7 @@ textarea {
36
  align-items: end;
37
  justify-content: space-between;
38
  gap: 16px;
39
- padding: 8px 0 18px;
40
  border-bottom: 1px solid #c7d2df;
41
  }
42
 
@@ -47,7 +67,7 @@ p {
47
  }
48
 
49
  h1 {
50
- font-size: 38px;
51
  line-height: 1;
52
  letter-spacing: 0;
53
  }
@@ -55,11 +75,11 @@ h1 {
55
  h2 {
56
  font-size: 15px;
57
  line-height: 1.2;
58
- color: #405064;
59
  }
60
 
61
  #model-label {
62
- margin-top: 8px;
63
  color: #5d6c7f;
64
  overflow-wrap: anywhere;
65
  }
@@ -69,7 +89,7 @@ h2 {
69
  flex-wrap: wrap;
70
  justify-content: end;
71
  gap: 8px;
72
- max-width: 620px;
73
  }
74
 
75
  .status {
@@ -78,134 +98,296 @@ h2 {
78
  padding: 7px 10px;
79
  border: 1px solid #b7c4d3;
80
  border-radius: 6px;
81
- background: #ffffff;
82
  color: #314155;
83
  overflow-wrap: anywhere;
84
  }
85
 
86
- .controls {
87
  display: grid;
88
- grid-template-columns: 1.2fr 1fr 1fr 1fr;
89
  gap: 12px;
90
- margin: 18px 0 12px;
91
  }
92
 
93
- label {
94
- display: grid;
95
- gap: 6px;
96
- color: #526173;
97
- font-size: 14px;
98
- font-weight: 650;
99
  }
100
 
101
- select,
102
- input,
103
- textarea {
104
- width: 100%;
105
- border: 1px solid #b8c5d4;
106
- border-radius: 6px;
107
- background: #ffffff;
108
- color: #152033;
109
  }
110
 
111
- select,
112
- input {
113
- height: 42px;
114
- padding: 0 10px;
 
 
 
 
115
  }
116
 
117
- .command-row {
118
- display: flex;
119
- flex-wrap: wrap;
120
  gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  margin-bottom: 14px;
 
 
 
 
122
  }
123
 
124
- button {
125
- min-width: 116px;
126
- height: 42px;
127
- padding: 0 16px;
128
- border: 0;
129
- border-radius: 6px;
130
- background: #176b6c;
131
- color: #ffffff;
132
- font-weight: 750;
133
- cursor: pointer;
134
  }
135
 
136
- button:nth-child(2) {
137
- background: #4f5f73;
138
  }
139
 
140
- button:nth-child(3) {
141
- background: #7a4d18;
 
 
142
  }
143
 
144
- button:disabled {
145
- cursor: wait;
146
- opacity: 0.64;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  }
148
 
149
- .workspace {
 
 
 
 
 
 
 
 
150
  display: grid;
151
- gap: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
 
154
- .prompt-pane {
155
  display: grid;
 
156
  gap: 10px;
 
 
 
 
 
157
  }
158
 
159
  textarea {
160
- min-height: 116px;
 
 
161
  resize: vertical;
162
- padding: 13px;
 
 
 
 
163
  line-height: 1.45;
164
  }
165
 
166
- #run {
167
- width: fit-content;
 
 
 
 
 
168
  }
169
 
170
- .panes {
171
  display: grid;
172
- grid-template-columns: 1.35fr 0.9fr;
173
  gap: 12px;
174
  }
175
 
176
- .pane {
177
  display: grid;
178
- gap: 8px;
179
- min-width: 0;
 
 
 
180
  }
181
 
182
- .pane.wide {
183
- grid-column: 1 / -1;
 
 
 
 
184
  }
185
 
186
- pre {
187
- min-height: 220px;
188
- max-height: 360px;
189
- margin: 0;
190
- padding: 14px;
191
  border: 1px solid #b8c5d4;
192
  border-radius: 6px;
193
- background: #fbfcfd;
194
- color: #162033;
195
- line-height: 1.45;
196
- white-space: pre-wrap;
197
- overflow: auto;
198
- overflow-wrap: anywhere;
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
 
201
  #event-log {
202
  min-height: 150px;
203
- max-height: 220px;
 
 
 
 
 
 
 
 
 
204
  }
205
 
206
- @media (max-width: 780px) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  #app {
208
- padding: 12px;
209
  }
210
 
211
  .topbar {
@@ -217,9 +399,20 @@ pre {
217
  justify-content: start;
218
  }
219
 
220
- .controls,
221
- .panes {
222
  grid-template-columns: 1fr;
223
  }
224
- }
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  :root {
2
+ color: #182233;
3
+ background: #eef2f6;
4
  font-family:
5
  Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
6
  }
 
21
  font: inherit;
22
  }
23
 
24
+ button {
25
+ min-height: 40px;
26
+ padding: 0 15px;
27
+ border: 0;
28
+ border-radius: 6px;
29
+ background: #176b6c;
30
+ color: #fff;
31
+ font-weight: 750;
32
+ cursor: pointer;
33
+ }
34
+
35
+ button:disabled {
36
+ cursor: wait;
37
+ opacity: 0.6;
38
+ }
39
+
40
  #app {
41
  min-height: 100vh;
42
+ padding: 14px;
43
  }
44
 
45
+ .app-shell {
46
+ display: grid;
47
+ grid-template-rows: auto minmax(0, 1fr);
48
+ gap: 12px;
49
+ width: min(1360px, 100%);
50
+ min-height: calc(100vh - 28px);
51
  margin: 0 auto;
52
  }
53
 
 
56
  align-items: end;
57
  justify-content: space-between;
58
  gap: 16px;
59
+ padding: 8px 0 14px;
60
  border-bottom: 1px solid #c7d2df;
61
  }
62
 
 
67
  }
68
 
69
  h1 {
70
+ font-size: 34px;
71
  line-height: 1;
72
  letter-spacing: 0;
73
  }
 
75
  h2 {
76
  font-size: 15px;
77
  line-height: 1.2;
78
+ color: #3f5064;
79
  }
80
 
81
  #model-label {
82
+ margin-top: 7px;
83
  color: #5d6c7f;
84
  overflow-wrap: anywhere;
85
  }
 
89
  flex-wrap: wrap;
90
  justify-content: end;
91
  gap: 8px;
92
+ max-width: 680px;
93
  }
94
 
95
  .status {
 
98
  padding: 7px 10px;
99
  border: 1px solid #b7c4d3;
100
  border-radius: 6px;
101
+ background: #fff;
102
  color: #314155;
103
  overflow-wrap: anywhere;
104
  }
105
 
106
+ .layout {
107
  display: grid;
108
+ grid-template-columns: minmax(0, 1fr) 340px;
109
  gap: 12px;
110
+ min-height: 0;
111
  }
112
 
113
+ .chat-panel,
114
+ .side-panel {
115
+ min-height: 0;
 
 
 
116
  }
117
 
118
+ .chat-panel {
119
+ display: grid;
120
+ grid-template-rows: minmax(0, 1fr) auto;
121
+ gap: 12px;
 
 
 
 
122
  }
123
 
124
+ .chat-scroll {
125
+ min-height: 420px;
126
+ max-height: calc(100vh - 214px);
127
+ overflow: auto;
128
+ padding: 18px;
129
+ border: 1px solid #c1ccda;
130
+ border-radius: 8px;
131
+ background: #fbfcfe;
132
  }
133
 
134
+ .empty-chat {
135
+ display: grid;
136
+ align-content: center;
137
  gap: 10px;
138
+ min-height: 320px;
139
+ color: #4c5d72;
140
+ text-align: center;
141
+ }
142
+
143
+ .empty-chat h2 {
144
+ font-size: 24px;
145
+ color: #182233;
146
+ }
147
+
148
+ .message {
149
+ display: grid;
150
+ gap: 7px;
151
+ width: min(760px, 92%);
152
  margin-bottom: 14px;
153
+ padding: 12px 14px;
154
+ border: 1px solid #c4ceda;
155
+ border-radius: 8px;
156
+ background: #fff;
157
  }
158
 
159
+ .message.user {
160
+ margin-left: auto;
161
+ border-color: #99bfbd;
162
+ background: #e9f7f4;
 
 
 
 
 
 
163
  }
164
 
165
+ .message.assistant {
166
+ border-color: #c4ceda;
167
  }
168
 
169
+ .message.tool {
170
+ width: min(820px, 96%);
171
+ margin-left: 24px;
172
+ background: #f5f7fa;
173
  }
174
 
175
+ .message.error {
176
+ border-color: #d99a9a;
177
+ background: #fff4f4;
178
+ }
179
+
180
+ .message-label {
181
+ color: #5e6e82;
182
+ font-size: 12px;
183
+ font-weight: 800;
184
+ text-transform: uppercase;
185
+ }
186
+
187
+ .message-body {
188
+ white-space: pre-wrap;
189
+ overflow-wrap: anywhere;
190
+ line-height: 1.48;
191
+ }
192
+
193
+ .message-code,
194
+ pre {
195
+ margin: 0;
196
+ white-space: pre-wrap;
197
+ overflow: auto;
198
+ overflow-wrap: anywhere;
199
+ line-height: 1.45;
200
  }
201
 
202
+ .message-code {
203
+ max-height: 240px;
204
+ padding: 10px;
205
+ border-radius: 6px;
206
+ background: #182233;
207
+ color: #e9eef5;
208
+ }
209
+
210
+ .tool-call {
211
  display: grid;
212
+ gap: 6px;
213
+ padding: 10px;
214
+ border: 1px solid #d1dae5;
215
+ border-radius: 6px;
216
+ background: #f7f9fc;
217
+ }
218
+
219
+ .tool-name {
220
+ color: #315f63;
221
+ font-weight: 800;
222
+ }
223
+
224
+ .tool-call code {
225
+ white-space: pre-wrap;
226
+ overflow-wrap: anywhere;
227
  }
228
 
229
+ .composer {
230
  display: grid;
231
+ grid-template-columns: minmax(0, 1fr) auto;
232
  gap: 10px;
233
+ align-items: end;
234
+ padding: 10px;
235
+ border: 1px solid #c1ccda;
236
+ border-radius: 8px;
237
+ background: #fff;
238
  }
239
 
240
  textarea {
241
+ width: 100%;
242
+ min-height: 64px;
243
+ max-height: 180px;
244
  resize: vertical;
245
+ padding: 12px;
246
+ border: 1px solid #b8c5d4;
247
+ border-radius: 6px;
248
+ background: #fff;
249
+ color: #152033;
250
  line-height: 1.45;
251
  }
252
 
253
+ .composer-actions {
254
+ display: flex;
255
+ gap: 8px;
256
+ }
257
+
258
+ #demo-prompt {
259
+ background: #4f5f73;
260
  }
261
 
262
+ .side-panel {
263
  display: grid;
264
+ align-content: start;
265
  gap: 12px;
266
  }
267
 
268
+ .control-group {
269
  display: grid;
270
+ gap: 10px;
271
+ padding: 12px;
272
+ border: 1px solid #c1ccda;
273
+ border-radius: 8px;
274
+ background: #fff;
275
  }
276
 
277
+ label {
278
+ display: grid;
279
+ gap: 6px;
280
+ color: #526173;
281
+ font-size: 13px;
282
+ font-weight: 700;
283
  }
284
 
285
+ select,
286
+ input {
287
+ width: 100%;
288
+ height: 40px;
289
+ padding: 0 10px;
290
  border: 1px solid #b8c5d4;
291
  border-radius: 6px;
292
+ background: #fff;
293
+ color: #152033;
294
+ }
295
+
296
+ .compact-grid {
297
+ display: grid;
298
+ grid-template-columns: 1fr 1fr;
299
+ gap: 8px;
300
+ }
301
+
302
+ .button-row {
303
+ display: grid;
304
+ grid-template-columns: 1fr 1fr;
305
+ gap: 8px;
306
+ }
307
+
308
+ #reset-sandbox,
309
+ #use-test-model {
310
+ background: #4f5f73;
311
  }
312
 
313
+ #files,
314
  #event-log {
315
  min-height: 150px;
316
+ max-height: 230px;
317
+ padding: 10px;
318
+ border: 1px solid #d1dae5;
319
+ border-radius: 6px;
320
+ background: #f7f9fc;
321
+ color: #243248;
322
+ }
323
+
324
+ #event-log {
325
+ max-height: 270px;
326
  }
327
 
328
+ .model-gate {
329
+ position: fixed;
330
+ inset: 0;
331
+ display: grid;
332
+ place-items: center;
333
+ padding: 18px;
334
+ background: rgba(20, 31, 45, 0.46);
335
+ z-index: 20;
336
+ }
337
+
338
+ .model-gate.hidden {
339
+ display: none;
340
+ }
341
+
342
+ .model-dialog {
343
+ display: grid;
344
+ gap: 16px;
345
+ width: min(540px, 100%);
346
+ padding: 22px;
347
+ border-radius: 8px;
348
+ background: #fff;
349
+ box-shadow: 0 24px 80px rgba(17, 27, 42, 0.22);
350
+ }
351
+
352
+ .eyebrow {
353
+ margin-bottom: 7px;
354
+ color: #176b6c;
355
+ font-size: 12px;
356
+ font-weight: 850;
357
+ text-transform: uppercase;
358
+ }
359
+
360
+ .model-dialog h2 {
361
+ font-size: 24px;
362
+ color: #172033;
363
+ }
364
+
365
+ .dialog-copy {
366
+ margin-top: 8px;
367
+ color: #526173;
368
+ line-height: 1.5;
369
+ }
370
+
371
+ .dialog-actions {
372
+ display: flex;
373
+ flex-wrap: wrap;
374
+ gap: 10px;
375
+ }
376
+
377
+ .dialog-options {
378
+ display: grid;
379
+ gap: 10px;
380
+ color: #526173;
381
+ }
382
+
383
+ #gate-status {
384
+ min-height: 22px;
385
+ overflow-wrap: anywhere;
386
+ }
387
+
388
+ @media (max-width: 900px) {
389
  #app {
390
+ padding: 10px;
391
  }
392
 
393
  .topbar {
 
399
  justify-content: start;
400
  }
401
 
402
+ .layout {
 
403
  grid-template-columns: 1fr;
404
  }
 
405
 
406
+ .chat-scroll {
407
+ min-height: 360px;
408
+ max-height: none;
409
+ }
410
+
411
+ .composer {
412
+ grid-template-columns: 1fr;
413
+ }
414
+
415
+ .composer-actions {
416
+ justify-content: end;
417
+ }
418
+ }