@premai/api-extension-pi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # `@premai/api-extension-pi`
2
+
3
+ A [Pi](https://pi.dev) extension that registers the **Prem API** inference provider. By default models route through `@premai/api-sdk`'s `createRvencClient`, which encrypts payloads end-to-end before they reach the gateway. Set `ENABLE_ZDR=true` to route through the plain OpenAI-compatible ZDR path instead.
4
+
5
+ [Documentation](https://docs.prem.io/) | [API keys](https://dashboard.prem.io/api-keys) | [npm](https://www.npmjs.com/package/@premai/api-extension-pi)
6
+
7
+ ## Modes
8
+
9
+ | `ENABLE_ZDR` | Transport | Model catalog |
10
+ | --- | --- | --- |
11
+ | `false` (unset) | Encrypted rvenc (`/rvenc/chat/completions`) | `/rvenc/models` (confidential) |
12
+ | `true` | Plain OpenAI (`/openai/chat/completions`) | `/openai/models` (standard) |
13
+
14
+ Models are fetched from the gateway at runtime.
15
+
16
+ ## Install
17
+
18
+ Add to your Pi `settings.json`:
19
+
20
+ ```json
21
+ {
22
+ "packages": ["npm:@premai/api-extension-pi@^0.1.0"]
23
+ }
24
+ ```
25
+
26
+ or point `extensions` at a local checkout:
27
+
28
+ ```json
29
+ {
30
+ "extensions": ["/path/to/api-extension-pi"]
31
+ }
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ Create a [Prem API key](https://dashboard.prem.io/api-keys), then set the environment variables the extension reads:
37
+
38
+ ```bash
39
+ export PREM_API_KEY="your-api-key"
40
+ export PROXY_URL="https://gateway.prem.io"
41
+ export ENCLAVE_URL="https://conf-engine.prem.io"
42
+ ```
43
+
44
+ | Variable | Required | Default | Purpose |
45
+ | --- | --- | --- | --- |
46
+ | `PREM_API_KEY` | yes | — | Gateway API key |
47
+ | `ENABLE_ZDR` | no | `false` | `true` routes through the plain OpenAI ZDR path |
48
+ | `PROXY_URL` | no | `https://gateway.prem.io` | Gateway proxy endpoint |
49
+ | `ENCLAVE_URL` | no | `https://conf-engine.prem.io` | Confidential enclave endpoint (confidential mode only) |
50
+ | `CLIENT_KEK` | no | ephemeral | 32-byte key (64 hex) for persistent DEK caching across runs (confidential mode only) |
51
+
52
+ Without `CLIENT_KEK`, an ephemeral key-encryption key is generated per process.
53
+ Keep the API key and KEK in a secret manager; never commit them.
54
+
55
+ ## Usage
56
+
57
+ Set `defaultProvider` in `settings.json`:
58
+
59
+ ```json
60
+ {
61
+ "defaultProvider": "prem-api"
62
+ }
63
+ ```
64
+
65
+ Pi picks a default model from the fetched catalog; set `defaultModel` to pin one:
66
+
67
+ ```json
68
+ {
69
+ "defaultProvider": "prem-api",
70
+ "defaultModel": "deepseek-v4-flash"
71
+ }
72
+ ```
73
+
74
+ ## Security
75
+
76
+ - Inference payloads are encrypted inside the Pi process before they leave the machine. The Prem gateway receives ciphertext plus operational metadata only.
77
+ - Pi's repository access, shell commands, MCP servers, hooks, and tool results stay on the local machine or in their own external systems — the confidential boundary covers supported model traffic only.
@@ -0,0 +1,4 @@
1
+ export declare function isZdrMode(): boolean;
2
+ export declare function proxyUrl(): string;
3
+ export declare function enclaveUrl(): string;
4
+ export declare function clientKek(): string | undefined;
@@ -0,0 +1,45 @@
1
+ import { type Api, type Model, type Tool, type TranscriptContext, getCurrentTools } from "@earendil-works/pi-ai";
2
+ export declare function convertMessages(model: Model<Api>, context: TranscriptContext): Record<string, unknown>[];
3
+ export declare function convertTools(tools: Tool[]): {
4
+ type: string;
5
+ function: {
6
+ name: string;
7
+ description: string;
8
+ parameters: import("@earendil-works/pi-ai").TSchema;
9
+ };
10
+ }[];
11
+ export declare function parseChunkUsage(rawUsage: {
12
+ prompt_tokens?: number;
13
+ completion_tokens?: number;
14
+ prompt_tokens_details?: {
15
+ cached_tokens?: number;
16
+ cache_write_tokens?: number;
17
+ };
18
+ }, model: Model<Api>, calculateCost: (model: Model<Api>, usage: {
19
+ input: number;
20
+ output: number;
21
+ cacheRead: number;
22
+ cacheWrite: number;
23
+ totalTokens: number;
24
+ cost: {
25
+ input: number;
26
+ output: number;
27
+ cacheRead: number;
28
+ cacheWrite: number;
29
+ total: number;
30
+ };
31
+ }) => void): {
32
+ input: number;
33
+ output: number;
34
+ cacheRead: number;
35
+ cacheWrite: number;
36
+ totalTokens: number;
37
+ cost: {
38
+ input: number;
39
+ output: number;
40
+ cacheRead: number;
41
+ cacheWrite: number;
42
+ total: number;
43
+ };
44
+ };
45
+ export { getCurrentTools };
@@ -0,0 +1,2 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export default function (pi: ExtensionAPI): void;
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ var D="prem-api-completions",O="https://gateway.prem.io",H="https://conf-engine.prem.io";function v(){return process.env.ENABLE_ZDR==="true"}function E(){return process.env.PROXY_URL||O}function S(){return process.env.ENCLAVE_URL||H}function j(){return process.env.CLIENT_KEK}function ee(e){if(e.credential?.type==="api_key"&&e.credential.key)return e.credential.key;return process.env.PREM_API_KEY}function re(e,f){return{id:e.model,name:e.name,api:D,reasoning:!1,input:e.input_modalities.includes("image")?["text","image"]:["text"],cost:{input:(f?.prompt_price_per_k??0)*1000,output:(f?.completion_price_per_k??0)*1000,cacheRead:(f?.cached_prompt_price_per_k??0)*1000,cacheWrite:0},contextWindow:131072,maxTokens:8192}}async function Y(e,f,r){let n=ee(e);if(!n)throw Error("No API key for Prem API");let t=E(),u=await fetch(`${t}${f}`,{headers:{Authorization:`Bearer ${n}`},signal:e.signal});if(!u.ok)throw Error(`Failed to fetch Prem API models: HTTP ${u.status}`);return(await u.json()).data.filter((a)=>a.type==="CHAT"&&a.visible_in_dashboard===1).map((a)=>re(a,a.price_config?.[r]?.default))}async function te(e){return Y(e,"/rvenc/models","confidential")}async function ne(e){return Y(e,"/openai/models","standard")}async function U(e){return v()?ne(e):te(e)}import{calculateCost as me,clampThinkingLevel as pe,createAssistantMessageEventStream as ce,parseStreamingJson as Z}from"@earendil-works/pi-ai";import{createRvencClient as de,generateNewClientKEK as ye}from"@premai/api-sdk";import ge from"openai";import{getCurrentSystemPrompt as ie,getCurrentTools as J}from"@earendil-works/pi-ai";function w(e){return e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"")}var ae="(image omitted: model does not support images)",fe="(tool image omitted: model does not support images)";function V(e){return e.type==="text"}function se(e){return e.type==="toolCall"}function ue(e){return e.type==="image"}function X(e,f){let r=[],n=!1;for(let t of e){if(t.type==="image"){if(!n)r.push({type:"text",text:f});n=!0;continue}r.push(t),n=t.type==="text"&&t.text===f}return r}function _e(e,f){if(f.input.includes("image"))return e;return e.map((r)=>{if(r.role==="user"&&Array.isArray(r.content))return{...r,content:X(r.content,ae)};if(r.role==="toolResult")return{...r,content:X(r.content,fe)};return r})}function Q(e,f){let r=[],n=ie(f.messages),t=_e(f.messages,e);if(n)r.push({role:"system",content:w(n)});for(let u=0;u<t.length;u++){let m=t[u];if(m.role==="system")continue;if(m.role==="user")if(typeof m.content==="string")r.push({role:"user",content:w(m.content)});else{let a=m.content.map((c)=>{if(c.type==="text")return{type:"text",text:w(c.text)};return{type:"image_url",image_url:{url:`data:${c.mimeType};base64,${c.data}`}}});if(a.length===0)continue;r.push({role:"user",content:a})}else if(m.role==="assistant"){let a={role:"assistant",content:null},h=m.content.filter(V).filter((g)=>g.text.trim().length>0).map((g)=>w(g.text)).join("");if(h.length>0)a.content=h;let P=m.content.filter(se);if(P.length>0)a.tool_calls=P.map((g)=>({id:g.id,type:"function",function:{name:g.name,arguments:JSON.stringify(g.arguments)}}));if(!(a.content!==null&&a.content!==void 0&&(typeof a.content==="string"?a.content.length>0:a.content.length>0))&&!a.tool_calls)continue;r.push(a)}else if(m.role==="toolResult"){let a=[],c=u;for(;c<t.length&&t[c].role==="toolResult";c++){let h=t[c],P=h.content,T=P.filter(V).map((y)=>y.text).join(`
2
+ `),g=P.some((y)=>y.type==="image"),F=T.length>0;if(r.push({role:"tool",content:w(F?T:"(see attached image)"),tool_call_id:h.toolCallId}),g&&e.input.includes("image")){for(let y of P)if(ue(y))a.push({type:"image_url",image_url:{url:`data:${y.mimeType};base64,${y.data}`}})}}if(u=c-1,a.length>0)r.push({role:"user",content:[{type:"text",text:"Attached image(s) from tool result:"},...a]});continue}}return r}function q(e){return e.map((f)=>({type:"function",function:{name:f.name,description:f.description,parameters:f.parameters}}))}function I(e,f,r){let n=e.prompt_tokens||0,t=e.prompt_tokens_details?.cached_tokens??0,u=e.prompt_tokens_details?.cache_write_tokens||0,m=Math.max(0,n-t-u),a=e.completion_tokens||0,c={input:m,output:a,cacheRead:t,cacheWrite:u,totalTokens:m+a+t+u,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}};return r(f,c),c}var C;function Pe(){if(!C)C=ye(),console.warn("[pi-extension-prem-api] CLIENT_KEK not set; generated an ephemeral key-encryption-key for this process. Set CLIENT_KEK in env for persistent DEK caching across runs.");return C}function he(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"};default:return{stopReason:"error",errorMessage:`Provider finish_reason: ${e}`}}}function Me(e,f,r){let n=Q(e,f),t={model:e.id,messages:n,stream:!0,stream_options:{include_usage:!0}};if(r?.maxTokens)t.max_completion_tokens=r.maxTokens;if(r?.temperature!==void 0)t.temperature=r.temperature;let u=J(f.messages);if(u.length>0)t.tools=q(u);if(r?.toolChoice)t.tool_choice=r.toolChoice;if(r?.reasoning&&e.reasoning){let m=e.thinkingLevelMap?.[r.reasoning]??r.reasoning;t.reasoning_effort=m}return t}var Te=(e,f,r)=>{let n=ce();return(async()=>{let t={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{let u=r?.apiKey;if(!u)throw Error("No API key for Prem API");let m=e.baseUrl||E(),a=S(),h=v()?new ge({apiKey:u,baseURL:`${m}/openai`,timeout:r?.timeoutMs}):await de({apiKey:u,clientKEK:j()||Pe(),requestTimeoutMs:r?.timeoutMs,config:{endpoints:{proxy:m,enclave:a}}}),P=Me(e,f,r),T=await r?.onPayload?.(P,e);if(T!==void 0)P=T;let g={...r?.signal?{signal:r.signal}:{},...r?.timeoutMs!==void 0?{timeout:r.timeoutMs}:{},maxRetries:r?.maxRetries??0},F=await h.chat.completions.create(P,g);await r?.onResponse?.({status:200,headers:{}},e),n.push({type:"start",partial:t});let y=null,R=null,L=!1,G=new Map,W=new Map,x=t.content,M=(i)=>x.indexOf(i),o=(i)=>{let _=M(i);if(_===-1)return;if(i.type==="text")n.push({type:"text_end",contentIndex:_,content:i.text,partial:t});else if(i.type==="thinking")n.push({type:"thinking_end",contentIndex:_,content:i.thinking,partial:t});else if(i.type==="toolCall")i.arguments=Z(i.partialArgs),delete i.partialArgs,delete i.streamIndex,n.push({type:"toolcall_end",contentIndex:_,toolCall:i,partial:t})},B=()=>{if(!y)y={type:"text",text:""},x.push(y),n.push({type:"text_start",contentIndex:M(y),partial:t});return y},b=(i)=>{if(!R)R={type:"thinking",thinking:"",thinkingSignature:i},x.push(R),n.push({type:"thinking_start",contentIndex:M(R),partial:t});return R},k=(i)=>{let _=typeof i.index==="number"?i.index:void 0,s=_!==void 0?G.get(_):void 0;if(!s&&i.id)s=W.get(i.id);if(!s){if(s={type:"toolCall",id:i.id||"",name:i.function?.name||"",arguments:{},partialArgs:"",streamIndex:_},_!==void 0)G.set(_,s);if(i.id)W.set(i.id,s);x.push(s),n.push({type:"toolcall_start",contentIndex:M(s),partial:t})}if(_!==void 0&&s.streamIndex===void 0)s.streamIndex=_,G.set(_,s);if(i.id)W.set(i.id,s);return s};for await(let i of F){if(!i||typeof i!=="object")continue;let _=i;if(t.responseId||=_.id,typeof _.model==="string"&&_.model.length>0&&_.model!==e.id)t.responseModel||=_.model;if(_.usage)t.usage=I(_.usage,e,me);let s=Array.isArray(_.choices)?_.choices[0]:void 0;if(!s)continue;if(s.finish_reason){let A=he(s.finish_reason);if(t.stopReason=A.stopReason,A.errorMessage)t.errorMessage=A.errorMessage;L=!0}if(s.delta){if(s.delta.content!==null&&s.delta.content!==void 0&&s.delta.content.length>0){let p=B();p.text+=s.delta.content,n.push({type:"text_delta",contentIndex:M(p),delta:s.delta.content,partial:t})}let A=s.delta,l=["reasoning_content","reasoning","reasoning_text"],$=null;for(let p of l){let d=A[p];if(typeof d==="string"&&d.length>0){$=p;break}}if($){let p=A[$];if(typeof p==="string"&&p.length>0){let d=b($);d.thinking+=p,n.push({type:"thinking_delta",contentIndex:M(d),delta:p,partial:t})}}if(s.delta.tool_calls)for(let p of s.delta.tool_calls){let d=k(p);if(!d.id&&p.id)d.id=p.id,W.set(p.id,d);if(!d.name&&p.function?.name)d.name=p.function.name;let z="";if(p.function?.arguments){z=p.function.arguments;let N=d;N.partialArgs=(N.partialArgs??"")+p.function.arguments,d.arguments=Z(N.partialArgs)}n.push({type:"toolcall_delta",contentIndex:M(d),delta:z,partial:t})}}}for(let i of x)o(i);if(r?.signal?.aborted)throw Error("Request was aborted");if(t.stopReason==="aborted")throw Error("Request was aborted");if(t.stopReason==="error")throw Error(t.errorMessage||"Provider returned an error stop reason");if(!L)throw Error("Stream ended without finish_reason");n.push({type:"done",reason:t.stopReason,message:t}),n.end()}catch(u){for(let m of t.content)delete m.partialArgs,delete m.streamIndex;t.stopReason=r?.signal?.aborted?"aborted":"error",t.errorMessage=u instanceof Error?u.message:JSON.stringify(u),n.push({type:"error",reason:t.stopReason,error:t}),n.end()}})(),n},K=(e,f,r)=>{let n=r?.reasoning?pe(e,r.reasoning):void 0,t=n==="off"?void 0:n;return Te(e,f,{temperature:r?.temperature,maxTokens:r?.maxTokens,signal:r?.signal,apiKey:r?.apiKey,cacheRetention:r?.cacheRetention,sessionId:r?.sessionId,headers:r?.headers,onPayload:r?.onPayload,onResponse:r?.onResponse,timeoutMs:r?.timeoutMs,maxRetries:r?.maxRetries,maxRetryDelayMs:r?.maxRetryDelayMs,metadata:r?.metadata,reasoning:t})};function Ae(e){e.registerProvider("prem-api",{name:"Prem API",baseUrl:O,apiKey:"$PREM_API_KEY",api:D,streamSimple:K,refreshModels:U})}export{Ae as default};
3
+
4
+ //# debugId=F3EC2C910B8B895F64756E2164756E21
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,16 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/types.ts", "../src/config.ts", "../src/models.ts", "../src/provider.ts", "../src/convert-messages.ts", "../src/sanitize.ts", "../src/index.ts"],
4
+ "sourcesContent": [
5
+ "/** Custom API id used to route Prem API models through the encrypted rvenc transport. */\nexport const PREM_API = \"prem-api-completions\";\n\nexport const DEFAULT_PROXY_URL = \"https://gateway.prem.io\";\nexport const DEFAULT_ENCLAVE_URL = \"https://conf-engine.prem.io\";",
6
+ "import { DEFAULT_ENCLAVE_URL, DEFAULT_PROXY_URL } from \"./types.js\";\n\n/** True when ENABLE_ZDR=true: route through the plain OpenAI ZDR path instead of encrypted rvenc. */\nexport function isZdrMode(): boolean {\n return process.env.ENABLE_ZDR === \"true\";\n}\n\nexport function proxyUrl(): string {\n return process.env.PROXY_URL || DEFAULT_PROXY_URL;\n}\n\nexport function enclaveUrl(): string {\n return process.env.ENCLAVE_URL || DEFAULT_ENCLAVE_URL;\n}\n\nexport function clientKek(): string | undefined {\n return process.env.CLIENT_KEK;\n}",
7
+ "import type { ProviderModelConfig } from \"@earendil-works/pi-coding-agent\";\nimport type { RefreshModelsContext } from \"@earendil-works/pi-ai\";\nimport { isZdrMode, proxyUrl } from \"./config.js\";\nimport { PREM_API } from \"./types.js\";\n\ninterface GatewayModel {\n model: string;\n name: string;\n type: string;\n input_modalities: string[];\n visible_in_dashboard: number;\n price_config?: {\n confidential?: {\n default?: {\n prompt_price_per_k?: number;\n completion_price_per_k?: number;\n cached_prompt_price_per_k?: number;\n };\n };\n standard?: {\n default?: {\n prompt_price_per_k?: number;\n completion_price_per_k?: number;\n cached_prompt_price_per_k?: number;\n };\n };\n };\n}\n\ninterface GatewayModelsResponse {\n data: GatewayModel[];\n}\n\nfunction apiKeyFrom(context: RefreshModelsContext): string | undefined {\n if (context.credential?.type === \"api_key\" && context.credential.key) {\n return context.credential.key;\n }\n return process.env.PREM_API_KEY;\n}\n\ninterface GatewayPrice {\n prompt_price_per_k?: number;\n completion_price_per_k?: number;\n cached_prompt_price_per_k?: number;\n}\n\nfunction toProviderModel(m: GatewayModel, price?: GatewayPrice): ProviderModelConfig {\n return {\n id: m.model,\n name: m.name,\n api: PREM_API,\n reasoning: false,\n input: m.input_modalities.includes(\"image\") ? ([\"text\", \"image\"] as const) : ([\"text\"] as const),\n cost: {\n input: (price?.prompt_price_per_k ?? 0) * 1000,\n output: (price?.completion_price_per_k ?? 0) * 1000,\n cacheRead: (price?.cached_prompt_price_per_k ?? 0) * 1000,\n cacheWrite: 0,\n },\n contextWindow: 131072,\n maxTokens: 8192,\n };\n}\n\nasync function fetchModels(\n context: RefreshModelsContext,\n path: string,\n priceKey: \"confidential\" | \"standard\",\n): Promise<ProviderModelConfig[]> {\n const apiKey = apiKeyFrom(context);\n if (!apiKey) throw new Error(\"No API key for Prem API\");\n const baseUrl = proxyUrl();\n\n const res = await fetch(`${baseUrl}${path}`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n signal: context.signal,\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch Prem API models: HTTP ${res.status}`);\n }\n const payload = (await res.json()) as GatewayModelsResponse;\n\n return payload.data\n .filter((m) => m.type === \"CHAT\" && m.visible_in_dashboard === 1)\n .map((m) => toProviderModel(m, m.price_config?.[priceKey]?.default));\n}\n\nexport async function fetchConfidentialModels(\n context: RefreshModelsContext,\n): Promise<ProviderModelConfig[]> {\n return fetchModels(context, \"/rvenc/models\", \"confidential\");\n}\n\nexport async function fetchStandardModels(\n context: RefreshModelsContext,\n): Promise<ProviderModelConfig[]> {\n return fetchModels(context, \"/openai/models\", \"standard\");\n}\n\nexport async function fetchModelsForMode(\n context: RefreshModelsContext,\n): Promise<ProviderModelConfig[]> {\n return isZdrMode() ? fetchStandardModels(context) : fetchConfidentialModels(context);\n}",
8
+ "import {\n type Api,\n type AssistantMessage,\n type AssistantMessageEventStream,\n type Model,\n type SimpleStreamOptions,\n type TextContent,\n type ThinkingContent,\n type ToolCall,\n type TranscriptContext,\n calculateCost,\n clampThinkingLevel,\n createAssistantMessageEventStream,\n parseStreamingJson,\n} from \"@earendil-works/pi-ai\";\nimport { createRvencClient, generateNewClientKEK } from \"@premai/api-sdk\";\nimport OpenAI from \"openai\";\nimport type { ChatCompletionChunk } from \"openai/resources/chat/completions\";\nimport { convertMessages, convertTools, getCurrentTools, parseChunkUsage } from \"./convert-messages.js\";\nimport { clientKek, enclaveUrl, isZdrMode, proxyUrl } from \"./config.js\";\nimport { PREM_API } from \"./types.js\";\n\nlet _ephemeralKEK: string | undefined;\nfunction ephemeralClientKEK(): string {\n if (!_ephemeralKEK) {\n _ephemeralKEK = generateNewClientKEK();\n console.warn(\n \"[pi-extension-prem-api] CLIENT_KEK not set; generated an ephemeral key-encryption-key for this process. \" +\n \"Set CLIENT_KEK in env for persistent DEK caching across runs.\",\n );\n }\n return _ephemeralKEK;\n}\n\nfunction mapStopReason(reason: string | null): { stopReason: string; errorMessage?: string } {\n if (reason === null) return { stopReason: \"stop\" };\n switch (reason) {\n case \"stop\":\n case \"end\":\n return { stopReason: \"stop\" };\n case \"length\":\n return { stopReason: \"length\" };\n case \"function_call\":\n case \"tool_calls\":\n return { stopReason: \"toolUse\" };\n case \"content_filter\":\n return { stopReason: \"error\", errorMessage: \"Provider finish_reason: content_filter\" };\n default:\n return { stopReason: \"error\", errorMessage: `Provider finish_reason: ${reason}` };\n }\n}\n\nfunction buildParams(model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) {\n const messages = convertMessages(model, context);\n const params: Record<string, unknown> = {\n model: model.id,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n };\n if (options?.maxTokens) params.max_completion_tokens = options.maxTokens;\n if (options?.temperature !== undefined) params.temperature = options.temperature;\n\n const tools = getCurrentTools(context.messages);\n if (tools.length > 0) {\n params.tools = convertTools(tools);\n }\n if (options?.toolChoice) params.tool_choice = options.toolChoice;\n if (options?.reasoning && model.reasoning) {\n const mapped = model.thinkingLevelMap?.[options.reasoning] ?? options.reasoning;\n params.reasoning_effort = mapped;\n }\n return params;\n}\n\ntype ContentBlock = TextContent | ThinkingContent | ToolCall;\n\nexport const streamPremApi = (\n model: Model<Api>,\n context: TranscriptContext,\n options?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n const stream = createAssistantMessageEventStream();\n (async () => {\n const output: AssistantMessage = {\n role: \"assistant\",\n content: [] as ContentBlock[],\n api: model.api,\n provider: model.provider,\n model: model.id,\n usage: {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: \"stop\" as AssistantMessage[\"stopReason\"],\n timestamp: Date.now(),\n };\n try {\n const apiKey = options?.apiKey;\n if (!apiKey) throw new Error(\"No API key for Prem API\");\n const proxy = model.baseUrl || proxyUrl();\n const enclave = enclaveUrl();\n const zdr = isZdrMode();\n const client = zdr\n ? new OpenAI({ apiKey, baseURL: `${proxy}/openai`, timeout: options?.timeoutMs })\n : await createRvencClient({\n apiKey,\n clientKEK: clientKek() || ephemeralClientKEK(),\n requestTimeoutMs: options?.timeoutMs,\n config: { endpoints: { proxy, enclave } },\n });\n\n let params = buildParams(model, context, options);\n const nextParams = await options?.onPayload?.(params, model);\n if (nextParams !== undefined) {\n params = nextParams as Record<string, unknown>;\n }\n\n const requestOptions = {\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),\n maxRetries: options?.maxRetries ?? 0,\n };\n\n // RvencClient hides upstream headers/status, so onResponse gets a synthetic 200.\n const openaiStream = (await (client as OpenAI).chat.completions.create(\n params as unknown as Parameters<typeof client.chat.completions.create>[0],\n requestOptions,\n )) as AsyncIterable<ChatCompletionChunk>;\n await options?.onResponse?.({ status: 200, headers: {} }, model);\n stream.push({ type: \"start\", partial: output });\n\n let textBlock: TextContent | null = null;\n let thinkingBlock: ThinkingContent | null = null;\n let hasFinishReason = false;\n const toolCallBlocksByIndex = new Map<number, ToolCall>();\n const toolCallBlocksById = new Map<string, ToolCall>();\n const blocks = output.content as ContentBlock[];\n const getContentIndex = (block: ContentBlock) => blocks.indexOf(block);\n const finishBlock = (block: ContentBlock) => {\n const contentIndex = getContentIndex(block);\n if (contentIndex === -1) return;\n if (block.type === \"text\") {\n stream.push({ type: \"text_end\", contentIndex, content: block.text, partial: output });\n } else if (block.type === \"thinking\") {\n stream.push({ type: \"thinking_end\", contentIndex, content: block.thinking, partial: output });\n } else if (block.type === \"toolCall\") {\n block.arguments = parseStreamingJson((block as unknown as { partialArgs?: string }).partialArgs);\n delete (block as unknown as Record<string, unknown>).partialArgs;\n delete (block as unknown as Record<string, unknown>).streamIndex;\n stream.push({ type: \"toolcall_end\", contentIndex, toolCall: block, partial: output });\n }\n };\n const ensureTextBlock = () => {\n if (!textBlock) {\n textBlock = { type: \"text\", text: \"\" };\n blocks.push(textBlock);\n stream.push({ type: \"text_start\", contentIndex: getContentIndex(textBlock), partial: output });\n }\n return textBlock;\n };\n const ensureThinkingBlock = (thinkingSignature: string) => {\n if (!thinkingBlock) {\n thinkingBlock = { type: \"thinking\", thinking: \"\", thinkingSignature };\n blocks.push(thinkingBlock);\n stream.push({\n type: \"thinking_start\",\n contentIndex: getContentIndex(thinkingBlock),\n partial: output,\n });\n }\n return thinkingBlock;\n };\n const ensureToolCallBlock = (toolCall: {\n index?: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n }) => {\n const streamIndex = typeof toolCall.index === \"number\" ? toolCall.index : undefined;\n let block = streamIndex !== undefined ? toolCallBlocksByIndex.get(streamIndex) : undefined;\n if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);\n if (!block) {\n block = {\n type: \"toolCall\",\n id: toolCall.id || \"\",\n name: toolCall.function?.name || \"\",\n arguments: {},\n partialArgs: \"\",\n streamIndex,\n } as unknown as ToolCall;\n if (streamIndex !== undefined) toolCallBlocksByIndex.set(streamIndex, block);\n if (toolCall.id) toolCallBlocksById.set(toolCall.id, block);\n blocks.push(block);\n stream.push({ type: \"toolcall_start\", contentIndex: getContentIndex(block), partial: output });\n }\n if (streamIndex !== undefined && (block as unknown as { streamIndex?: number }).streamIndex === undefined) {\n (block as unknown as Record<string, unknown>).streamIndex = streamIndex;\n toolCallBlocksByIndex.set(streamIndex, block);\n }\n if (toolCall.id) toolCallBlocksById.set(toolCall.id, block);\n return block;\n };\n\n for await (const chunk of openaiStream) {\n if (!chunk || typeof chunk !== \"object\") continue;\n const c = chunk as {\n id?: string;\n model?: string;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n prompt_tokens_details?: { cached_tokens?: number; cache_write_tokens?: number };\n };\n choices?: Array<{\n finish_reason?: string | null;\n delta?: {\n content?: string | null;\n reasoning_content?: string;\n reasoning?: string;\n reasoning_text?: string;\n tool_calls?: Array<{\n index?: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n }>;\n };\n }>;\n };\n output.responseId ||= c.id;\n if (typeof c.model === \"string\" && c.model.length > 0 && c.model !== model.id) {\n output.responseModel ||= c.model;\n }\n if (c.usage) {\n output.usage = parseChunkUsage(c.usage, model, calculateCost);\n }\n const choice = Array.isArray(c.choices) ? c.choices[0] : undefined;\n if (!choice) continue;\n if (choice.finish_reason) {\n const r = mapStopReason(choice.finish_reason);\n output.stopReason = r.stopReason as AssistantMessage[\"stopReason\"];\n if (r.errorMessage) output.errorMessage = r.errorMessage;\n hasFinishReason = true;\n }\n if (choice.delta) {\n if (\n choice.delta.content !== null &&\n choice.delta.content !== undefined &&\n choice.delta.content.length > 0\n ) {\n const block = ensureTextBlock();\n block.text += choice.delta.content;\n stream.push({\n type: \"text_delta\",\n contentIndex: getContentIndex(block),\n delta: choice.delta.content,\n partial: output,\n });\n }\n const deltaFields = choice.delta as Record<string, unknown>;\n const reasoningFields = [\"reasoning_content\", \"reasoning\", \"reasoning_text\"];\n let foundReasoningField: string | null = null;\n for (const field of reasoningFields) {\n const value = deltaFields[field];\n if (typeof value === \"string\" && value.length > 0) {\n foundReasoningField = field;\n break;\n }\n }\n if (foundReasoningField) {\n const delta = deltaFields[foundReasoningField] as string;\n if (typeof delta === \"string\" && delta.length > 0) {\n const block = ensureThinkingBlock(foundReasoningField);\n block.thinking += delta;\n stream.push({\n type: \"thinking_delta\",\n contentIndex: getContentIndex(block),\n delta,\n partial: output,\n });\n }\n }\n if (choice.delta.tool_calls) {\n for (const toolCall of choice.delta.tool_calls) {\n const block = ensureToolCallBlock(toolCall);\n if (!block.id && toolCall.id) {\n block.id = toolCall.id;\n toolCallBlocksById.set(toolCall.id, block);\n }\n if (!block.name && toolCall.function?.name) {\n block.name = toolCall.function.name;\n }\n let delta = \"\";\n if (toolCall.function?.arguments) {\n delta = toolCall.function.arguments;\n const rec = block as unknown as { partialArgs?: string };\n rec.partialArgs = (rec.partialArgs ?? \"\") + toolCall.function.arguments;\n block.arguments = parseStreamingJson(rec.partialArgs);\n }\n stream.push({\n type: \"toolcall_delta\",\n contentIndex: getContentIndex(block),\n delta,\n partial: output,\n });\n }\n }\n }\n }\n for (const block of blocks) finishBlock(block);\n if (options?.signal?.aborted) throw new Error(\"Request was aborted\");\n if (output.stopReason === \"aborted\") throw new Error(\"Request was aborted\");\n if (output.stopReason === \"error\") {\n throw new Error(output.errorMessage || \"Provider returned an error stop reason\");\n }\n if (!hasFinishReason) throw new Error(\"Stream ended without finish_reason\");\n stream.push({\n type: \"done\",\n reason: output.stopReason as Extract<AssistantMessage[\"stopReason\"], \"stop\" | \"length\" | \"toolUse\" | \"deferred\">,\n message: output,\n });\n stream.end();\n } catch (error) {\n for (const block of output.content) {\n delete (block as unknown as Record<string, unknown>).partialArgs;\n delete (block as unknown as Record<string, unknown>).streamIndex;\n }\n output.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);\n stream.push({ type: \"error\", reason: output.stopReason, error: output });\n stream.end();\n }\n })();\n return stream;\n};\n\nexport const streamSimplePremApi = (\n model: Model<Api>,\n context: TranscriptContext,\n options?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n const reasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;\n const reasoningEffort = reasoning === \"off\" ? undefined : reasoning;\n return streamPremApi(model, context, {\n temperature: options?.temperature,\n maxTokens: options?.maxTokens,\n signal: options?.signal,\n apiKey: options?.apiKey,\n cacheRetention: options?.cacheRetention,\n sessionId: options?.sessionId,\n headers: options?.headers,\n onPayload: options?.onPayload,\n onResponse: options?.onResponse,\n timeoutMs: options?.timeoutMs,\n maxRetries: options?.maxRetries,\n maxRetryDelayMs: options?.maxRetryDelayMs,\n metadata: options?.metadata,\n reasoning: reasoningEffort,\n });\n};\n\nexport { PREM_API };",
9
+ "import {\n type Api,\n type ImageContent,\n type Model,\n type TextContent,\n type ThinkingContent,\n type Tool,\n type ToolCall,\n type TranscriptContext,\n getCurrentSystemPrompt,\n getCurrentTools,\n} from \"@earendil-works/pi-ai\";\nimport { sanitizeSurrogates } from \"./sanitize.js\";\n\nconst NON_VISION_PLACEHOLDER = \"(image omitted: model does not support images)\";\nconst NON_VISION_TOOL_PLACEHOLDER = \"(tool image omitted: model does not support images)\";\n\ntype ContentBlock = TextContent | ImageContent | ThinkingContent | ToolCall;\n\nfunction isText(b: ContentBlock): b is TextContent {\n return b.type === \"text\";\n}\nfunction isToolCall(b: ContentBlock): b is ToolCall {\n return b.type === \"toolCall\";\n}\nfunction isImage(b: ContentBlock): b is ImageContent {\n return b.type === \"image\";\n}\n\nfunction replaceImagesWithPlaceholder(\n content: ContentBlock[],\n placeholder: string,\n): ContentBlock[] {\n const result: ContentBlock[] = [];\n let prevWasPlaceholder = false;\n for (const block of content) {\n if (block.type === \"image\") {\n if (!prevWasPlaceholder) result.push({ type: \"text\", text: placeholder });\n prevWasPlaceholder = true;\n continue;\n }\n result.push(block);\n prevWasPlaceholder = block.type === \"text\" && block.text === placeholder;\n }\n return result;\n}\n\nfunction downgradeUnsupportedImages(messages: TranscriptContext[\"messages\"], model: Model<Api>) {\n if (model.input.includes(\"image\")) return messages;\n return messages.map((msg) => {\n if (msg.role === \"user\" && Array.isArray(msg.content)) {\n return { ...msg, content: replaceImagesWithPlaceholder(msg.content, NON_VISION_PLACEHOLDER) };\n }\n if (msg.role === \"toolResult\") {\n return { ...msg, content: replaceImagesWithPlaceholder(msg.content, NON_VISION_TOOL_PLACEHOLDER) };\n }\n return msg;\n });\n}\n\nexport function convertMessages(model: Model<Api>, context: TranscriptContext) {\n const params: Array<Record<string, unknown>> = [];\n const systemPrompt = getCurrentSystemPrompt(context.messages);\n const transformedMessages = downgradeUnsupportedImages(context.messages, model);\n\n if (systemPrompt) {\n params.push({ role: \"system\", content: sanitizeSurrogates(systemPrompt) });\n }\n\n for (let i = 0; i < transformedMessages.length; i++) {\n const msg = transformedMessages[i];\n if (msg.role === \"system\") continue;\n\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n params.push({ role: \"user\", content: sanitizeSurrogates(msg.content) });\n } else {\n const content = (msg.content as (TextContent | ImageContent)[]).map((item) => {\n if (item.type === \"text\") {\n return { type: \"text\", text: sanitizeSurrogates(item.text) };\n }\n return { type: \"image_url\", image_url: { url: `data:${item.mimeType};base64,${item.data}` } };\n });\n if (content.length === 0) continue;\n params.push({ role: \"user\", content });\n }\n } else if (msg.role === \"assistant\") {\n const assistantMsg: Record<string, unknown> = { role: \"assistant\", content: null };\n const textParts = msg.content\n .filter(isText)\n .filter((b) => b.text.trim().length > 0)\n .map((b) => sanitizeSurrogates(b.text));\n const assistantText = textParts.join(\"\");\n if (assistantText.length > 0) assistantMsg.content = assistantText;\n\n const toolCalls = msg.content.filter(isToolCall);\n if (toolCalls.length > 0) {\n assistantMsg.tool_calls = toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },\n }));\n }\n\n const hasContent =\n assistantMsg.content !== null &&\n assistantMsg.content !== undefined &&\n (typeof assistantMsg.content === \"string\"\n ? assistantMsg.content.length > 0\n : (assistantMsg.content as unknown[]).length > 0);\n if (!hasContent && !assistantMsg.tool_calls) continue;\n params.push(assistantMsg);\n } else if (msg.role === \"toolResult\") {\n const imageBlocks: Array<Record<string, unknown>> = [];\n let j = i;\n for (; j < transformedMessages.length && transformedMessages[j].role === \"toolResult\"; j++) {\n const toolMsg = transformedMessages[j] as Extract<typeof transformedMessages[number], { role: \"toolResult\" }>;\n const toolContent = toolMsg.content as (TextContent | ImageContent)[];\n const textResult = toolContent\n .filter(isText)\n .map((b) => b.text)\n .join(\"\\n\");\n const hasImages = toolContent.some((c) => c.type === \"image\");\n const hasText = textResult.length > 0;\n params.push({\n role: \"tool\",\n content: sanitizeSurrogates(hasText ? textResult : \"(see attached image)\"),\n tool_call_id: toolMsg.toolCallId,\n });\n if (hasImages && model.input.includes(\"image\")) {\n for (const block of toolContent) {\n if (isImage(block)) {\n imageBlocks.push({\n type: \"image_url\",\n image_url: { url: `data:${block.mimeType};base64,${block.data}` },\n });\n }\n }\n }\n }\n i = j - 1;\n if (imageBlocks.length > 0) {\n params.push({\n role: \"user\",\n content: [{ type: \"text\", text: \"Attached image(s) from tool result:\" }, ...imageBlocks],\n });\n }\n continue;\n }\n }\n return params;\n}\n\nexport function convertTools(tools: Tool[]) {\n return tools.map((tool) => ({\n type: \"function\",\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\nexport function parseChunkUsage(\n rawUsage: {\n prompt_tokens?: number;\n completion_tokens?: number;\n prompt_tokens_details?: { cached_tokens?: number; cache_write_tokens?: number };\n },\n model: Model<Api>,\n calculateCost: (model: Model<Api>, usage: {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n totalTokens: number;\n cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };\n }) => void,\n) {\n const promptTokens = rawUsage.prompt_tokens || 0;\n const cacheReadTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? 0;\n const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;\n const input = Math.max(0, promptTokens - cacheReadTokens - cacheWriteTokens);\n const outputTokens = rawUsage.completion_tokens || 0;\n const usage = {\n input,\n output: outputTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: cacheWriteTokens,\n totalTokens: input + outputTokens + cacheReadTokens + cacheWriteTokens,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n calculateCost(model, usage);\n return usage;\n}\n\nexport { getCurrentTools };",
10
+ "/** Strip lone UTF-16 surrogates that would break JSON serialization to the gateway. */\nexport function sanitizeSurrogates(text: string): string {\n return text.replace(\n /[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/g,\n \"\",\n );\n}",
11
+ "import type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\nimport { fetchModelsForMode } from \"./models.js\";\nimport { streamSimplePremApi } from \"./provider.js\";\nimport { DEFAULT_PROXY_URL, PREM_API } from \"./types.js\";\n\nexport default function (pi: ExtensionAPI) {\n pi.registerProvider(\"prem-api\", {\n name: \"Prem API\",\n baseUrl: DEFAULT_PROXY_URL,\n apiKey: \"$PREM_API_KEY\",\n api: PREM_API,\n streamSimple: streamSimplePremApi,\n refreshModels: fetchModelsForMode,\n });\n}"
12
+ ],
13
+ "mappings": "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",
14
+ "debugId": "F3EC2C910B8B895F64756E2164756E21",
15
+ "names": []
16
+ }
@@ -0,0 +1,5 @@
1
+ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
2
+ import type { RefreshModelsContext } from "@earendil-works/pi-ai";
3
+ export declare function fetchConfidentialModels(context: RefreshModelsContext): Promise<ProviderModelConfig[]>;
4
+ export declare function fetchStandardModels(context: RefreshModelsContext): Promise<ProviderModelConfig[]>;
5
+ export declare function fetchModelsForMode(context: RefreshModelsContext): Promise<ProviderModelConfig[]>;
@@ -0,0 +1,5 @@
1
+ import { type Api, type AssistantMessageEventStream, type Model, type SimpleStreamOptions, type TranscriptContext } from "@earendil-works/pi-ai";
2
+ import { PREM_API } from "./types.js";
3
+ export declare const streamPremApi: (model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) => AssistantMessageEventStream;
4
+ export declare const streamSimplePremApi: (model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) => AssistantMessageEventStream;
5
+ export { PREM_API };
@@ -0,0 +1 @@
1
+ export declare function sanitizeSurrogates(text: string): string;
@@ -0,0 +1,3 @@
1
+ export declare const PREM_API = "prem-api-completions";
2
+ export declare const DEFAULT_PROXY_URL = "https://gateway.prem.io";
3
+ export declare const DEFAULT_ENCLAVE_URL = "https://conf-engine.prem.io";
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@premai/api-extension-pi",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension that registers the Prem API inference provider (end-to-end encrypted via @premai/api-sdk createRvencClient).",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "pi",
9
+ "pi-extension",
10
+ "prem-api",
11
+ "confidential",
12
+ "prem",
13
+ "llm"
14
+ ],
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "main": "dist/index.js",
20
+ "types": "dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "pi": {
28
+ "extensions": [
29
+ "./dist/index.js"
30
+ ]
31
+ },
32
+ "scripts": {
33
+ "build": "bun build src/index.ts --outdir dist --entry-naming \"[name].[ext]\" --target node --packages external --minify --sourcemap=linked && tsc -p tsconfig.json --emitDeclarationOnly",
34
+ "test": "bun test",
35
+ "typecheck": "tsc --noEmit -p tsconfig.json"
36
+ },
37
+ "dependencies": {
38
+ "@earendil-works/pi-ai": "^0.87.0",
39
+ "@earendil-works/pi-coding-agent": "^0.87.0",
40
+ "@premai/api-sdk": "^1.0.66",
41
+ "openai": "^6.40.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/bun": "^1.4.2",
45
+ "@types/node": "^22.0.0",
46
+ "typescript": "^5.5.0"
47
+ }
48
+ }