@perstack/runtime 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2564 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,1667 @@
|
|
|
1
|
-
import {existsSync}from'fs';import {readFile,writeFile,mkdir,readdir}from'fs/promises';import pe from'path';import {createId}from'@paralleldrive/cuid2';import ko from'smol-toml';import {setup,assign,createActor}from'xstate';import {z as z$1,ZodError}from'zod';import {anthropic}from'@ai-sdk/anthropic';import {google}from'@ai-sdk/google';import {openai}from'@ai-sdk/openai';import {Client}from'@modelcontextprotocol/sdk/client/index.js';import {SSEClientTransport}from'@modelcontextprotocol/sdk/client/sse.js';import {StdioClientTransport}from'@modelcontextprotocol/sdk/client/stdio.js';import {McpError}from'@modelcontextprotocol/sdk/types.js';import {generateText,jsonSchema}from'ai';import {dedent}from'ts-dedent';var ft="claude-4-sonnet-20250514",ge=["claude-4-opus-20250514","claude-4-sonnet-20250514","claude-3-7-sonnet-20250219","claude-3-5-sonnet-latest","claude-3-5-sonnet-20241022","claude-3-5-sonnet-20240620","claude-3-5-haiku-latest","claude-3-5-haiku-20241022"],ue=["gemini-2.5-pro-preview-05-06","gemini-2.5-pro-exp-03-25","gemini-2.5-flash-preview-04-17","gemini-2.0-pro-exp-02-05","gemini-2.0-flash-thinking-exp-01-21","gemini-2.0-flash","gemini-2.0-flash-001","gemini-2.0-flash-live-001","gemini-2.0-flash-lite","gemini-2.0-flash-exp","gemini-1.5-pro","gemini-1.5-pro-latest","gemini-1.5-pro-001","gemini-1.5-pro-002","gemini-1.5-flash","gemini-1.5-flash-latest","gemini-1.5-flash-001","gemini-1.5-flash-002","gemini-1.5-flash-8b","gemini-1.5-flash-8b-latest","gemini-1.5-flash-8b-001"],de=["o4-mini","o4-mini-2025-04-16","o3","o3-2025-04-16","o3-mini","o3-mini-2025-01-31","o1","o1-2024-12-17","o1-mini","o1-mini-2024-09-12","gpt-4.5-preview","gpt-4.5-preview-2025-02-27","gpt-4.1","gpt-4.1-2025-04-14","gpt-4.1-mini","gpt-4.1-mini-2025-04-14","gpt-4.1-nano","gpt-4.1-nano-2025-04-14","gpt-4o","gpt-4o-2024-05-13","gpt-4o-2024-08-06","gpt-4o-2024-11-20","gpt-4o-audio-preview","gpt-4o-audio-preview-2024-10-01","gpt-4o-audio-preview-2024-12-17","gpt-4o-search-preview","gpt-4o-search-preview-2025-03-11","gpt-4o-mini-search-preview","gpt-4o-mini-search-preview-2025-03-11","gpt-4o-mini","gpt-4o-mini-2024-07-18"],ht=Object.fromEntries([...ge,...ue,...de].map(e=>[e,{default:e===ft,model:e}]));function te(){let e=Object.values(ht).find(t=>t.default);if(!e)throw new Error("No default model found");return e.model}function z(e){let t=e??te();if(ge.includes(t))return anthropic(t);if(ue.includes(t))return google(t);if(de.includes(t))return openai(t);throw new Error(`Unsupported model: ${t}`)}var I=z$1.object({id:z$1.string()}),A=I.extend({type:z$1.literal("textPart"),text:z$1.string()}),yt=I.extend({type:z$1.literal("imageUrlPart"),url:z$1.string().url(),mimeType:z$1.string()}),fe=I.extend({type:z$1.literal("imageInlinePart"),encodedData:z$1.string(),mimeType:z$1.string()}),xt=I.extend({type:z$1.literal("imageBinaryPart"),data:z$1.string(),mimeType:z$1.string()}),kt=I.extend({type:z$1.literal("fileUrlPart"),url:z$1.string().url(),mimeType:z$1.string()}),Tt=I.extend({type:z$1.literal("fileInlinePart"),encodedData:z$1.string(),mimeType:z$1.string()}),Rt=I.extend({type:z$1.literal("fileBinaryPart"),data:z$1.string(),mimeType:z$1.string()}),St=I.extend({type:z$1.literal("toolCallPart"),toolCallId:z$1.string(),toolName:z$1.string(),args:z$1.unknown()}),Pt=I.extend({type:z$1.literal("toolResultPart"),toolCallId:z$1.string(),toolName:z$1.string(),contents:z$1.array(z$1.union([A,fe])),isError:z$1.boolean().optional()}),L=z$1.object({id:z$1.string()}),Ct=L.extend({type:z$1.literal("instructionMessage"),contents:z$1.array(A),cache:z$1.boolean().optional()}),vt=L.extend({type:z$1.literal("userMessage"),contents:z$1.array(z$1.union([A,yt,fe,xt,kt,Tt,Rt])),cache:z$1.boolean().optional()}),wt=L.extend({type:z$1.literal("expertMessage"),contents:z$1.array(z$1.union([A,St])),cache:z$1.boolean().optional()}),It=L.extend({type:z$1.literal("toolMessage"),contents:z$1.array(Pt),cache:z$1.boolean().optional()}),he=z$1.union([Ct,vt,wt,It]);var Mt=/^(@[a-z0-9][a-z0-9_-]*\/)?[a-z0-9][a-z0-9_-]*$/,bt=/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/,Et=/^[a-z0-9][a-z0-9_-]*$/,D=/^((?:@[a-z0-9][a-z0-9_\.-]*\/)?[a-z0-9][a-z0-9_\.-]*)(?:@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?)|@([a-z0-9][a-z0-9_\.-]*))?$/,Uo=/^[a-z0-9][a-z0-9._-]*$/,$t=214;function jo(e){let t=e.match(D);if(!t)throw new Error(`Invalid expert key format: ${e}`);let[o,r,a,i]=t;return {key:o,name:r,version:a,tag:i}}var G=z$1.object({type:z$1.literal("mcpStdioSkill"),name:z$1.string(),description:z$1.string().optional(),rule:z$1.string().optional(),pick:z$1.array(z$1.string()).optional().default([]),omit:z$1.array(z$1.string()).optional().default([]),command:z$1.string(),packageName:z$1.string().optional(),args:z$1.array(z$1.string()).optional().default([]),requiredEnv:z$1.array(z$1.string()).optional().default([])}),K=z$1.object({type:z$1.literal("mcpSseSkill"),name:z$1.string(),description:z$1.string().optional(),rule:z$1.string().optional(),pick:z$1.array(z$1.string()).optional().default([]),omit:z$1.array(z$1.string()).optional().default([]),endpoint:z$1.string()}),ye=z$1.object({name:z$1.string(),description:z$1.string().optional(),inputJsonSchema:z$1.string()}),q=z$1.object({type:z$1.literal("interactiveSkill"),name:z$1.string(),description:z$1.string().optional(),rule:z$1.string().optional(),tools:z$1.record(z$1.string(),ye.omit({name:true})).transform(e=>Object.fromEntries(Object.entries(e).map(([t,o])=>[t,ye.parse({...o,name:t})])))}),J=z$1.object({key:z$1.string().regex(D).min(1),name:z$1.string().regex(Mt).min(1).max($t),version:z$1.string().regex(bt),description:z$1.string().min(1).max(1024*2).optional(),instruction:z$1.string().min(1).max(1024*20),skills:z$1.record(z$1.string(),z$1.discriminatedUnion("type",[G.omit({name:true}),K.omit({name:true}),q.omit({name:true})])).optional().default({"@perstack/base":{type:"mcpStdioSkill",description:"Base skill",command:"npx",args:["-y","@perstack/base"]}}).transform(e=>Object.fromEntries(Object.entries(e).map(([t,o])=>[t,z$1.discriminatedUnion("type",[G,K,q]).parse({...o,name:t})]))),delegates:z$1.array(z$1.string().regex(D).min(1)).optional().default([]),tags:z$1.array(z$1.string().regex(Et).min(1)).optional().default([])}),Nt=z$1.object({promptTokens:z$1.number(),completionTokens:z$1.number(),totalTokens:z$1.number(),cacheCreationInputTokens:z$1.number(),cacheReadInputTokens:z$1.number()}),Ft=z$1.enum(["init","proceeding","completed","stoppedByInteractiveTool","stoppedByDelegate","stoppedByExceededMaxSteps","stoppedByError"]),oe=z$1.object({id:z$1.string(),runId:z$1.string(),status:Ft,stepNumber:z$1.number(),messages:z$1.array(he),expert:z$1.object({key:z$1.string(),name:z$1.string(),version:z$1.string()}),delegateTo:z$1.object({expert:z$1.object({key:z$1.string(),name:z$1.string(),version:z$1.string()}),toolCallId:z$1.string(),toolName:z$1.string(),query:z$1.string()}).optional(),delegatedBy:z$1.object({expert:z$1.object({key:z$1.string(),name:z$1.string(),version:z$1.string()}),toolCallId:z$1.string(),toolName:z$1.string(),checkpointId:z$1.string()}).optional(),usage:Nt}),ke=z$1.object({setting:z$1.object({runId:z$1.string().optional().transform(e=>e??createId()),expertKey:z$1.string().regex(D).min(1),input:z$1.object({text:z$1.string().optional(),interactiveToolCallResult:z$1.object({toolCallId:z$1.string(),toolName:z$1.string(),text:z$1.string()}).optional()}),experts:z$1.record(z$1.string().regex(D).min(1),J.omit({key:true})).optional().default({}).transform(e=>Object.fromEntries(Object.entries(e).map(([t,o])=>[t,J.parse({...o,key:t})]))),model:z$1.string().optional().default(te()),temperature:z$1.number().min(0).max(1).optional().default(.3),maxSteps:z$1.number().min(1).optional(),maxRetries:z$1.number().min(0).optional().default(10),workspace:z$1.string().optional(),startedAt:z$1.number().optional().default(Date.now()),updatedAt:z$1.number().optional().default(Date.now())}),checkpoint:oe.optional()});function k(e){return (t,o,r)=>({type:e,id:createId(),expertKey:o.expert.key,timestamp:Date.now(),runId:t.runId,stepNumber:o.stepNumber,...r})}var ne=k("startRun"),Te=k("startGeneration"),re=k("retry"),Re=k("callTool"),Se=k("callInteractiveTool"),Pe=k("callDelegate"),Ce=k("resolveToolResult"),ve=k("resolveThought"),we=k("resolvePdfFile"),Ie=k("resolveImageFile"),Me=k("attemptCompletion"),$=k("finishToolCall"),be=k("completeRun"),Ee=k("stopRunByInteractiveTool"),$e=k("stopRunByDelegate"),Ne=k("stopRunByExceededMaxSteps"),Fe=k("continueToNextStep");async function De(e,t){let o=O(e),a=(await readdir(o,{withFileTypes:true}).then(s=>s.filter(u=>u.isFile()&&u.name.startsWith("checkpoint-")))).find(s=>s.name.endsWith(`-${t}.json`));if(!a)throw new Error(`checkpoint not found: ${e} ${t}`);let i=`${o}/${a.name}`,c=await readFile(i,"utf8");return oe.parse(JSON.parse(c))}async function Oe(e,t){let{id:o,runId:r,stepNumber:a}=e,i=O(r),c=`${i}/checkpoint-${t}-${a}-${o}.json`;await mkdir(i,{recursive:true}),await writeFile(c,JSON.stringify(e,null,2));}async function Be(e){let{timestamp:t,runId:o,stepNumber:r,type:a}=e,i=O(o),c=`${i}/event-${t}-${r}-${a}.json`;await mkdir(i,{recursive:true}),await writeFile(c,JSON.stringify(e,null,2));}var _e={version:"0.0.9"};var m=console.info,ze=console.debug,g=e=>{let t=new Date().toISOString(),o=e.stepNumber,r=e.expertKey;return `${t} ${o} ${r}`};async function se(e){switch(await Be(e),e.type){case "startRun":{m(`${g(e)} \u{1F99C} Perstack@${_e.version} started`);let{inputMessages:t}=e;for(let o of t)o.type==="userMessage"?Ot(o):o.type==="toolMessage"&&Bt(o);break}case "startGeneration":{m(`${g(e)} \u{1F99C} Generating tool call`);break}case "retry":{B(e),m(`${g(e)} \u{1F99C} Retrying tool call generation`);break}case "callTool":{if(B(e),m(`${g(e)} \u{1F99C} Calling tool`),e.toolCall.skillName==="@perstack/base")switch(e.toolCall.toolName){case "think":{let t=e.toolCall.args.thought;m(`${g(e)} \u{1F4AD} Thought Updated:`),ze(t);break}case "readPdfFile":{let t=e.toolCall.args.path;m(`${g(e)} \u{1F4C4} Reading PDF: ${t}`);break}case "readImageFile":{let t=e.toolCall.args.path;m(`${g(e)} \u{1F5BC}\uFE0F Reading Image: ${t}`);break}default:{m(`${g(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`),m(`${g(e)} \u{1F527} Args: ${e.toolCall.args}`);break}}else m(`${g(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`),m(`${g(e)} \u{1F527} Args: ${e.toolCall.args}`);break}case "callInteractiveTool":{B(e),m(`${g(e)} \u{1F99C} Calling interactive tool`),m(`${g(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`),m(`${g(e)} \u{1F527} Args: ${e.toolCall.args}`);break}case "callDelegate":{B(e),m(`${g(e)} \u{1F99C} Calling delegate`),m(`${g(e)} \u{1F527} Tool: ${e.toolCall.toolName}`),m(`${g(e)} \u{1F527} Args: ${e.toolCall.args}`);break}case "resolveToolResult":{if(m(`${g(e)} \u{1F527} Resolved Tool Result`),e.toolResult.skillName==="@perstack/base")switch(e.toolResult.toolName){case "todo":{let t=e.toolResult.result.find(r=>r.type==="textPart")?.text,{todos:o}=JSON.parse(t??"{}");m(`${g(e)} \u{1F4C4} Todo:`);for(let r of o)ze(`${r.completed?"[x]":"[ ]"} ${r.id}: ${r.title}`);break}default:{m(`${g(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`),m(`${g(e)} \u{1F527} Result: ${e.toolResult.result}`);break}}else m(`${g(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`),m(`${g(e)} \u{1F527} Result: ${e.toolResult.result}`);break}case "resolveThought":{m(`${g(e)} \u{1F4AD} Resolved Thought:`,e.toolResult);break}case "resolvePdfFile":{m(`${g(e)} \u{1F4C4} Resolved PDF:`,e.toolResult);break}case "resolveImageFile":{m(`${g(e)} \u{1F5BC}\uFE0F Resolved Image:`,e.toolResult);break}case "attemptCompletion":{m(`${g(e)} \u2705 Attempting completion`);break}case "completeRun":{B(e),m(`${g(e)} \u{1F99C} Completing run`),m(`${g(e)} \u{1F99C} Result:`,e.text);break}case "stopRunByInteractiveTool":{m(`${g(e)} \u{1F99C} Stopping run by interactive tool`);break}case "stopRunByDelegate":{m(`${g(e)} \u{1F99C} Stopping run by delegate`);break}case "stopRunByExceededMaxSteps":{m(`${g(e)} \u{1F99C} Stopping run by exceeded max steps`);break}case "continueToNextStep":{m(`${g(e)} \u{1F99C} Continuing to next step`);break}}}function Ot(e){let t=new Date().toISOString(),o=e.contents;for(let r of o)r.type==="textPart"?m(`${t} \u{1F4AC} User: ${r.text}`):r.type==="imageUrlPart"?m(`${t} \u{1F5BC}\uFE0F User: ${r.url}`):r.type==="imageInlinePart"?m(`${t} \u{1F5BC}\uFE0F User: Inline image`):r.type==="imageBinaryPart"?m(`${t} \u{1F5BC}\uFE0F User: Binary image`):r.type==="fileUrlPart"?m(`${t} \u{1F4C4} User: ${r.url}`):r.type==="fileInlinePart"?m(`${t} \u{1F4C4} User: Inline file`):r.type==="fileBinaryPart"&&m(`${t} \u{1F4C4} User: Binary file`);}function Bt(e){let t=new Date().toISOString(),o=e.contents;for(let r of o)if(r.type==="toolResultPart"){let{contents:a}=r;for(let i of a)i.type==="textPart"?m(`${t} \u{1F4AC} Tool Result: ${i.text}`):i.type==="imageInlinePart"&&m(`${t} \u{1F5BC}\uFE0F Tool Result: Inline image`);}}function B(e){let t=[`In: ${e.usage.promptTokens.toLocaleString()}`,`Out: ${e.usage.completionTokens.toLocaleString()}`,`Total: ${e.usage.totalTokens.toLocaleString()}`,`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`].join(", ");m(`${g(e)} \u{1F4CA} Tokens usage: ${t}`);let o=[`In: ${e.usage.promptTokens.toLocaleString()}`,`Out: ${e.usage.completionTokens.toLocaleString()}`,`Total: ${e.usage.totalTokens.toLocaleString()}`,`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`].join(", ");m(`${g(e)} \u{1F4CA} Total usage: ${o}`);}var V=class{listeners=[];subscribe(t){this.listeners.push(t);}async emit(t){for(let o of this.listeners)await o({...t,id:createId(),timestamp:Date.now()});}};var zt=z$1.object({name:z$1.string(),version:z$1.string(),minRuntimeVersion:z$1.string(),description:z$1.string(),instruction:z$1.string(),skills:z$1.record(z$1.string(),z$1.discriminatedUnion("type",[G.omit({name:true}),K.omit({name:true}),q.omit({name:true})])),delegates:z$1.array(z$1.string()),tags:z$1.array(z$1.string()),status:z$1.string(),owner:z$1.object({name:z$1.string()}),createdAt:z$1.string().datetime(),updatedAt:z$1.string().datetime()}),Ae=z$1.object({data:z$1.object({expert:zt})});var Y=class{baseUrl;registry;apiKey;constructor(t){this.baseUrl=t?.baseUrl??process.env.PERSTACK_API_BASE_URL??"https://api.perstack.ai",this.apiKey=t?.apiKey??process.env.PERSTACK_API_KEY,this.registry=new ae(this);}},ae=class{client;endpoint;constructor(t){this.client=t,this.endpoint="/api/registry/v1/experts";}async get(t){let o=encodeURIComponent(t),r=new URL(`${this.endpoint}/${o}`,this.client.baseUrl),a={"Content-Type":"application/json",Authorization:`Bearer ${this.client.apiKey}`},i=await fetch(r.toString(),{headers:a});if(!i.ok)throw new Error(`Registry returned non-200 status code: ${i.status}, reason: ${i.statusText}`);let c=await i.json();return Ae.parse(c)}};async function ie(e,t){if(t[e])return t[e];let o=new Y,{data:r}=await o.registry.get(e),a=J.parse({...r.expert,key:e});return t[e]=a,a}var _=class{_toolDefinitions=[];_initialized=false;name;type;skill;interactiveSkill;expert;_mcpClient;_params;constructor(t){switch(this._params=t,this.type=t.type,t.type){case "mcp":this.name=t.skill.name,this.skill=t.skill;break;case "interactive":this.name=t.interactiveSkill.name,this.interactiveSkill=t.interactiveSkill;break;case "delegate":this.name=t.expert.name,this.expert=t.expert;break}}async init(){switch(this._params.type){case "mcp":{await this._initMcpSkill(this._params);break}case "interactive":{await this._initInteractiveSkill(this._params);break}case "delegate":{await this._initDelegate(this._params);break}}}async _initMcpSkill(t){switch(this._mcpClient=new Client({name:`${t.skill.name}-mcp-client`,version:"1.0.0"}),t.skill.type){case "mcpStdioSkill":{if(!t.skill.command)throw new Error(`Skill ${t.skill.name} has no command`);let{command:r,args:a}=this._getCommandArgs(t.skill);console.error(r,a);let i=new StdioClientTransport({command:r,args:a,env:Object.fromEntries(Object.entries(process.env).filter(([c,s])=>s!==void 0&&s!=="").map(([c,s])=>[c,s]))});await this._mcpClient.connect(i);break}case "mcpSseSkill":{if(!t.skill.endpoint)throw new Error(`Skill ${t.skill.name} has no endpoint`);let r=new SSEClientTransport(new URL(t.skill.endpoint));await this._mcpClient.connect(r);break}}let{tools:o}=await this._mcpClient.listTools();this._toolDefinitions=o.map(r=>({skillName:t.skill.name,name:r.name,description:r.description,inputSchema:r.inputSchema,interactive:false})),this._initialized=true;}_getCommandArgs(t){let{name:o,command:r,packageName:a,args:i}=t;if(!a&&(!i||i.length===0))throw new Error(`Skill ${o} has no packageName or args. Please provide one of them.`);if(a&&i&&i.length>0)throw new Error(`Skill ${o} has both packageName and args. Please provide only one of them.`);let c=i&&i.length>0?i:[a];return r==="npx"&&!c.includes("-y")&&(c=["-y",...c]),{command:r,args:c}}async _initInteractiveSkill(t){this._toolDefinitions=Object.values(t.interactiveSkill.tools).map(o=>({skillName:t.interactiveSkill.name,name:o.name,description:o.description,inputSchema:JSON.parse(o.inputJsonSchema),interactive:true})),this._initialized=true;}async _initDelegate(t){this._toolDefinitions=[{skillName:t.expert.name,name:t.expert.name.split("/").pop()??t.expert.name,description:t.expert.description,inputSchema:{type:"object",properties:{query:{type:"string"}},required:["query"]},interactive:false}],this._initialized=true;}async close(){this._mcpClient&&await this._mcpClient.close();}async getToolDefinitions(){if(!this._initialized)throw new Error(`${this.name} is not initialized`);if(this._params.type==="mcp"){let t=this._params.skill.omit??[],o=this._params.skill.pick??[];return this._toolDefinitions.filter(r=>t.length>0?!t.includes(r.name):true).filter(r=>o.length>0?o.includes(r.name):true)}return this._toolDefinitions}async callTool(t,o){switch(this._params.type){case "mcp":{if(!this._mcpClient)throw new Error(`${this.name} is not initialized`);try{let r=await this._mcpClient.callTool({name:t,arguments:o});return this._convertToolResult(r,t,o)}catch(r){return this._handleToolError(r,t)}}case "interactive":return [];case "delegate":return []}}_handleToolError(t,o){if(t instanceof McpError)return [{type:"textPart",text:`Error calling tool ${o}: ${t.message}`,id:createId()}];if(t instanceof ZodError)return [{type:"textPart",text:`Invalid tool call arguments: ${t}`,id:createId()}];throw t}_convertToolResult(t,o,r){return !t.content||t.content.length===0?[{type:"textPart",text:`Tool ${o} returned nothing with arguments: ${JSON.stringify(r)}`,id:createId()}]:t.content.filter(a=>a.type!=="audio"&&a.type!=="resource_link").map(a=>this._convertPart(a))}_convertPart(t){switch(t.type){case "text":return !t.text||t.text===""?{type:"textPart",text:"Error: No content",id:createId()}:{type:"textPart",text:t.text,id:createId()};case "image":if(!t.data||!t.mimeType)throw new Error("Image part must have both data and mimeType");return {type:"imageInlinePart",encodedData:t.data,mimeType:t.mimeType,id:createId()};case "resource":if(!t.resource)throw new Error("Resource part must have resource content");return this._convertResource(t.resource)}}_convertResource(t){if(!t.mimeType)throw new Error(`Resource ${JSON.stringify(t)} has no mimeType`);if(t.text&&typeof t.text=="string")return {type:"textPart",text:t.text,id:createId()};if(t.blob&&typeof t.blob=="string")return {type:"fileInlinePart",encodedData:t.blob,mimeType:t.mimeType,id:createId()};throw new Error(`Unsupported resource type: ${JSON.stringify(t)}`)}};async function Le(e,t){let o={},{skills:r}=e;r["@perstack/base"]||(r["@perstack/base"]={type:"mcpStdioSkill",name:"@perstack/base",description:"The base skill for Perstack",pick:[],omit:[],command:"npx",args:["-y","@perstack/base"],requiredEnv:[]});let a=await Promise.all(Object.values(r).filter(s=>s.type==="mcpStdioSkill"||s.type==="mcpSseSkill").map(async s=>{let u=new _({type:"mcp",skill:s});return await u.init(),u})),i=await Promise.all(Object.values(r).filter(s=>s.type==="interactiveSkill").map(async s=>{let u=new _({type:"interactive",interactiveSkill:s});return await u.init(),u})),c=await Promise.all(e.delegates.map(async s=>{let u=t[s],h=new _({type:"delegate",expert:u});return await h.init(),h}));for(let s of a)o[s.name]=s;for(let s of i)o[s.name]=s;for(let s of c)o[s.name]=s;return o}async function Ge(e){for(let t of Object.values(e))await t.close();}async function N(e,t){for(let o of Object.values(e)){let r=await o.getToolDefinitions();for(let a of r)if(a.name===t)return o}throw new Error(`Tool ${t} not found`)}async function Ke(e){let t={};for(let o of Object.values(e)){let r=await o.getToolDefinitions();for(let a of r)t[a.name]={description:a.description,parameters:jsonSchema(a.inputSchema)};}return t}async function qe({setting:e,checkpoint:t,step:o,skillManagers:r}){if(!o?.toolCall)throw new Error("No tool call found");let{id:a,toolName:i,args:c}=o.toolCall,s=await N(r,i);if(!s||!s.expert)throw new Error(`Delegation error: skill manager "${i}" not found`);if(!c||!c.query||typeof c.query!="string")throw new Error("Delegation error: query is undefined");return $e(e,t,{checkpoint:{...t,status:"stoppedByDelegate",delegateTo:{expert:{key:s.expert.key,name:s.expert.name,version:s.expert.version},toolCallId:a,toolName:i,query:c.query}},step:{...o,finishedAt:new Date().getTime()}})}async function Je({setting:e,checkpoint:t,step:o}){return Ee(e,t,{checkpoint:{...t,status:"stoppedByInteractiveTool"},step:{...o,finishedAt:new Date().getTime()}})}async function Ve({setting:e,checkpoint:t,step:o,skillManagers:r}){if(!o?.toolCall)throw new Error("No tool call found");let{id:a,skillName:i,toolName:c,args:s}=o.toolCall,u=await N(r,c);if(u.type!=="mcp")throw new Error(`Incorrect SkillType, required MCP, got ${u.type}`);let h=await u.callTool(c,s),d={id:a,skillName:i,toolName:c,result:h};if(i==="@perstack/base"){if(c==="think")return ve(e,t,{toolResult:d});if(c==="attemptCompletion")return Me(e,t,{toolResult:d});if(c==="readPdfFile")return we(e,t,{toolResult:d});if(c==="readImageFile")return Ie(e,t,{toolResult:d})}return Ce(e,t,{toolResult:d})}async function Ye({setting:e,checkpoint:t,step:o}){return e.maxSteps!==void 0&&t.stepNumber>e.maxSteps?Ne(e,t,{checkpoint:{...t,status:"stoppedByExceededMaxSteps"},step:{...o,finishedAt:new Date().getTime()}}):Fe(e,t,{checkpoint:{...t},step:{...o,finishedAt:new Date().getTime()},nextCheckpoint:{...t,id:createId(),stepNumber:t.stepNumber+1}})}function F(e){return {type:"userMessage",contents:e.map(t=>({...t,id:createId()})),id:createId()}}function U(e){return {type:"expertMessage",contents:e.map(t=>({...t,id:createId()})),id:createId()}}function C(e){return {type:"toolMessage",contents:e.map(t=>({...t,contents:t.contents.map(o=>({...o,id:createId()})),id:createId()})),id:createId()}}function W(e){switch(e.type){case "instructionMessage":return {role:"system",content:Wt(e.contents),providerOptions:e.cache?{anthropic:{cacheControl:{type:"ephemeral"}}}:void 0};case "userMessage":return {role:"user",content:Ht(e.contents),providerOptions:e.cache?{anthropic:{cacheControl:{type:"ephemeral"}}}:void 0};case "expertMessage":return {role:"assistant",content:Qt(e.contents),providerOptions:e.cache?{anthropic:{cacheControl:{type:"ephemeral"}}}:void 0};case "toolMessage":return {role:"tool",content:Zt(e.contents),providerOptions:e.cache?{anthropic:{cacheControl:{type:"ephemeral"}}}:void 0}}}function Wt(e){return e.reduce((t,o)=>dedent`
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// src/runtime.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { createId as createId7 } from "@paralleldrive/cuid2";
|
|
6
|
+
import TOML from "smol-toml";
|
|
7
|
+
import { createActor } from "xstate";
|
|
8
|
+
|
|
9
|
+
// src/default-store.ts
|
|
10
|
+
import { mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
11
|
+
|
|
12
|
+
// src/schemas/runtime.ts
|
|
13
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
14
|
+
import { z as z2 } from "zod";
|
|
15
|
+
|
|
16
|
+
// src/model.ts
|
|
17
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
18
|
+
import { google } from "@ai-sdk/google";
|
|
19
|
+
import { openai } from "@ai-sdk/openai";
|
|
20
|
+
var DefaultModel = "claude-4-sonnet-20250514";
|
|
21
|
+
var AnthropicModels = [
|
|
22
|
+
"claude-4-opus-20250514",
|
|
23
|
+
"claude-4-sonnet-20250514",
|
|
24
|
+
"claude-3-7-sonnet-20250219",
|
|
25
|
+
"claude-3-5-sonnet-latest",
|
|
26
|
+
"claude-3-5-sonnet-20241022",
|
|
27
|
+
"claude-3-5-sonnet-20240620",
|
|
28
|
+
"claude-3-5-haiku-latest",
|
|
29
|
+
"claude-3-5-haiku-20241022"
|
|
30
|
+
// "claude-3-opus-latest", - No pdf support
|
|
31
|
+
// "claude-3-opus-20240229", - No pdf support
|
|
32
|
+
// "claude-3-sonnet-20240229", - No pdf support
|
|
33
|
+
// "claude-3-haiku-20240307", - No pdf support
|
|
34
|
+
];
|
|
35
|
+
var GoogleModels = [
|
|
36
|
+
"gemini-2.5-pro-preview-05-06",
|
|
37
|
+
"gemini-2.5-pro-exp-03-25",
|
|
38
|
+
"gemini-2.5-flash-preview-04-17",
|
|
39
|
+
"gemini-2.0-pro-exp-02-05",
|
|
40
|
+
"gemini-2.0-flash-thinking-exp-01-21",
|
|
41
|
+
"gemini-2.0-flash",
|
|
42
|
+
"gemini-2.0-flash-001",
|
|
43
|
+
"gemini-2.0-flash-live-001",
|
|
44
|
+
"gemini-2.0-flash-lite",
|
|
45
|
+
"gemini-2.0-flash-exp",
|
|
46
|
+
"gemini-1.5-pro",
|
|
47
|
+
"gemini-1.5-pro-latest",
|
|
48
|
+
"gemini-1.5-pro-001",
|
|
49
|
+
"gemini-1.5-pro-002",
|
|
50
|
+
"gemini-1.5-flash",
|
|
51
|
+
"gemini-1.5-flash-latest",
|
|
52
|
+
"gemini-1.5-flash-001",
|
|
53
|
+
"gemini-1.5-flash-002",
|
|
54
|
+
"gemini-1.5-flash-8b",
|
|
55
|
+
"gemini-1.5-flash-8b-latest",
|
|
56
|
+
"gemini-1.5-flash-8b-001"
|
|
57
|
+
// "gemini-exp-1206", - Unclear model spec
|
|
58
|
+
// "gemma-3-27b-it", - Unclear model spec
|
|
59
|
+
// "learnlm-1.5-pro-experimental", - Unclear model spec
|
|
60
|
+
];
|
|
61
|
+
var OpenAIModels = [
|
|
62
|
+
"o4-mini",
|
|
63
|
+
"o4-mini-2025-04-16",
|
|
64
|
+
"o3",
|
|
65
|
+
"o3-2025-04-16",
|
|
66
|
+
"o3-mini",
|
|
67
|
+
"o3-mini-2025-01-31",
|
|
68
|
+
"o1",
|
|
69
|
+
"o1-2024-12-17",
|
|
70
|
+
"o1-mini",
|
|
71
|
+
"o1-mini-2024-09-12",
|
|
72
|
+
// "o1-preview", - No tool support
|
|
73
|
+
// "o1-preview-2024-09-12", - No tool support
|
|
74
|
+
"gpt-4.5-preview",
|
|
75
|
+
"gpt-4.5-preview-2025-02-27",
|
|
76
|
+
"gpt-4.1",
|
|
77
|
+
"gpt-4.1-2025-04-14",
|
|
78
|
+
"gpt-4.1-mini",
|
|
79
|
+
"gpt-4.1-mini-2025-04-14",
|
|
80
|
+
"gpt-4.1-nano",
|
|
81
|
+
"gpt-4.1-nano-2025-04-14",
|
|
82
|
+
"gpt-4o",
|
|
83
|
+
"gpt-4o-2024-05-13",
|
|
84
|
+
"gpt-4o-2024-08-06",
|
|
85
|
+
"gpt-4o-2024-11-20",
|
|
86
|
+
"gpt-4o-audio-preview",
|
|
87
|
+
"gpt-4o-audio-preview-2024-10-01",
|
|
88
|
+
"gpt-4o-audio-preview-2024-12-17",
|
|
89
|
+
"gpt-4o-search-preview",
|
|
90
|
+
"gpt-4o-search-preview-2025-03-11",
|
|
91
|
+
"gpt-4o-mini-search-preview",
|
|
92
|
+
"gpt-4o-mini-search-preview-2025-03-11",
|
|
93
|
+
"gpt-4o-mini",
|
|
94
|
+
"gpt-4o-mini-2024-07-18"
|
|
95
|
+
// "gpt-4-turbo", - Legacy model
|
|
96
|
+
// "gpt-4-turbo-2024-04-09", - Legacy model
|
|
97
|
+
// "gpt-4-turbo-preview", - Legacy model
|
|
98
|
+
// "gpt-4-0125-preview", - No image input support
|
|
99
|
+
// "gpt-4-1106-preview", - No image input support
|
|
100
|
+
// "gpt-4", - No image input support
|
|
101
|
+
// "gpt-4-0613", - No image input support
|
|
102
|
+
// "gpt-3.5-turbo-0125", - Legacy model
|
|
103
|
+
// "gpt-3.5-turbo", - Legacy model
|
|
104
|
+
// "gpt-3.5-turbo-1106", - Legacy model
|
|
105
|
+
// "chatgpt-4o-latest", - No tool support
|
|
106
|
+
];
|
|
107
|
+
var SupportedModels = Object.fromEntries(
|
|
108
|
+
[...AnthropicModels, ...GoogleModels, ...OpenAIModels].map((model) => [
|
|
109
|
+
model,
|
|
110
|
+
{ default: model === DefaultModel, model }
|
|
111
|
+
])
|
|
112
|
+
);
|
|
113
|
+
function getDefaultModelName() {
|
|
114
|
+
const model = Object.values(SupportedModels).find((model2) => model2.default);
|
|
115
|
+
if (!model) {
|
|
116
|
+
throw new Error("No default model found");
|
|
117
|
+
}
|
|
118
|
+
return model.model;
|
|
119
|
+
}
|
|
120
|
+
function getModel(modelId) {
|
|
121
|
+
const unwrappedModelId = modelId ?? getDefaultModelName();
|
|
122
|
+
if (AnthropicModels.includes(unwrappedModelId)) {
|
|
123
|
+
return anthropic(unwrappedModelId);
|
|
124
|
+
}
|
|
125
|
+
if (GoogleModels.includes(unwrappedModelId)) {
|
|
126
|
+
return google(unwrappedModelId);
|
|
127
|
+
}
|
|
128
|
+
if (OpenAIModels.includes(unwrappedModelId)) {
|
|
129
|
+
return openai(unwrappedModelId);
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Unsupported model: ${unwrappedModelId}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/schemas/messages.ts
|
|
135
|
+
import { z } from "zod";
|
|
136
|
+
var BasePartSchema = z.object({
|
|
137
|
+
id: z.string()
|
|
138
|
+
});
|
|
139
|
+
var TextPartSchema = BasePartSchema.extend({
|
|
140
|
+
type: z.literal("textPart"),
|
|
141
|
+
text: z.string()
|
|
142
|
+
});
|
|
143
|
+
var ImageUrlPartSchema = BasePartSchema.extend({
|
|
144
|
+
type: z.literal("imageUrlPart"),
|
|
145
|
+
url: z.string().url(),
|
|
146
|
+
mimeType: z.string()
|
|
147
|
+
});
|
|
148
|
+
var ImageInlinePartSchema = BasePartSchema.extend({
|
|
149
|
+
type: z.literal("imageInlinePart"),
|
|
150
|
+
encodedData: z.string(),
|
|
151
|
+
mimeType: z.string()
|
|
152
|
+
});
|
|
153
|
+
var ImageBinaryPartSchema = BasePartSchema.extend({
|
|
154
|
+
type: z.literal("imageBinaryPart"),
|
|
155
|
+
data: z.string(),
|
|
156
|
+
mimeType: z.string()
|
|
157
|
+
});
|
|
158
|
+
var FileUrlPartSchema = BasePartSchema.extend({
|
|
159
|
+
type: z.literal("fileUrlPart"),
|
|
160
|
+
url: z.string().url(),
|
|
161
|
+
mimeType: z.string()
|
|
162
|
+
});
|
|
163
|
+
var FileInlinePartSchema = BasePartSchema.extend({
|
|
164
|
+
type: z.literal("fileInlinePart"),
|
|
165
|
+
encodedData: z.string(),
|
|
166
|
+
mimeType: z.string()
|
|
167
|
+
});
|
|
168
|
+
var FileBinaryPartSchema = BasePartSchema.extend({
|
|
169
|
+
type: z.literal("fileBinaryPart"),
|
|
170
|
+
data: z.string(),
|
|
171
|
+
mimeType: z.string()
|
|
172
|
+
});
|
|
173
|
+
var ToolCallPartSchema = BasePartSchema.extend({
|
|
174
|
+
type: z.literal("toolCallPart"),
|
|
175
|
+
toolCallId: z.string(),
|
|
176
|
+
toolName: z.string(),
|
|
177
|
+
args: z.unknown()
|
|
178
|
+
});
|
|
179
|
+
var ToolResultPartSchema = BasePartSchema.extend({
|
|
180
|
+
type: z.literal("toolResultPart"),
|
|
181
|
+
toolCallId: z.string(),
|
|
182
|
+
toolName: z.string(),
|
|
183
|
+
contents: z.array(z.union([TextPartSchema, ImageInlinePartSchema])),
|
|
184
|
+
isError: z.boolean().optional()
|
|
185
|
+
});
|
|
186
|
+
var BaseMessageSchema = z.object({
|
|
187
|
+
id: z.string()
|
|
188
|
+
});
|
|
189
|
+
var InstructionMessageSchema = BaseMessageSchema.extend({
|
|
190
|
+
type: z.literal("instructionMessage"),
|
|
191
|
+
contents: z.array(TextPartSchema),
|
|
192
|
+
cache: z.boolean().optional()
|
|
193
|
+
});
|
|
194
|
+
var UserMessageSchema = BaseMessageSchema.extend({
|
|
195
|
+
type: z.literal("userMessage"),
|
|
196
|
+
contents: z.array(
|
|
197
|
+
z.union([
|
|
198
|
+
TextPartSchema,
|
|
199
|
+
ImageUrlPartSchema,
|
|
200
|
+
ImageInlinePartSchema,
|
|
201
|
+
ImageBinaryPartSchema,
|
|
202
|
+
FileUrlPartSchema,
|
|
203
|
+
FileInlinePartSchema,
|
|
204
|
+
FileBinaryPartSchema
|
|
205
|
+
])
|
|
206
|
+
),
|
|
207
|
+
cache: z.boolean().optional()
|
|
208
|
+
});
|
|
209
|
+
var ExpertMessageSchema = BaseMessageSchema.extend({
|
|
210
|
+
type: z.literal("expertMessage"),
|
|
211
|
+
contents: z.array(z.union([TextPartSchema, ToolCallPartSchema])),
|
|
212
|
+
cache: z.boolean().optional()
|
|
213
|
+
});
|
|
214
|
+
var ToolMessageSchema = BaseMessageSchema.extend({
|
|
215
|
+
type: z.literal("toolMessage"),
|
|
216
|
+
contents: z.array(ToolResultPartSchema),
|
|
217
|
+
cache: z.boolean().optional()
|
|
218
|
+
});
|
|
219
|
+
var MessageSchema = z.union([
|
|
220
|
+
InstructionMessageSchema,
|
|
221
|
+
UserMessageSchema,
|
|
222
|
+
ExpertMessageSchema,
|
|
223
|
+
ToolMessageSchema
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
// src/schemas/runtime.ts
|
|
227
|
+
var expertNameRegex = /^(@[a-z0-9][a-z0-9_-]*\/)?[a-z0-9][a-z0-9_-]*$/;
|
|
228
|
+
var versionRegex = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
|
|
229
|
+
var tagNameRegex = /^[a-z0-9][a-z0-9_-]*$/;
|
|
230
|
+
var expertKeyRegex = /^((?:@[a-z0-9][a-z0-9_\.-]*\/)?[a-z0-9][a-z0-9_\.-]*)(?:@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?)|@([a-z0-9][a-z0-9_\.-]*))?$/;
|
|
231
|
+
var skillNameRegex = /^[a-z0-9][a-z0-9._-]*$/;
|
|
232
|
+
var maxNameLength = 214;
|
|
233
|
+
function parseExpertKey(expertKey) {
|
|
234
|
+
const match = expertKey.match(expertKeyRegex);
|
|
235
|
+
if (!match) {
|
|
236
|
+
throw new Error(`Invalid expert key format: ${expertKey}`);
|
|
237
|
+
}
|
|
238
|
+
const [key, name, version, tag] = match;
|
|
239
|
+
return {
|
|
240
|
+
key,
|
|
241
|
+
name,
|
|
242
|
+
version,
|
|
243
|
+
tag
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
var McpStdioSkillSchema = z2.object({
|
|
247
|
+
type: z2.literal("mcpStdioSkill"),
|
|
248
|
+
name: z2.string(),
|
|
249
|
+
description: z2.string().optional(),
|
|
250
|
+
rule: z2.string().optional(),
|
|
251
|
+
pick: z2.array(z2.string()).optional().default([]),
|
|
252
|
+
omit: z2.array(z2.string()).optional().default([]),
|
|
253
|
+
command: z2.string(),
|
|
254
|
+
packageName: z2.string().optional(),
|
|
255
|
+
args: z2.array(z2.string()).optional().default([]),
|
|
256
|
+
requiredEnv: z2.array(z2.string()).optional().default([])
|
|
257
|
+
});
|
|
258
|
+
var McpSseSkillSchema = z2.object({
|
|
259
|
+
type: z2.literal("mcpSseSkill"),
|
|
260
|
+
name: z2.string(),
|
|
261
|
+
description: z2.string().optional(),
|
|
262
|
+
rule: z2.string().optional(),
|
|
263
|
+
pick: z2.array(z2.string()).optional().default([]),
|
|
264
|
+
omit: z2.array(z2.string()).optional().default([]),
|
|
265
|
+
endpoint: z2.string()
|
|
266
|
+
});
|
|
267
|
+
var InteractiveToolSchema = z2.object({
|
|
268
|
+
name: z2.string(),
|
|
269
|
+
description: z2.string().optional(),
|
|
270
|
+
inputJsonSchema: z2.string()
|
|
271
|
+
});
|
|
272
|
+
var InteractiveSkillSchema = z2.object({
|
|
273
|
+
type: z2.literal("interactiveSkill"),
|
|
274
|
+
name: z2.string(),
|
|
275
|
+
description: z2.string().optional(),
|
|
276
|
+
rule: z2.string().optional(),
|
|
277
|
+
tools: z2.record(z2.string(), InteractiveToolSchema.omit({ name: true })).transform((tools) => {
|
|
278
|
+
return Object.fromEntries(
|
|
279
|
+
Object.entries(tools).map(([key, toolWithoutName]) => [
|
|
280
|
+
key,
|
|
281
|
+
InteractiveToolSchema.parse({ ...toolWithoutName, name: key })
|
|
282
|
+
])
|
|
283
|
+
);
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
var ExpertSchema = z2.object({
|
|
287
|
+
key: z2.string().regex(expertKeyRegex).min(1),
|
|
288
|
+
name: z2.string().regex(expertNameRegex).min(1).max(maxNameLength),
|
|
289
|
+
version: z2.string().regex(versionRegex),
|
|
290
|
+
description: z2.string().min(1).max(1024 * 2).optional(),
|
|
291
|
+
instruction: z2.string().min(1).max(1024 * 20),
|
|
292
|
+
skills: z2.record(
|
|
293
|
+
z2.string(),
|
|
294
|
+
z2.discriminatedUnion("type", [
|
|
295
|
+
McpStdioSkillSchema.omit({ name: true }),
|
|
296
|
+
McpSseSkillSchema.omit({ name: true }),
|
|
297
|
+
InteractiveSkillSchema.omit({ name: true })
|
|
298
|
+
])
|
|
299
|
+
).optional().default({
|
|
300
|
+
"@perstack/base": {
|
|
301
|
+
type: "mcpStdioSkill",
|
|
302
|
+
description: "Base skill",
|
|
303
|
+
command: "npx",
|
|
304
|
+
args: ["-y", "@perstack/base"]
|
|
305
|
+
}
|
|
306
|
+
}).transform((skills) => {
|
|
307
|
+
return Object.fromEntries(
|
|
308
|
+
Object.entries(skills).map(([key, skillWithoutName]) => [
|
|
309
|
+
key,
|
|
310
|
+
z2.discriminatedUnion("type", [
|
|
311
|
+
McpStdioSkillSchema,
|
|
312
|
+
McpSseSkillSchema,
|
|
313
|
+
InteractiveSkillSchema
|
|
314
|
+
]).parse({ ...skillWithoutName, name: key })
|
|
315
|
+
])
|
|
316
|
+
);
|
|
317
|
+
}),
|
|
318
|
+
delegates: z2.array(z2.string().regex(expertKeyRegex).min(1)).optional().default([]),
|
|
319
|
+
tags: z2.array(z2.string().regex(tagNameRegex).min(1)).optional().default([])
|
|
320
|
+
});
|
|
321
|
+
var UsageSchema = z2.object({
|
|
322
|
+
promptTokens: z2.number(),
|
|
323
|
+
completionTokens: z2.number(),
|
|
324
|
+
totalTokens: z2.number(),
|
|
325
|
+
cacheCreationInputTokens: z2.number(),
|
|
326
|
+
cacheReadInputTokens: z2.number()
|
|
327
|
+
});
|
|
328
|
+
var CheckpointStatusSchema = z2.enum([
|
|
329
|
+
"init",
|
|
330
|
+
"proceeding",
|
|
331
|
+
"completed",
|
|
332
|
+
"stoppedByInteractiveTool",
|
|
333
|
+
"stoppedByDelegate",
|
|
334
|
+
"stoppedByExceededMaxSteps",
|
|
335
|
+
"stoppedByError"
|
|
336
|
+
]);
|
|
337
|
+
var CheckpointSchema = z2.object({
|
|
338
|
+
id: z2.string(),
|
|
339
|
+
runId: z2.string(),
|
|
340
|
+
status: CheckpointStatusSchema,
|
|
341
|
+
stepNumber: z2.number(),
|
|
342
|
+
messages: z2.array(MessageSchema),
|
|
343
|
+
expert: z2.object({
|
|
344
|
+
key: z2.string(),
|
|
345
|
+
name: z2.string(),
|
|
346
|
+
version: z2.string()
|
|
347
|
+
}),
|
|
348
|
+
delegateTo: z2.object({
|
|
349
|
+
expert: z2.object({
|
|
350
|
+
key: z2.string(),
|
|
351
|
+
name: z2.string(),
|
|
352
|
+
version: z2.string()
|
|
353
|
+
}),
|
|
354
|
+
toolCallId: z2.string(),
|
|
355
|
+
toolName: z2.string(),
|
|
356
|
+
query: z2.string()
|
|
357
|
+
}).optional(),
|
|
358
|
+
delegatedBy: z2.object({
|
|
359
|
+
expert: z2.object({
|
|
360
|
+
key: z2.string(),
|
|
361
|
+
name: z2.string(),
|
|
362
|
+
version: z2.string()
|
|
363
|
+
}),
|
|
364
|
+
toolCallId: z2.string(),
|
|
365
|
+
toolName: z2.string(),
|
|
366
|
+
checkpointId: z2.string()
|
|
367
|
+
}).optional(),
|
|
368
|
+
usage: UsageSchema
|
|
369
|
+
});
|
|
370
|
+
var RunParamsSchema = z2.object({
|
|
371
|
+
setting: z2.object({
|
|
372
|
+
runId: z2.string().optional().transform((value) => value ?? createId()),
|
|
373
|
+
expertKey: z2.string().regex(expertKeyRegex).min(1),
|
|
374
|
+
input: z2.object({
|
|
375
|
+
text: z2.string().optional(),
|
|
376
|
+
interactiveToolCallResult: z2.object({
|
|
377
|
+
toolCallId: z2.string(),
|
|
378
|
+
toolName: z2.string(),
|
|
379
|
+
text: z2.string()
|
|
380
|
+
}).optional()
|
|
381
|
+
}),
|
|
382
|
+
experts: z2.record(z2.string().regex(expertKeyRegex).min(1), ExpertSchema.omit({ key: true })).optional().default({}).transform(
|
|
383
|
+
(experts) => Object.fromEntries(
|
|
384
|
+
Object.entries(experts).map(([key, expertWithoutKey]) => [
|
|
385
|
+
key,
|
|
386
|
+
ExpertSchema.parse({
|
|
387
|
+
...expertWithoutKey,
|
|
388
|
+
key
|
|
389
|
+
})
|
|
390
|
+
])
|
|
391
|
+
)
|
|
392
|
+
),
|
|
393
|
+
model: z2.string().optional().default(getDefaultModelName()),
|
|
394
|
+
temperature: z2.number().min(0).max(1).optional().default(0.3),
|
|
395
|
+
maxSteps: z2.number().min(1).optional(),
|
|
396
|
+
maxRetries: z2.number().min(0).optional().default(10),
|
|
397
|
+
/** Optional workspace directory path. If provided, sets WORKSPACE_DIR environment variable for skill execution. */
|
|
398
|
+
workspace: z2.string().optional(),
|
|
399
|
+
startedAt: z2.number().optional().default(Date.now()),
|
|
400
|
+
updatedAt: z2.number().optional().default(Date.now())
|
|
401
|
+
}),
|
|
402
|
+
checkpoint: CheckpointSchema.optional()
|
|
403
|
+
});
|
|
404
|
+
function createEvent(type) {
|
|
405
|
+
return (setting, checkpoint, data) => {
|
|
406
|
+
return {
|
|
407
|
+
type,
|
|
408
|
+
id: createId(),
|
|
409
|
+
expertKey: checkpoint.expert.key,
|
|
410
|
+
timestamp: Date.now(),
|
|
411
|
+
runId: setting.runId,
|
|
412
|
+
stepNumber: checkpoint.stepNumber,
|
|
413
|
+
...data
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
var startRun = createEvent("startRun");
|
|
418
|
+
var startGeneration = createEvent("startGeneration");
|
|
419
|
+
var retry = createEvent("retry");
|
|
420
|
+
var callTool = createEvent("callTool");
|
|
421
|
+
var callInteractiveTool = createEvent("callInteractiveTool");
|
|
422
|
+
var callDelegate = createEvent("callDelegate");
|
|
423
|
+
var resolveToolResult = createEvent("resolveToolResult");
|
|
424
|
+
var resolveThought = createEvent("resolveThought");
|
|
425
|
+
var resolvePdfFile = createEvent("resolvePdfFile");
|
|
426
|
+
var resolveImageFile = createEvent("resolveImageFile");
|
|
427
|
+
var attemptCompletion = createEvent("attemptCompletion");
|
|
428
|
+
var finishToolCall = createEvent("finishToolCall");
|
|
429
|
+
var completeRun = createEvent("completeRun");
|
|
430
|
+
var stopRunByInteractiveTool = createEvent("stopRunByInteractiveTool");
|
|
431
|
+
var stopRunByDelegate = createEvent("stopRunByDelegate");
|
|
432
|
+
var stopRunByExceededMaxSteps = createEvent("stopRunByExceededMaxSteps");
|
|
433
|
+
var continueToNextStep = createEvent("continueToNextStep");
|
|
434
|
+
|
|
435
|
+
// src/default-store.ts
|
|
436
|
+
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
437
|
+
const runDir = getRunDir(runId);
|
|
438
|
+
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
439
|
+
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
440
|
+
);
|
|
441
|
+
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
442
|
+
if (!checkpointFile) {
|
|
443
|
+
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
444
|
+
}
|
|
445
|
+
const checkpointPath = `${runDir}/${checkpointFile.name}`;
|
|
446
|
+
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
447
|
+
return CheckpointSchema.parse(JSON.parse(checkpoint));
|
|
448
|
+
}
|
|
449
|
+
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
450
|
+
const { id, runId, stepNumber } = checkpoint;
|
|
451
|
+
const runDir = getRunDir(runId);
|
|
452
|
+
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
453
|
+
await mkdir(runDir, { recursive: true });
|
|
454
|
+
await writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
|
|
455
|
+
}
|
|
456
|
+
async function defaultStoreEvent(event) {
|
|
457
|
+
const { timestamp, runId, stepNumber, type } = event;
|
|
458
|
+
const runDir = getRunDir(runId);
|
|
459
|
+
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
460
|
+
await mkdir(runDir, { recursive: true });
|
|
461
|
+
await writeFile(eventPath, JSON.stringify(event, null, 2));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// package.json
|
|
465
|
+
var package_default = {
|
|
466
|
+
name: "@perstack/runtime",
|
|
467
|
+
version: "0.0.11",
|
|
468
|
+
description: "PerStack Runtime",
|
|
469
|
+
author: "Wintermute Technologies, Inc.",
|
|
470
|
+
license: "Apache-2.0",
|
|
471
|
+
type: "module",
|
|
472
|
+
exports: {
|
|
473
|
+
".": "./src/index.ts"
|
|
474
|
+
},
|
|
475
|
+
publishConfig: {
|
|
476
|
+
exports: {
|
|
477
|
+
".": "./dist/index.js"
|
|
478
|
+
},
|
|
479
|
+
types: "./dist/index.d.ts"
|
|
480
|
+
},
|
|
481
|
+
files: [
|
|
482
|
+
"dist"
|
|
483
|
+
],
|
|
484
|
+
scripts: {
|
|
485
|
+
clean: "rm -rf dist",
|
|
486
|
+
dev: "tsup --watch --config ../../tsup.config.ts",
|
|
487
|
+
build: "npm run clean && tsup --config ../../tsup.config.ts",
|
|
488
|
+
prepublishOnly: "npm run clean && npm run build"
|
|
489
|
+
},
|
|
490
|
+
dependencies: {
|
|
491
|
+
"@ai-sdk/anthropic": "^1.2.12",
|
|
492
|
+
"@ai-sdk/google": "^1.2.19",
|
|
493
|
+
"@ai-sdk/openai": "^1.3.22",
|
|
494
|
+
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
495
|
+
"@paralleldrive/cuid2": "^2.2.2",
|
|
496
|
+
ai: "^4.3.16",
|
|
497
|
+
"smol-toml": "^1.3.4",
|
|
498
|
+
"ts-dedent": "^2.2.0",
|
|
499
|
+
xstate: "^5.19.4",
|
|
500
|
+
zod: "^3.25.67"
|
|
501
|
+
},
|
|
502
|
+
devDependencies: {
|
|
503
|
+
"@tsconfig/node22": "^22.0.2",
|
|
504
|
+
"@types/node": "^24.0.3",
|
|
505
|
+
tsup: "^8.5.0",
|
|
506
|
+
typescript: "^5.8.3",
|
|
507
|
+
vitest: "^3.2.3"
|
|
508
|
+
},
|
|
509
|
+
engines: {
|
|
510
|
+
node: ">=22.0.0"
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// src/events/default-event-listener.ts
|
|
515
|
+
var log = console.info;
|
|
516
|
+
var debug = console.debug;
|
|
517
|
+
var header = (e) => {
|
|
518
|
+
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
519
|
+
const stepNumber = e.stepNumber;
|
|
520
|
+
const key = e.expertKey;
|
|
521
|
+
return `${t} ${stepNumber} ${key}`;
|
|
522
|
+
};
|
|
523
|
+
async function defaultEventListener(e) {
|
|
524
|
+
await defaultStoreEvent(e);
|
|
525
|
+
switch (e.type) {
|
|
526
|
+
case "startRun": {
|
|
527
|
+
log(`${header(e)} \u{1F99C} Perstack@${package_default.version} started`);
|
|
528
|
+
const { inputMessages } = e;
|
|
529
|
+
for (const message of inputMessages) {
|
|
530
|
+
if (message.type === "userMessage") {
|
|
531
|
+
logUserMessage(message);
|
|
532
|
+
} else if (message.type === "toolMessage") {
|
|
533
|
+
logToolMessage(message);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
case "startGeneration": {
|
|
539
|
+
log(`${header(e)} \u{1F99C} Generating tool call`);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
case "retry": {
|
|
543
|
+
logUsage(e);
|
|
544
|
+
log(`${header(e)} \u{1F99C} Retrying tool call generation`);
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
case "callTool": {
|
|
548
|
+
logUsage(e);
|
|
549
|
+
log(`${header(e)} \u{1F99C} Calling tool`);
|
|
550
|
+
if (e.toolCall.skillName === "@perstack/base") {
|
|
551
|
+
switch (e.toolCall.toolName) {
|
|
552
|
+
case "think": {
|
|
553
|
+
const thought = e.toolCall.args.thought;
|
|
554
|
+
log(`${header(e)} \u{1F4AD} Thought Updated:`);
|
|
555
|
+
debug(thought);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case "readPdfFile": {
|
|
559
|
+
const path2 = e.toolCall.args.path;
|
|
560
|
+
log(`${header(e)} \u{1F4C4} Reading PDF: ${path2}`);
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
case "readImageFile": {
|
|
564
|
+
const path2 = e.toolCall.args.path;
|
|
565
|
+
log(`${header(e)} \u{1F5BC}\uFE0F Reading Image: ${path2}`);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
default: {
|
|
569
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
570
|
+
log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
576
|
+
log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
case "callInteractiveTool": {
|
|
581
|
+
logUsage(e);
|
|
582
|
+
log(`${header(e)} \u{1F99C} Calling interactive tool`);
|
|
583
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
584
|
+
log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
case "callDelegate": {
|
|
588
|
+
logUsage(e);
|
|
589
|
+
log(`${header(e)} \u{1F99C} Calling delegate`);
|
|
590
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolCall.toolName}`);
|
|
591
|
+
log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "resolveToolResult": {
|
|
595
|
+
log(`${header(e)} \u{1F527} Resolved Tool Result`);
|
|
596
|
+
if (e.toolResult.skillName === "@perstack/base") {
|
|
597
|
+
switch (e.toolResult.toolName) {
|
|
598
|
+
case "todo": {
|
|
599
|
+
const text = e.toolResult.result.find((r) => r.type === "textPart")?.text;
|
|
600
|
+
const { todos } = JSON.parse(text ?? "{}");
|
|
601
|
+
log(`${header(e)} \u{1F4C4} Todo:`);
|
|
602
|
+
for (const todo of todos) {
|
|
603
|
+
debug(`${todo.completed ? "[x]" : "[ ]"} ${todo.id}: ${todo.title}`);
|
|
604
|
+
}
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
default: {
|
|
608
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
609
|
+
log(`${header(e)} \u{1F527} Result: ${e.toolResult.result}`);
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
} else {
|
|
614
|
+
log(`${header(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
615
|
+
log(`${header(e)} \u{1F527} Result: ${e.toolResult.result}`);
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case "resolveThought": {
|
|
620
|
+
log(`${header(e)} \u{1F4AD} Resolved Thought:`, e.toolResult);
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case "resolvePdfFile": {
|
|
624
|
+
log(`${header(e)} \u{1F4C4} Resolved PDF:`, e.toolResult);
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "resolveImageFile": {
|
|
628
|
+
log(`${header(e)} \u{1F5BC}\uFE0F Resolved Image:`, e.toolResult);
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
case "attemptCompletion": {
|
|
632
|
+
log(`${header(e)} \u2705 Attempting completion`);
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
case "completeRun": {
|
|
636
|
+
logUsage(e);
|
|
637
|
+
log(`${header(e)} \u{1F99C} Completing run`);
|
|
638
|
+
log(`${header(e)} \u{1F99C} Result:`, e.text);
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
case "stopRunByInteractiveTool": {
|
|
642
|
+
log(`${header(e)} \u{1F99C} Stopping run by interactive tool`);
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
case "stopRunByDelegate": {
|
|
646
|
+
log(`${header(e)} \u{1F99C} Stopping run by delegate`);
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
case "stopRunByExceededMaxSteps": {
|
|
650
|
+
log(`${header(e)} \u{1F99C} Stopping run by exceeded max steps`);
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
case "continueToNextStep": {
|
|
654
|
+
log(`${header(e)} \u{1F99C} Continuing to next step`);
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function logUserMessage(message) {
|
|
660
|
+
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
661
|
+
const contents = message.contents;
|
|
662
|
+
for (const content of contents) {
|
|
663
|
+
if (content.type === "textPart") {
|
|
664
|
+
log(`${t} \u{1F4AC} User: ${content.text}`);
|
|
665
|
+
} else if (content.type === "imageUrlPart") {
|
|
666
|
+
log(`${t} \u{1F5BC}\uFE0F User: ${content.url}`);
|
|
667
|
+
} else if (content.type === "imageInlinePart") {
|
|
668
|
+
log(`${t} \u{1F5BC}\uFE0F User: Inline image`);
|
|
669
|
+
} else if (content.type === "imageBinaryPart") {
|
|
670
|
+
log(`${t} \u{1F5BC}\uFE0F User: Binary image`);
|
|
671
|
+
} else if (content.type === "fileUrlPart") {
|
|
672
|
+
log(`${t} \u{1F4C4} User: ${content.url}`);
|
|
673
|
+
} else if (content.type === "fileInlinePart") {
|
|
674
|
+
log(`${t} \u{1F4C4} User: Inline file`);
|
|
675
|
+
} else if (content.type === "fileBinaryPart") {
|
|
676
|
+
log(`${t} \u{1F4C4} User: Binary file`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function logToolMessage(message) {
|
|
681
|
+
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
682
|
+
const contents = message.contents;
|
|
683
|
+
for (const content of contents) {
|
|
684
|
+
if (content.type === "toolResultPart") {
|
|
685
|
+
const { contents: contents2 } = content;
|
|
686
|
+
for (const content2 of contents2) {
|
|
687
|
+
if (content2.type === "textPart") {
|
|
688
|
+
log(`${t} \u{1F4AC} Tool Result: ${content2.text}`);
|
|
689
|
+
} else if (content2.type === "imageInlinePart") {
|
|
690
|
+
log(`${t} \u{1F5BC}\uFE0F Tool Result: Inline image`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function logUsage(e) {
|
|
697
|
+
const usageByGeneration = [
|
|
698
|
+
`In: ${e.usage.promptTokens.toLocaleString()}`,
|
|
699
|
+
`Out: ${e.usage.completionTokens.toLocaleString()}`,
|
|
700
|
+
`Total: ${e.usage.totalTokens.toLocaleString()}`,
|
|
701
|
+
`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
|
|
702
|
+
`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
|
|
703
|
+
].join(", ");
|
|
704
|
+
log(`${header(e)} \u{1F4CA} Tokens usage: ${usageByGeneration}`);
|
|
705
|
+
const usageByRun = [
|
|
706
|
+
`In: ${e.usage.promptTokens.toLocaleString()}`,
|
|
707
|
+
`Out: ${e.usage.completionTokens.toLocaleString()}`,
|
|
708
|
+
`Total: ${e.usage.totalTokens.toLocaleString()}`,
|
|
709
|
+
`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
|
|
710
|
+
`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
|
|
711
|
+
].join(", ");
|
|
712
|
+
log(`${header(e)} \u{1F4CA} Total usage: ${usageByRun}`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/events/event-emitter.ts
|
|
716
|
+
import { createId as createId2 } from "@paralleldrive/cuid2";
|
|
717
|
+
var RunEventEmitter = class {
|
|
718
|
+
listeners = [];
|
|
719
|
+
subscribe(listener) {
|
|
720
|
+
this.listeners.push(listener);
|
|
721
|
+
}
|
|
722
|
+
async emit(event) {
|
|
723
|
+
for (const listener of this.listeners) {
|
|
724
|
+
await listener({
|
|
725
|
+
...event,
|
|
726
|
+
id: createId2(),
|
|
727
|
+
timestamp: Date.now()
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/schemas/api-registry.ts
|
|
734
|
+
import { z as z3 } from "zod";
|
|
735
|
+
var RegistryV1ExpertSchema = z3.object({
|
|
736
|
+
name: z3.string(),
|
|
737
|
+
version: z3.string(),
|
|
738
|
+
minRuntimeVersion: z3.string(),
|
|
739
|
+
description: z3.string(),
|
|
740
|
+
instruction: z3.string(),
|
|
741
|
+
skills: z3.record(
|
|
742
|
+
z3.string(),
|
|
743
|
+
z3.discriminatedUnion("type", [
|
|
744
|
+
McpStdioSkillSchema.omit({ name: true }),
|
|
745
|
+
McpSseSkillSchema.omit({ name: true }),
|
|
746
|
+
InteractiveSkillSchema.omit({ name: true })
|
|
747
|
+
])
|
|
748
|
+
),
|
|
749
|
+
delegates: z3.array(z3.string()),
|
|
750
|
+
tags: z3.array(z3.string()),
|
|
751
|
+
status: z3.string(),
|
|
752
|
+
owner: z3.object({ name: z3.string() }),
|
|
753
|
+
createdAt: z3.string().datetime(),
|
|
754
|
+
updatedAt: z3.string().datetime()
|
|
755
|
+
});
|
|
756
|
+
var RegistryV1ExpertsGetResponseSchema = z3.object({
|
|
757
|
+
data: z3.object({
|
|
758
|
+
expert: RegistryV1ExpertSchema
|
|
759
|
+
})
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// src/api-clinent.ts
|
|
763
|
+
var Perstack = class {
|
|
764
|
+
baseUrl;
|
|
765
|
+
registry;
|
|
766
|
+
apiKey;
|
|
767
|
+
constructor(params) {
|
|
768
|
+
this.baseUrl = params?.baseUrl ?? process.env.PERSTACK_API_BASE_URL ?? "https://api.perstack.ai";
|
|
769
|
+
this.apiKey = params?.apiKey ?? process.env.PERSTACK_API_KEY;
|
|
770
|
+
this.registry = new RegistryClientV1(this);
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var RegistryClientV1 = class {
|
|
774
|
+
client;
|
|
775
|
+
endpoint;
|
|
776
|
+
constructor(client) {
|
|
777
|
+
this.client = client;
|
|
778
|
+
this.endpoint = "/api/registry/v1/experts";
|
|
779
|
+
}
|
|
780
|
+
async get(expertKey) {
|
|
781
|
+
const safeExpertKey = encodeURIComponent(expertKey);
|
|
782
|
+
const url = new URL(`${this.endpoint}/${safeExpertKey}`, this.client.baseUrl);
|
|
783
|
+
const headers = {
|
|
784
|
+
"Content-Type": "application/json",
|
|
785
|
+
Authorization: `Bearer ${this.client.apiKey}`
|
|
786
|
+
};
|
|
787
|
+
const result = await fetch(url.toString(), { headers });
|
|
788
|
+
if (!result.ok) {
|
|
789
|
+
throw new Error(
|
|
790
|
+
`Registry returned non-200 status code: ${result.status}, reason: ${result.statusText}`
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
const json = await result.json();
|
|
794
|
+
return RegistryV1ExpertsGetResponseSchema.parse(json);
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// src/resolve-expert-to-run.ts
|
|
799
|
+
async function resolveExpertToRun(expertKey, experts) {
|
|
800
|
+
if (experts[expertKey]) {
|
|
801
|
+
return experts[expertKey];
|
|
802
|
+
}
|
|
803
|
+
const client = new Perstack();
|
|
804
|
+
const { data } = await client.registry.get(expertKey);
|
|
805
|
+
const expert = ExpertSchema.parse({
|
|
806
|
+
...data.expert,
|
|
807
|
+
key: expertKey
|
|
808
|
+
});
|
|
809
|
+
experts[expertKey] = expert;
|
|
810
|
+
return expert;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/runtime-state-machine.ts
|
|
814
|
+
import { assign, setup } from "xstate";
|
|
815
|
+
|
|
816
|
+
// src/skill-manager.ts
|
|
817
|
+
import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
|
|
818
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
819
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
820
|
+
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
821
|
+
import { createId as createId3 } from "@paralleldrive/cuid2";
|
|
822
|
+
import { jsonSchema } from "ai";
|
|
823
|
+
import { ZodError } from "zod";
|
|
824
|
+
var SkillManager = class {
|
|
825
|
+
_toolDefinitions = [];
|
|
826
|
+
_initialized = false;
|
|
827
|
+
name;
|
|
828
|
+
type;
|
|
829
|
+
skill;
|
|
830
|
+
interactiveSkill;
|
|
831
|
+
expert;
|
|
832
|
+
_mcpClient;
|
|
833
|
+
_params;
|
|
834
|
+
constructor(params) {
|
|
835
|
+
this._params = params;
|
|
836
|
+
this.type = params.type;
|
|
837
|
+
switch (params.type) {
|
|
838
|
+
case "mcp":
|
|
839
|
+
this.name = params.skill.name;
|
|
840
|
+
this.skill = params.skill;
|
|
841
|
+
break;
|
|
842
|
+
case "interactive":
|
|
843
|
+
this.name = params.interactiveSkill.name;
|
|
844
|
+
this.interactiveSkill = params.interactiveSkill;
|
|
845
|
+
break;
|
|
846
|
+
case "delegate":
|
|
847
|
+
this.name = params.expert.name;
|
|
848
|
+
this.expert = params.expert;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
async init() {
|
|
853
|
+
switch (this._params.type) {
|
|
854
|
+
case "mcp": {
|
|
855
|
+
await this._initMcpSkill(this._params);
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
case "interactive": {
|
|
859
|
+
await this._initInteractiveSkill(this._params);
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
case "delegate": {
|
|
863
|
+
await this._initDelegate(this._params);
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
async _initMcpSkill(params) {
|
|
869
|
+
this._mcpClient = new McpClient({
|
|
870
|
+
name: `${params.skill.name}-mcp-client`,
|
|
871
|
+
version: "1.0.0"
|
|
872
|
+
});
|
|
873
|
+
switch (params.skill.type) {
|
|
874
|
+
case "mcpStdioSkill": {
|
|
875
|
+
if (!params.skill.command) {
|
|
876
|
+
throw new Error(`Skill ${params.skill.name} has no command`);
|
|
877
|
+
}
|
|
878
|
+
const { command, args } = this._getCommandArgs(params.skill);
|
|
879
|
+
console.error(command, args);
|
|
880
|
+
const transport = new StdioClientTransport({
|
|
881
|
+
command,
|
|
882
|
+
args,
|
|
883
|
+
env: Object.fromEntries(
|
|
884
|
+
Object.entries(process.env).filter(([_, value]) => value !== void 0 && value !== "").map(([key, value]) => [key, value])
|
|
885
|
+
)
|
|
886
|
+
});
|
|
887
|
+
await this._mcpClient.connect(transport);
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
890
|
+
case "mcpSseSkill": {
|
|
891
|
+
if (!params.skill.endpoint) {
|
|
892
|
+
throw new Error(`Skill ${params.skill.name} has no endpoint`);
|
|
893
|
+
}
|
|
894
|
+
const transport = new SSEClientTransport(new URL(params.skill.endpoint));
|
|
895
|
+
await this._mcpClient.connect(transport);
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const { tools } = await this._mcpClient.listTools();
|
|
900
|
+
this._toolDefinitions = tools.map((tool) => ({
|
|
901
|
+
skillName: params.skill.name,
|
|
902
|
+
name: tool.name,
|
|
903
|
+
description: tool.description,
|
|
904
|
+
inputSchema: tool.inputSchema,
|
|
905
|
+
interactive: false
|
|
906
|
+
}));
|
|
907
|
+
this._initialized = true;
|
|
908
|
+
}
|
|
909
|
+
_getCommandArgs(skill) {
|
|
910
|
+
const { name, command, packageName, args } = skill;
|
|
911
|
+
if (!packageName && (!args || args.length === 0)) {
|
|
912
|
+
throw new Error(`Skill ${name} has no packageName or args. Please provide one of them.`);
|
|
913
|
+
}
|
|
914
|
+
if (packageName && args && args.length > 0) {
|
|
915
|
+
throw new Error(
|
|
916
|
+
`Skill ${name} has both packageName and args. Please provide only one of them.`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
let newArgs = args && args.length > 0 ? args : [packageName];
|
|
920
|
+
if (command === "npx" && !newArgs.includes("-y")) {
|
|
921
|
+
newArgs = ["-y", ...newArgs];
|
|
922
|
+
}
|
|
923
|
+
return { command, args: newArgs };
|
|
924
|
+
}
|
|
925
|
+
async _initInteractiveSkill(params) {
|
|
926
|
+
this._toolDefinitions = Object.values(params.interactiveSkill.tools).map((tool) => ({
|
|
927
|
+
skillName: params.interactiveSkill.name,
|
|
928
|
+
name: tool.name,
|
|
929
|
+
description: tool.description,
|
|
930
|
+
inputSchema: JSON.parse(tool.inputJsonSchema),
|
|
931
|
+
interactive: true
|
|
932
|
+
}));
|
|
933
|
+
this._initialized = true;
|
|
934
|
+
}
|
|
935
|
+
async _initDelegate(params) {
|
|
936
|
+
this._toolDefinitions = [
|
|
937
|
+
{
|
|
938
|
+
skillName: params.expert.name,
|
|
939
|
+
name: params.expert.name.split("/").pop() ?? params.expert.name,
|
|
940
|
+
description: params.expert.description,
|
|
941
|
+
inputSchema: {
|
|
942
|
+
type: "object",
|
|
943
|
+
properties: { query: { type: "string" } },
|
|
944
|
+
required: ["query"]
|
|
945
|
+
},
|
|
946
|
+
interactive: false
|
|
947
|
+
}
|
|
948
|
+
];
|
|
949
|
+
this._initialized = true;
|
|
950
|
+
}
|
|
951
|
+
async close() {
|
|
952
|
+
if (this._mcpClient) {
|
|
953
|
+
await this._mcpClient.close();
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
async getToolDefinitions() {
|
|
957
|
+
if (!this._initialized) {
|
|
958
|
+
throw new Error(`${this.name} is not initialized`);
|
|
959
|
+
}
|
|
960
|
+
if (this._params.type === "mcp") {
|
|
961
|
+
const omit = this._params.skill.omit ?? [];
|
|
962
|
+
const pick = this._params.skill.pick ?? [];
|
|
963
|
+
return this._toolDefinitions.filter((tool) => omit.length > 0 ? !omit.includes(tool.name) : true).filter((tool) => pick.length > 0 ? pick.includes(tool.name) : true);
|
|
964
|
+
}
|
|
965
|
+
return this._toolDefinitions;
|
|
966
|
+
}
|
|
967
|
+
async callTool(toolName, input) {
|
|
968
|
+
switch (this._params.type) {
|
|
969
|
+
case "mcp": {
|
|
970
|
+
if (!this._mcpClient) {
|
|
971
|
+
throw new Error(`${this.name} is not initialized`);
|
|
972
|
+
}
|
|
973
|
+
try {
|
|
974
|
+
const result = await this._mcpClient.callTool({
|
|
975
|
+
name: toolName,
|
|
976
|
+
arguments: input
|
|
977
|
+
});
|
|
978
|
+
return this._convertToolResult(result, toolName, input);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
return this._handleToolError(error, toolName);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
case "interactive": {
|
|
984
|
+
return [];
|
|
985
|
+
}
|
|
986
|
+
case "delegate": {
|
|
987
|
+
return [];
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
_handleToolError(error, toolName) {
|
|
992
|
+
if (error instanceof McpError) {
|
|
993
|
+
return [
|
|
994
|
+
{
|
|
995
|
+
type: "textPart",
|
|
996
|
+
text: `Error calling tool ${toolName}: ${error.message}`,
|
|
997
|
+
id: createId3()
|
|
998
|
+
}
|
|
999
|
+
];
|
|
1000
|
+
}
|
|
1001
|
+
if (error instanceof ZodError) {
|
|
1002
|
+
return [{ type: "textPart", text: `Invalid tool call arguments: ${error}`, id: createId3() }];
|
|
1003
|
+
}
|
|
1004
|
+
throw error;
|
|
1005
|
+
}
|
|
1006
|
+
_convertToolResult(result, toolName, input) {
|
|
1007
|
+
if (!result.content || result.content.length === 0) {
|
|
1008
|
+
return [
|
|
1009
|
+
{
|
|
1010
|
+
type: "textPart",
|
|
1011
|
+
text: `Tool ${toolName} returned nothing with arguments: ${JSON.stringify(input)}`,
|
|
1012
|
+
id: createId3()
|
|
1013
|
+
}
|
|
1014
|
+
];
|
|
1015
|
+
}
|
|
1016
|
+
return result.content.filter((part) => part.type !== "audio" && part.type !== "resource_link").map((part) => this._convertPart(part));
|
|
1017
|
+
}
|
|
1018
|
+
_convertPart(part) {
|
|
1019
|
+
switch (part.type) {
|
|
1020
|
+
case "text":
|
|
1021
|
+
if (!part.text || part.text === "") {
|
|
1022
|
+
return { type: "textPart", text: "Error: No content", id: createId3() };
|
|
1023
|
+
}
|
|
1024
|
+
return { type: "textPart", text: part.text, id: createId3() };
|
|
1025
|
+
case "image":
|
|
1026
|
+
if (!part.data || !part.mimeType) {
|
|
1027
|
+
throw new Error("Image part must have both data and mimeType");
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
type: "imageInlinePart",
|
|
1031
|
+
encodedData: part.data,
|
|
1032
|
+
mimeType: part.mimeType,
|
|
1033
|
+
id: createId3()
|
|
1034
|
+
};
|
|
1035
|
+
case "resource":
|
|
1036
|
+
if (!part.resource) {
|
|
1037
|
+
throw new Error("Resource part must have resource content");
|
|
1038
|
+
}
|
|
1039
|
+
return this._convertResource(part.resource);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
_convertResource(resource) {
|
|
1043
|
+
if (!resource.mimeType) {
|
|
1044
|
+
throw new Error(`Resource ${JSON.stringify(resource)} has no mimeType`);
|
|
1045
|
+
}
|
|
1046
|
+
if (resource.text && typeof resource.text === "string") {
|
|
1047
|
+
return { type: "textPart", text: resource.text, id: createId3() };
|
|
1048
|
+
}
|
|
1049
|
+
if (resource.blob && typeof resource.blob === "string") {
|
|
1050
|
+
return {
|
|
1051
|
+
type: "fileInlinePart",
|
|
1052
|
+
encodedData: resource.blob,
|
|
1053
|
+
mimeType: resource.mimeType,
|
|
1054
|
+
id: createId3()
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
throw new Error(`Unsupported resource type: ${JSON.stringify(resource)}`);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
async function getSkillManagers(expert, experts) {
|
|
1061
|
+
const skillManagers = {};
|
|
1062
|
+
const { skills } = expert;
|
|
1063
|
+
if (!skills["@perstack/base"]) {
|
|
1064
|
+
skills["@perstack/base"] = {
|
|
1065
|
+
type: "mcpStdioSkill",
|
|
1066
|
+
name: "@perstack/base",
|
|
1067
|
+
description: "The base skill for Perstack",
|
|
1068
|
+
pick: [],
|
|
1069
|
+
omit: [],
|
|
1070
|
+
command: "npx",
|
|
1071
|
+
args: ["-y", "@perstack/base"],
|
|
1072
|
+
requiredEnv: []
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
const mcpSkillManagers = await Promise.all(
|
|
1076
|
+
Object.values(skills).filter((skill) => skill.type === "mcpStdioSkill" || skill.type === "mcpSseSkill").map(async (skill) => {
|
|
1077
|
+
const skillManager = new SkillManager({
|
|
1078
|
+
type: "mcp",
|
|
1079
|
+
skill
|
|
1080
|
+
});
|
|
1081
|
+
await skillManager.init();
|
|
1082
|
+
return skillManager;
|
|
1083
|
+
})
|
|
1084
|
+
);
|
|
1085
|
+
const interactiveSkillManagers = await Promise.all(
|
|
1086
|
+
Object.values(skills).filter((skill) => skill.type === "interactiveSkill").map(async (interactiveSkill) => {
|
|
1087
|
+
const skillManager = new SkillManager({
|
|
1088
|
+
type: "interactive",
|
|
1089
|
+
interactiveSkill
|
|
1090
|
+
});
|
|
1091
|
+
await skillManager.init();
|
|
1092
|
+
return skillManager;
|
|
1093
|
+
})
|
|
1094
|
+
);
|
|
1095
|
+
const delegateSkillManagers = await Promise.all(
|
|
1096
|
+
expert.delegates.map(async (delegateExpertName) => {
|
|
1097
|
+
const delegate = experts[delegateExpertName];
|
|
1098
|
+
const skillManager = new SkillManager({
|
|
1099
|
+
type: "delegate",
|
|
1100
|
+
expert: delegate
|
|
1101
|
+
});
|
|
1102
|
+
await skillManager.init();
|
|
1103
|
+
return skillManager;
|
|
1104
|
+
})
|
|
1105
|
+
);
|
|
1106
|
+
for (const skillManager of mcpSkillManagers) {
|
|
1107
|
+
skillManagers[skillManager.name] = skillManager;
|
|
1108
|
+
}
|
|
1109
|
+
for (const skillManager of interactiveSkillManagers) {
|
|
1110
|
+
skillManagers[skillManager.name] = skillManager;
|
|
1111
|
+
}
|
|
1112
|
+
for (const skillManager of delegateSkillManagers) {
|
|
1113
|
+
skillManagers[skillManager.name] = skillManager;
|
|
1114
|
+
}
|
|
1115
|
+
return skillManagers;
|
|
1116
|
+
}
|
|
1117
|
+
async function closeSkillManagers(skillManagers) {
|
|
1118
|
+
for (const skillManager of Object.values(skillManagers)) {
|
|
1119
|
+
await skillManager.close();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
async function getSkillManagerByToolName(skillManagers, toolName) {
|
|
1123
|
+
for (const skillManager of Object.values(skillManagers)) {
|
|
1124
|
+
const toolDefinitions = await skillManager.getToolDefinitions();
|
|
1125
|
+
for (const toolDefinition of toolDefinitions) {
|
|
1126
|
+
if (toolDefinition.name === toolName) {
|
|
1127
|
+
return skillManager;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
throw new Error(`Tool ${toolName} not found`);
|
|
1132
|
+
}
|
|
1133
|
+
async function getToolSet(skillManagers) {
|
|
1134
|
+
const tools = {};
|
|
1135
|
+
for (const skillManager of Object.values(skillManagers)) {
|
|
1136
|
+
const toolDefinitions = await skillManager.getToolDefinitions();
|
|
1137
|
+
for (const toolDefinition of toolDefinitions) {
|
|
1138
|
+
tools[toolDefinition.name] = {
|
|
1139
|
+
description: toolDefinition.description,
|
|
1140
|
+
parameters: jsonSchema(toolDefinition.inputSchema)
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return tools;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// src/states/calling-delegate.ts
|
|
1148
|
+
async function callingDelegateLogic({
|
|
1149
|
+
setting,
|
|
1150
|
+
checkpoint,
|
|
1151
|
+
step,
|
|
1152
|
+
skillManagers
|
|
1153
|
+
}) {
|
|
1154
|
+
if (!step?.toolCall) {
|
|
1155
|
+
throw new Error("No tool call found");
|
|
1156
|
+
}
|
|
1157
|
+
const { id, toolName, args } = step.toolCall;
|
|
1158
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
|
|
1159
|
+
if (!skillManager || !skillManager.expert) {
|
|
1160
|
+
throw new Error(`Delegation error: skill manager "${toolName}" not found`);
|
|
1161
|
+
}
|
|
1162
|
+
if (!args || !args.query || typeof args.query !== "string") {
|
|
1163
|
+
throw new Error("Delegation error: query is undefined");
|
|
1164
|
+
}
|
|
1165
|
+
return stopRunByDelegate(setting, checkpoint, {
|
|
1166
|
+
checkpoint: {
|
|
1167
|
+
...checkpoint,
|
|
1168
|
+
status: "stoppedByDelegate",
|
|
1169
|
+
delegateTo: {
|
|
1170
|
+
expert: {
|
|
1171
|
+
key: skillManager.expert.key,
|
|
1172
|
+
name: skillManager.expert.name,
|
|
1173
|
+
version: skillManager.expert.version
|
|
1174
|
+
},
|
|
1175
|
+
toolCallId: id,
|
|
1176
|
+
toolName,
|
|
1177
|
+
query: args.query
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
step: {
|
|
1181
|
+
...step,
|
|
1182
|
+
finishedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/states/calling-interactive-tool.ts
|
|
1188
|
+
async function callingInteractiveToolLogic({
|
|
1189
|
+
setting,
|
|
1190
|
+
checkpoint,
|
|
1191
|
+
step
|
|
1192
|
+
}) {
|
|
1193
|
+
return stopRunByInteractiveTool(setting, checkpoint, {
|
|
1194
|
+
checkpoint: {
|
|
1195
|
+
...checkpoint,
|
|
1196
|
+
status: "stoppedByInteractiveTool"
|
|
1197
|
+
},
|
|
1198
|
+
step: {
|
|
1199
|
+
...step,
|
|
1200
|
+
finishedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// src/states/calling-tool.ts
|
|
1206
|
+
async function callingToolLogic({
|
|
1207
|
+
setting,
|
|
1208
|
+
checkpoint,
|
|
1209
|
+
step,
|
|
1210
|
+
skillManagers
|
|
1211
|
+
}) {
|
|
1212
|
+
if (!step?.toolCall) {
|
|
1213
|
+
throw new Error("No tool call found");
|
|
1214
|
+
}
|
|
1215
|
+
const { id, skillName, toolName, args } = step.toolCall;
|
|
1216
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
|
|
1217
|
+
if (skillManager.type !== "mcp") {
|
|
1218
|
+
throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
|
|
1219
|
+
}
|
|
1220
|
+
const result = await skillManager.callTool(toolName, args);
|
|
1221
|
+
const toolResult = { id, skillName, toolName, result };
|
|
1222
|
+
if (skillName === "@perstack/base") {
|
|
1223
|
+
if (toolName === "think") {
|
|
1224
|
+
return resolveThought(setting, checkpoint, { toolResult });
|
|
1225
|
+
}
|
|
1226
|
+
if (toolName === "attemptCompletion") {
|
|
1227
|
+
return attemptCompletion(setting, checkpoint, { toolResult });
|
|
1228
|
+
}
|
|
1229
|
+
if (toolName === "readPdfFile") {
|
|
1230
|
+
return resolvePdfFile(setting, checkpoint, { toolResult });
|
|
1231
|
+
}
|
|
1232
|
+
if (toolName === "readImageFile") {
|
|
1233
|
+
return resolveImageFile(setting, checkpoint, { toolResult });
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return resolveToolResult(setting, checkpoint, { toolResult });
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/states/finishing-step.ts
|
|
1240
|
+
import { createId as createId4 } from "@paralleldrive/cuid2";
|
|
1241
|
+
async function finishingStepLogic({
|
|
1242
|
+
setting,
|
|
1243
|
+
checkpoint,
|
|
1244
|
+
step
|
|
1245
|
+
}) {
|
|
1246
|
+
if (setting.maxSteps !== void 0 && checkpoint.stepNumber > setting.maxSteps) {
|
|
1247
|
+
return stopRunByExceededMaxSteps(setting, checkpoint, {
|
|
1248
|
+
checkpoint: {
|
|
1249
|
+
...checkpoint,
|
|
1250
|
+
status: "stoppedByExceededMaxSteps"
|
|
1251
|
+
},
|
|
1252
|
+
step: {
|
|
1253
|
+
...step,
|
|
1254
|
+
finishedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
return continueToNextStep(setting, checkpoint, {
|
|
1259
|
+
checkpoint: {
|
|
1260
|
+
...checkpoint
|
|
1261
|
+
},
|
|
1262
|
+
step: {
|
|
1263
|
+
...step,
|
|
1264
|
+
finishedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
1265
|
+
},
|
|
1266
|
+
nextCheckpoint: {
|
|
1267
|
+
...checkpoint,
|
|
1268
|
+
id: createId4(),
|
|
1269
|
+
stepNumber: checkpoint.stepNumber + 1
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// src/states/generating-run-result.ts
|
|
1275
|
+
import { generateText } from "ai";
|
|
1276
|
+
|
|
1277
|
+
// src/messages/message.ts
|
|
1278
|
+
import { createId as createId5 } from "@paralleldrive/cuid2";
|
|
1279
|
+
import { dedent } from "ts-dedent";
|
|
1280
|
+
function createUserMessage(contents) {
|
|
1281
|
+
return {
|
|
1282
|
+
type: "userMessage",
|
|
1283
|
+
contents: contents.map((part) => ({
|
|
1284
|
+
...part,
|
|
1285
|
+
id: createId5()
|
|
1286
|
+
})),
|
|
1287
|
+
id: createId5()
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function createExpertMessage(contents) {
|
|
1291
|
+
return {
|
|
1292
|
+
type: "expertMessage",
|
|
1293
|
+
contents: contents.map((part) => ({
|
|
1294
|
+
...part,
|
|
1295
|
+
id: createId5()
|
|
1296
|
+
})),
|
|
1297
|
+
id: createId5()
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
function createToolMessage(contents) {
|
|
1301
|
+
return {
|
|
1302
|
+
type: "toolMessage",
|
|
1303
|
+
contents: contents.map((part) => ({
|
|
1304
|
+
...part,
|
|
1305
|
+
contents: part.contents.map((part2) => ({
|
|
1306
|
+
...part2,
|
|
1307
|
+
id: createId5()
|
|
1308
|
+
})),
|
|
1309
|
+
id: createId5()
|
|
1310
|
+
})),
|
|
1311
|
+
id: createId5()
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
function messageToCoreMessage(message) {
|
|
1315
|
+
switch (message.type) {
|
|
1316
|
+
case "instructionMessage":
|
|
1317
|
+
return {
|
|
1318
|
+
role: "system",
|
|
1319
|
+
content: instructionContentsToCoreContent(message.contents),
|
|
1320
|
+
providerOptions: message.cache ? {
|
|
1321
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
1322
|
+
} : void 0
|
|
1323
|
+
};
|
|
1324
|
+
case "userMessage":
|
|
1325
|
+
return {
|
|
1326
|
+
role: "user",
|
|
1327
|
+
content: userContentsToCoreContent(message.contents),
|
|
1328
|
+
providerOptions: message.cache ? {
|
|
1329
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
1330
|
+
} : void 0
|
|
1331
|
+
};
|
|
1332
|
+
case "expertMessage":
|
|
1333
|
+
return {
|
|
1334
|
+
role: "assistant",
|
|
1335
|
+
content: expertContentsToCoreContent(message.contents),
|
|
1336
|
+
providerOptions: message.cache ? {
|
|
1337
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
1338
|
+
} : void 0
|
|
1339
|
+
};
|
|
1340
|
+
case "toolMessage":
|
|
1341
|
+
return {
|
|
1342
|
+
role: "tool",
|
|
1343
|
+
content: toolContentsToCoreContent(message.contents),
|
|
1344
|
+
providerOptions: message.cache ? {
|
|
1345
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
1346
|
+
} : void 0
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
function instructionContentsToCoreContent(contents) {
|
|
1351
|
+
return contents.reduce((acc, part) => {
|
|
1352
|
+
return dedent`
|
|
1353
|
+
${acc}
|
|
1354
|
+
${part.text}
|
|
1355
|
+
`.trim();
|
|
1356
|
+
}, "");
|
|
1357
|
+
}
|
|
1358
|
+
function userContentsToCoreContent(contents) {
|
|
1359
|
+
return contents.map((part) => {
|
|
1360
|
+
switch (part.type) {
|
|
1361
|
+
case "textPart":
|
|
1362
|
+
return textPartToCoreTextPart(part);
|
|
1363
|
+
case "imageUrlPart":
|
|
1364
|
+
return imageUrlPartToCoreImagePart(part);
|
|
1365
|
+
case "imageInlinePart":
|
|
1366
|
+
return imageInlinePartToCoreImagePart(part);
|
|
1367
|
+
case "imageBinaryPart":
|
|
1368
|
+
return imageBinaryPartToCoreImagePart(part);
|
|
1369
|
+
case "fileUrlPart":
|
|
1370
|
+
return fileUrlPartToCoreFilePart(part);
|
|
1371
|
+
case "fileInlinePart":
|
|
1372
|
+
return fileInlinePartToCoreFilePart(part);
|
|
1373
|
+
case "fileBinaryPart":
|
|
1374
|
+
return fileBinaryPartToCoreFilePart(part);
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
function expertContentsToCoreContent(contents) {
|
|
1379
|
+
return contents.map((part) => {
|
|
1380
|
+
switch (part.type) {
|
|
1381
|
+
case "textPart":
|
|
1382
|
+
return textPartToCoreTextPart(part);
|
|
1383
|
+
case "toolCallPart":
|
|
1384
|
+
return toolCallPartToCoreToolCallPart(part);
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
function toolContentsToCoreContent(contents) {
|
|
1389
|
+
return contents.map((part) => {
|
|
1390
|
+
switch (part.type) {
|
|
1391
|
+
case "toolResultPart":
|
|
1392
|
+
return toolResultPartToCoreToolResultPart(part);
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
function textPartToCoreTextPart(part) {
|
|
1397
|
+
return {
|
|
1398
|
+
type: "text",
|
|
1399
|
+
text: part.text
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
function imageUrlPartToCoreImagePart(part) {
|
|
1403
|
+
return {
|
|
1404
|
+
type: "image",
|
|
1405
|
+
image: part.url,
|
|
1406
|
+
mimeType: part.mimeType
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
function imageInlinePartToCoreImagePart(part) {
|
|
1410
|
+
return {
|
|
1411
|
+
type: "image",
|
|
1412
|
+
image: part.encodedData,
|
|
1413
|
+
mimeType: part.mimeType
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
function imageBinaryPartToCoreImagePart(part) {
|
|
1417
|
+
return {
|
|
1418
|
+
type: "image",
|
|
1419
|
+
image: part.data,
|
|
1420
|
+
mimeType: part.mimeType
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function fileUrlPartToCoreFilePart(part) {
|
|
1424
|
+
return {
|
|
1425
|
+
type: "file",
|
|
1426
|
+
data: part.url,
|
|
1427
|
+
mimeType: part.mimeType
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
function fileInlinePartToCoreFilePart(part) {
|
|
1431
|
+
return {
|
|
1432
|
+
type: "file",
|
|
1433
|
+
data: part.encodedData,
|
|
1434
|
+
mimeType: part.mimeType
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
function fileBinaryPartToCoreFilePart(part) {
|
|
1438
|
+
return {
|
|
1439
|
+
type: "file",
|
|
1440
|
+
data: part.data,
|
|
1441
|
+
mimeType: part.mimeType
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
function toolCallPartToCoreToolCallPart(part) {
|
|
1445
|
+
return {
|
|
1446
|
+
type: "tool-call",
|
|
1447
|
+
toolCallId: part.toolCallId,
|
|
1448
|
+
toolName: part.toolName,
|
|
1449
|
+
args: part.args
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function toolResultPartToCoreToolResultPart(part) {
|
|
1453
|
+
return {
|
|
1454
|
+
type: "tool-result",
|
|
1455
|
+
toolCallId: part.toolCallId,
|
|
1456
|
+
toolName: part.toolName,
|
|
1457
|
+
result: part.contents,
|
|
1458
|
+
experimental_content: part.contents.map((part2) => {
|
|
1459
|
+
switch (part2.type) {
|
|
1460
|
+
case "textPart":
|
|
1461
|
+
return {
|
|
1462
|
+
type: "text",
|
|
1463
|
+
text: part2.text
|
|
1464
|
+
};
|
|
1465
|
+
case "imageInlinePart":
|
|
1466
|
+
return {
|
|
1467
|
+
type: "image",
|
|
1468
|
+
data: part2.encodedData,
|
|
1469
|
+
mimeType: part2.mimeType
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
}),
|
|
1473
|
+
isError: part.isError
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/usage.ts
|
|
1478
|
+
function createEmptyUsage() {
|
|
1479
|
+
return {
|
|
1480
|
+
promptTokens: 0,
|
|
1481
|
+
completionTokens: 0,
|
|
1482
|
+
totalTokens: 0,
|
|
1483
|
+
cacheCreationInputTokens: 0,
|
|
1484
|
+
cacheReadInputTokens: 0
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
function usageFromGenerateTextResult(result) {
|
|
1488
|
+
let cacheCreationInputTokens = 0;
|
|
1489
|
+
let cacheReadInputTokens = 0;
|
|
1490
|
+
if (result.providerMetadata?.anthropic) {
|
|
1491
|
+
const anthropicMetadata = result.providerMetadata.anthropic;
|
|
1492
|
+
cacheCreationInputTokens = anthropicMetadata.cacheCreationInputTokens || 0;
|
|
1493
|
+
cacheReadInputTokens = anthropicMetadata.cacheReadInputTokens || 0;
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
promptTokens: result.usage.promptTokens,
|
|
1497
|
+
completionTokens: result.usage.completionTokens,
|
|
1498
|
+
totalTokens: result.usage.totalTokens,
|
|
1499
|
+
cacheCreationInputTokens,
|
|
1500
|
+
cacheReadInputTokens
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
function sumUsage(a, b) {
|
|
1504
|
+
return {
|
|
1505
|
+
promptTokens: a.promptTokens + b.promptTokens,
|
|
1506
|
+
completionTokens: a.completionTokens + b.completionTokens,
|
|
1507
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
1508
|
+
cacheCreationInputTokens: a.cacheCreationInputTokens + b.cacheCreationInputTokens,
|
|
1509
|
+
cacheReadInputTokens: a.cacheReadInputTokens + b.cacheReadInputTokens
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// src/states/generating-run-result.ts
|
|
1514
|
+
async function generatingRunResultLogic({
|
|
1515
|
+
setting,
|
|
1516
|
+
checkpoint,
|
|
1517
|
+
step
|
|
1518
|
+
}) {
|
|
1519
|
+
if (!step?.toolCall || !step?.toolResult) {
|
|
1520
|
+
throw new Error("No tool call or tool result found");
|
|
1521
|
+
}
|
|
1522
|
+
const { id, toolName } = step.toolCall;
|
|
1523
|
+
const { result } = step.toolResult;
|
|
1524
|
+
const toolMessage = createToolMessage([
|
|
1525
|
+
{
|
|
1526
|
+
type: "toolResultPart",
|
|
1527
|
+
toolCallId: id,
|
|
1528
|
+
toolName,
|
|
1529
|
+
contents: result.filter(
|
|
1530
|
+
(part) => part.type === "textPart" || part.type === "imageInlinePart"
|
|
1531
|
+
)
|
|
1532
|
+
}
|
|
1533
|
+
]);
|
|
1534
|
+
const model = getModel(setting.model);
|
|
1535
|
+
const { messages } = checkpoint;
|
|
1536
|
+
const generationResult = await generateText({
|
|
1537
|
+
model,
|
|
1538
|
+
messages: [...messages, toolMessage].map(messageToCoreMessage),
|
|
1539
|
+
temperature: setting.temperature,
|
|
1540
|
+
maxRetries: setting.maxRetries
|
|
1541
|
+
});
|
|
1542
|
+
const usage = usageFromGenerateTextResult(generationResult);
|
|
1543
|
+
const { text } = generationResult;
|
|
1544
|
+
const newMessages = [toolMessage, createExpertMessage(text ? [{ type: "textPart", text }] : [])];
|
|
1545
|
+
return completeRun(setting, checkpoint, {
|
|
1546
|
+
checkpoint: {
|
|
1547
|
+
...checkpoint,
|
|
1548
|
+
messages: [...messages, ...newMessages],
|
|
1549
|
+
usage: sumUsage(checkpoint.usage, usage),
|
|
1550
|
+
status: "completed"
|
|
1551
|
+
},
|
|
1552
|
+
step: {
|
|
1553
|
+
...step,
|
|
1554
|
+
newMessages: [...step.newMessages, ...newMessages],
|
|
1555
|
+
finishedAt: (/* @__PURE__ */ new Date()).getTime(),
|
|
1556
|
+
usage: sumUsage(step.usage, usage)
|
|
1557
|
+
},
|
|
1558
|
+
text,
|
|
1559
|
+
usage
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// src/states/generating-tool-call.ts
|
|
1564
|
+
import { generateText as generateText2 } from "ai";
|
|
1565
|
+
async function generatingToolCallLogic({
|
|
1566
|
+
setting,
|
|
1567
|
+
checkpoint,
|
|
1568
|
+
skillManagers
|
|
1569
|
+
}) {
|
|
1570
|
+
const { messages } = checkpoint;
|
|
1571
|
+
const model = getModel(setting.model);
|
|
1572
|
+
const result = await generateText2({
|
|
1573
|
+
model,
|
|
1574
|
+
messages: messages.map(messageToCoreMessage),
|
|
1575
|
+
temperature: setting.temperature,
|
|
1576
|
+
maxRetries: setting.maxRetries,
|
|
1577
|
+
tools: await getToolSet(skillManagers),
|
|
1578
|
+
toolChoice: "required"
|
|
1579
|
+
});
|
|
1580
|
+
const usage = usageFromGenerateTextResult(result);
|
|
1581
|
+
const { text, toolCalls, finishReason } = result;
|
|
1582
|
+
const toolCall = toolCalls[0];
|
|
1583
|
+
if (!toolCall) {
|
|
1584
|
+
return retry(setting, checkpoint, {
|
|
1585
|
+
newMessages: [
|
|
1586
|
+
createExpertMessage(text ? [{ type: "textPart", text }] : []),
|
|
1587
|
+
createUserMessage([
|
|
1588
|
+
{
|
|
1589
|
+
type: "textPart",
|
|
1590
|
+
text: "You must generate a tool call. Try again."
|
|
1591
|
+
}
|
|
1592
|
+
])
|
|
1593
|
+
],
|
|
1594
|
+
usage
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
if (finishReason !== "tool-calls") {
|
|
1598
|
+
switch (finishReason) {
|
|
1599
|
+
case "length": {
|
|
1600
|
+
return retry(setting, checkpoint, {
|
|
1601
|
+
newMessages: [
|
|
1602
|
+
createExpertMessage([
|
|
1603
|
+
{
|
|
1604
|
+
type: "toolCallPart",
|
|
1605
|
+
toolCallId: toolCall.toolCallId,
|
|
1606
|
+
toolName: toolCall.toolName,
|
|
1607
|
+
args: toolCall.args
|
|
1608
|
+
}
|
|
1609
|
+
]),
|
|
1610
|
+
createToolMessage([
|
|
1611
|
+
{
|
|
1612
|
+
type: "toolResultPart",
|
|
1613
|
+
toolCallId: toolCall.toolCallId,
|
|
1614
|
+
toolName: toolCall.toolName,
|
|
1615
|
+
contents: [{ type: "textPart", text: "Error: Generation length exceeded" }]
|
|
1616
|
+
}
|
|
1617
|
+
])
|
|
1618
|
+
],
|
|
1619
|
+
usage
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
default:
|
|
1623
|
+
throw new Error(`Unexpected finish reason: ${finishReason}`);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
const contents = [
|
|
1627
|
+
{
|
|
1628
|
+
type: "toolCallPart",
|
|
1629
|
+
toolCallId: toolCall.toolCallId,
|
|
1630
|
+
toolName: toolCall.toolName,
|
|
1631
|
+
args: toolCall.args
|
|
1632
|
+
}
|
|
1633
|
+
];
|
|
1634
|
+
if (text) {
|
|
1635
|
+
contents.push({
|
|
1636
|
+
type: "textPart",
|
|
1637
|
+
text
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
1641
|
+
const eventPayload = {
|
|
1642
|
+
newMessage: createExpertMessage(contents),
|
|
1643
|
+
toolCall: {
|
|
1644
|
+
id: toolCall.toolCallId,
|
|
1645
|
+
skillName: skillManager.name,
|
|
1646
|
+
toolName: toolCall.toolName,
|
|
1647
|
+
args: toolCall.args
|
|
1648
|
+
},
|
|
1649
|
+
usage
|
|
1650
|
+
};
|
|
1651
|
+
switch (skillManager.type) {
|
|
1652
|
+
case "mcp":
|
|
1653
|
+
return callTool(setting, checkpoint, eventPayload);
|
|
1654
|
+
case "interactive":
|
|
1655
|
+
return callInteractiveTool(setting, checkpoint, eventPayload);
|
|
1656
|
+
case "delegate":
|
|
1657
|
+
return callDelegate(setting, checkpoint, eventPayload);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// src/messages/instruction-message.ts
|
|
1662
|
+
import { createId as createId6 } from "@paralleldrive/cuid2";
|
|
1663
|
+
import { dedent as dedent2 } from "ts-dedent";
|
|
1664
|
+
var metaInstruction = dedent2`
|
|
5
1665
|
IMPORTANT:
|
|
6
1666
|
You are NOT an "interactive" AI agent.
|
|
7
1667
|
From the start of the agent loop until the completion of the task,
|
|
@@ -41,40 +1701,925 @@ import {existsSync}from'fs';import {readFile,writeFile,mkdir,readdir}from'fs/pro
|
|
|
41
1701
|
- 専門家である以上、ユーザーが期待する以上の品質のアウトプットを行ってください
|
|
42
1702
|
|
|
43
1703
|
Environment information:
|
|
44
|
-
- Current time is ${new Date().toISOString()}
|
|
1704
|
+
- Current time is ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
45
1705
|
- Current working directory is ${process.cwd()}
|
|
46
|
-
`;
|
|
1706
|
+
`;
|
|
1707
|
+
function createInstructionMessage(expert, experts) {
|
|
1708
|
+
const instruction = dedent2`
|
|
47
1709
|
You are Perstack, an AI expert that tackles tasks requested by users by utilizing all available tools.
|
|
48
1710
|
|
|
49
1711
|
(The following information describes your nature and role as an AI, the mechanisms of the AI system, and other meta-cognitive aspects.)
|
|
50
1712
|
|
|
51
|
-
${
|
|
1713
|
+
${metaInstruction}
|
|
52
1714
|
|
|
53
1715
|
---
|
|
54
1716
|
(The following describes the objective, steps, rules, etc. regarding your expert task.)
|
|
55
1717
|
|
|
56
|
-
${
|
|
1718
|
+
${expert.instruction}
|
|
57
1719
|
|
|
58
1720
|
---
|
|
59
1721
|
(The following is an overview of each skill and the rules for calling tools.)
|
|
60
1722
|
|
|
61
|
-
${
|
|
1723
|
+
${getSkillRules(expert)}
|
|
62
1724
|
|
|
63
1725
|
---
|
|
64
1726
|
(The following is an overview of each delegate expert and the rules for calling tools.)
|
|
65
1727
|
|
|
66
1728
|
You can delegate tasks to the following experts by calling delegate expert name as a tool:
|
|
67
1729
|
|
|
68
|
-
${
|
|
69
|
-
`;
|
|
70
|
-
|
|
1730
|
+
${getDelegateRules(expert, experts)}
|
|
1731
|
+
`;
|
|
1732
|
+
return {
|
|
1733
|
+
type: "instructionMessage",
|
|
1734
|
+
contents: [
|
|
1735
|
+
{
|
|
1736
|
+
id: createId6(),
|
|
1737
|
+
type: "textPart",
|
|
1738
|
+
text: instruction
|
|
1739
|
+
}
|
|
1740
|
+
],
|
|
1741
|
+
id: createId6(),
|
|
1742
|
+
cache: true
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
function getSkillRules(expert) {
|
|
1746
|
+
return Object.values(expert.skills).reduce((acc, skill) => {
|
|
1747
|
+
if (!skill.rule) {
|
|
1748
|
+
return acc;
|
|
1749
|
+
}
|
|
1750
|
+
return dedent2`
|
|
1751
|
+
${acc}
|
|
1752
|
+
|
|
1753
|
+
"${skill.name}" skill rules:
|
|
1754
|
+
${skill.rule}
|
|
1755
|
+
`.trim();
|
|
1756
|
+
}, "");
|
|
1757
|
+
}
|
|
1758
|
+
function getDelegateRules(expert, experts) {
|
|
1759
|
+
return expert.delegates.reduce((acc, delegateExpertName) => {
|
|
1760
|
+
const delegate = experts[delegateExpertName];
|
|
1761
|
+
if (!delegate) {
|
|
1762
|
+
return acc;
|
|
1763
|
+
}
|
|
1764
|
+
return dedent2`
|
|
1765
|
+
${acc}
|
|
1766
|
+
|
|
1767
|
+
About "${delegate.name}":
|
|
1768
|
+
${delegate.description}
|
|
1769
|
+
`.trim();
|
|
1770
|
+
}, "");
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
// src/states/init.ts
|
|
1774
|
+
async function initLogic({
|
|
1775
|
+
setting,
|
|
1776
|
+
checkpoint
|
|
1777
|
+
}) {
|
|
1778
|
+
const { expertKey, experts } = setting;
|
|
1779
|
+
const expert = experts[expertKey];
|
|
1780
|
+
switch (checkpoint.status) {
|
|
1781
|
+
case "init":
|
|
1782
|
+
return startRun(setting, checkpoint, {
|
|
1783
|
+
initialCheckpoint: checkpoint,
|
|
1784
|
+
inputMessages: [createInstructionMessage(expert, experts), getInputMessage(setting.input)]
|
|
1785
|
+
});
|
|
1786
|
+
default:
|
|
1787
|
+
return startRun(setting, checkpoint, {
|
|
1788
|
+
initialCheckpoint: checkpoint,
|
|
1789
|
+
inputMessages: [getInputMessage(setting.input)]
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
function getInputMessage(input) {
|
|
1794
|
+
if (input.text) {
|
|
1795
|
+
return createUserMessage([{ type: "textPart", text: input.text }]);
|
|
1796
|
+
}
|
|
1797
|
+
if (input.interactiveToolCallResult) {
|
|
1798
|
+
return createToolMessage([
|
|
1799
|
+
{
|
|
1800
|
+
type: "toolResultPart",
|
|
1801
|
+
toolCallId: input.interactiveToolCallResult.toolCallId,
|
|
1802
|
+
toolName: input.interactiveToolCallResult.toolName,
|
|
1803
|
+
contents: [{ type: "textPart", text: input.interactiveToolCallResult.text }]
|
|
1804
|
+
}
|
|
1805
|
+
]);
|
|
1806
|
+
}
|
|
1807
|
+
throw new Error("Input message is undefined");
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/states/preparing-for-step.ts
|
|
1811
|
+
async function preparingForStepLogic({
|
|
1812
|
+
setting,
|
|
1813
|
+
checkpoint
|
|
1814
|
+
}) {
|
|
1815
|
+
return startGeneration(setting, checkpoint, {
|
|
1816
|
+
messages: checkpoint.messages
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/states/resolving-image-file.ts
|
|
1821
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1822
|
+
async function resolvingImageFileLogic({
|
|
1823
|
+
setting,
|
|
1824
|
+
checkpoint,
|
|
1825
|
+
step
|
|
1826
|
+
}) {
|
|
1827
|
+
if (!step?.toolCall || !step?.toolResult) {
|
|
1828
|
+
throw new Error("No tool call or tool result found");
|
|
1829
|
+
}
|
|
1830
|
+
const { id, toolName } = step.toolCall;
|
|
1831
|
+
const { result } = step.toolResult;
|
|
1832
|
+
const textParts = result.filter((part) => part.type === "textPart");
|
|
1833
|
+
const files = [];
|
|
1834
|
+
for (const textPart of textParts) {
|
|
1835
|
+
let imageInfo;
|
|
1836
|
+
try {
|
|
1837
|
+
imageInfo = JSON.parse(textPart.text);
|
|
1838
|
+
} catch {
|
|
1839
|
+
files.push({
|
|
1840
|
+
type: "textPart",
|
|
1841
|
+
text: textPart.text
|
|
1842
|
+
});
|
|
1843
|
+
continue;
|
|
1844
|
+
}
|
|
1845
|
+
const { path: path2, mimeType, size } = imageInfo;
|
|
1846
|
+
const file = await readFile2(path2).then((buffer) => ({
|
|
1847
|
+
encodedData: buffer.toString("base64"),
|
|
1848
|
+
mimeType,
|
|
1849
|
+
size
|
|
1850
|
+
}));
|
|
1851
|
+
files.push({
|
|
1852
|
+
type: "imageInlinePart",
|
|
1853
|
+
encodedData: file.encodedData,
|
|
1854
|
+
mimeType: file.mimeType
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
return finishToolCall(setting, checkpoint, {
|
|
1858
|
+
newMessages: [
|
|
1859
|
+
createToolMessage([
|
|
1860
|
+
{
|
|
1861
|
+
type: "toolResultPart",
|
|
1862
|
+
toolCallId: id,
|
|
1863
|
+
toolName,
|
|
1864
|
+
contents: files
|
|
1865
|
+
}
|
|
1866
|
+
])
|
|
1867
|
+
]
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// src/states/resolving-pdf-file.ts
|
|
1872
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1873
|
+
async function resolvingPdfFileLogic({
|
|
1874
|
+
setting,
|
|
1875
|
+
checkpoint,
|
|
1876
|
+
step
|
|
1877
|
+
}) {
|
|
1878
|
+
if (!step?.toolCall || !step?.toolResult) {
|
|
1879
|
+
throw new Error("No tool call or tool result found");
|
|
1880
|
+
}
|
|
1881
|
+
const { id, toolName } = step.toolCall;
|
|
1882
|
+
const { result } = step.toolResult;
|
|
1883
|
+
const textParts = result.filter((part) => part.type === "textPart");
|
|
1884
|
+
const files = [];
|
|
1885
|
+
for (const textPart of textParts) {
|
|
1886
|
+
let pdfInfo;
|
|
1887
|
+
try {
|
|
1888
|
+
pdfInfo = JSON.parse(textPart.text);
|
|
1889
|
+
} catch {
|
|
1890
|
+
files.push({
|
|
1891
|
+
type: "textPart",
|
|
1892
|
+
text: textPart.text
|
|
1893
|
+
});
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
const { path: path2, mimeType, size } = pdfInfo;
|
|
1897
|
+
const file = await readFile3(path2).then((buffer) => ({
|
|
1898
|
+
encodedData: buffer.toString("base64"),
|
|
1899
|
+
mimeType,
|
|
1900
|
+
size
|
|
1901
|
+
}));
|
|
1902
|
+
files.push({
|
|
1903
|
+
type: "fileInlinePart",
|
|
1904
|
+
encodedData: file.encodedData,
|
|
1905
|
+
mimeType: file.mimeType
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
return finishToolCall(setting, checkpoint, {
|
|
1909
|
+
newMessages: [
|
|
1910
|
+
createToolMessage([
|
|
1911
|
+
{
|
|
1912
|
+
type: "toolResultPart",
|
|
1913
|
+
toolCallId: id,
|
|
1914
|
+
toolName,
|
|
1915
|
+
contents: [
|
|
1916
|
+
{
|
|
1917
|
+
type: "textPart",
|
|
1918
|
+
text: "User uploads PDF file as follows."
|
|
1919
|
+
}
|
|
1920
|
+
]
|
|
1921
|
+
}
|
|
1922
|
+
]),
|
|
1923
|
+
createUserMessage(files)
|
|
1924
|
+
]
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// src/states/resolving-tool-result.ts
|
|
1929
|
+
async function resolvingToolResultLogic({
|
|
1930
|
+
setting,
|
|
1931
|
+
checkpoint,
|
|
1932
|
+
step
|
|
1933
|
+
}) {
|
|
1934
|
+
if (!step?.toolCall || !step?.toolResult) {
|
|
1935
|
+
throw new Error("No tool call or tool result found");
|
|
1936
|
+
}
|
|
1937
|
+
const { id, toolName } = step.toolCall;
|
|
1938
|
+
const { result } = step.toolResult;
|
|
1939
|
+
return finishToolCall(setting, checkpoint, {
|
|
1940
|
+
newMessages: [
|
|
1941
|
+
createToolMessage([
|
|
1942
|
+
{
|
|
1943
|
+
type: "toolResultPart",
|
|
1944
|
+
toolCallId: id,
|
|
1945
|
+
toolName,
|
|
1946
|
+
contents: result.filter(
|
|
1947
|
+
(part) => part.type === "textPart" || part.type === "imageInlinePart"
|
|
1948
|
+
)
|
|
1949
|
+
}
|
|
1950
|
+
])
|
|
1951
|
+
]
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
// src/states/resolving-thought.ts
|
|
1956
|
+
async function resolvingThoughtLogic(context) {
|
|
1957
|
+
return resolvingToolResultLogic(context);
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// src/runtime-state-machine.ts
|
|
1961
|
+
var runtimeStateMachine = setup({
|
|
1962
|
+
types: {
|
|
1963
|
+
input: {},
|
|
1964
|
+
context: {},
|
|
1965
|
+
events: {}
|
|
1966
|
+
}
|
|
1967
|
+
}).createMachine({
|
|
1968
|
+
/** @xstate-layout N4IgpgJg5mDOIC5QCUCuA7AdASXQSwBcBiWAgQwCcC10BtABgF1FQAHAe1kL3fRZAAeiAKz0AzJgDsAJgAsksZNlixs+gDZZAGhABPRIszCAjGIAcY4crPHhJyQF8HOmpgAKFMK0p50UAGLsFADKBF4k5FQA4mDoYBRkBDx0TPwcXEm8-EIIZmaymPRK8saSopL00tI6+gjq9JjSltLG9HnqZkqdTi4YmDFxCUl+ACrs7AA2AMJkExNEngQUugzMSCDp3FnrOcam0pgAnHbS9GeSZsIq6jWIh2KHUsYd9MbSwl2S6j0grgPxiV8UDGkxmcyIAGNZhMQRNVmlOFs+DtEPt1Jg3odpGVbFcrLcENIzOjbGYipIZMpnmYfn9YgDhsDxtNoZDobgwgkIUkAG5gWHw9abTLI0C7ImSTDqYTY2S45TCCwE54HUr1WRXSqHcRVWl9f5DIGwsHzKFzAAiYAmYCgiTAgrYiJF2VRYnoj1ehwskkOZTEz2EBMMJnMFnezxUF2+zl+fRNRuZCzgkz5sOQcFQEwIDo2TuSLoQpWJUvowl9Flk0sqBLlZik2Mkpjyan90d6WHjo0TnlgKf5AAt2KgoP3s6khXntmLUT6JL6PkoFJX1MZtHoRKIpdrK3sqorpG3Yx3oQnJknexM+W4IAAzfx4a054X5lGFn2PMTYlSyWQH32WAl1DKKV1Q6L1V1EWQ9WPOZT3mHs+2wABbMgYHvR9x0dDIX2nN8zEeOVjGORsmgsMQCTJYRMGJORwP9MwDxpGNXE7Jkz0SMIkNYAgpnYLjrRFJ9J1FQQZzJTBPxUfDDjlN1OgJIkSUVUtDlXCpsUPVx0wvHk4O0zNiBvXw8FgftjWhITsKnUTCU-SU92JMlfSaGtjDrGQKSbfJxGeaDMG0lMjUHYdRyIIz8FM8y5kspECyabFGneRz3RkN011qOwGnUbcVzeJKDz8gLLyBa87wfMAwuMyLmRNGLnVfeL7KSl5nPI9c6mA9RQPwmwNVLQrk2KvxkNQsB0Iq8KTLMmqLMw3MrJEnJGsSxUWtSgkfSU-JDmIqNKj8g1AT8Gh9KzSE+NYASwBoOqcJs+K61sDo5SKBifwU4tSRUtTKi+KDmLjE9hvQTkyG5PBU0TUh2FYGgACFdA5AFwchyZbus3YyklYRst-DUlDEaU2tqFUMS+NyPmlPZDiAvzWMta1bTCCIYfh3QGZtO10cWmcLiOQnG3qHHiSDbGvM-Ex1EjYk-PvCL+yBUJwghXhhlQfl2AAOTAAQCCV1hubiqxjCMRUZXMGSvVUZV6A1LcuorDp8I0WWqoVvx9ZZ2GMARgBRAQITASBIAAWTIAR9dgQ2Gs0Ki8nyK4bCuDVRZNzRwxse4GK6pwY3QdgIDgfgaARBaCxUBosU0cw5TLUiCQAWlkR4nPdbVCeyld-vbHB8AIUvYtfRQ6yr6xa6xcwCTLaiadj1dKxk1phD8jwvB8PxAhCMJWAH+rcKAyuyVaa5Wi+KeW9njV59xpeDvpQ0u1BaFd7u3Y2keDpO7LW2FDLc+Z6AnsPEO4ZYAxghMOCL8MaojsJKYi8cHZ5C9EGNoRg1CdH3GcZeYD-KDV0o-CYp1+4TjLg1cQkp-SfjDE9Ew1R2o-hNnsUs1MfR-UuANHSQUhwjmIVhQeuFTg-ilF8GUblTgKDoRlBQhQJEaiJMnfCHDAp+FKuNKBPNCSlgaOIeQttWxS0DO1bKdY9HvH9D+FotMcFFXwVAEaaFyrqLirbBo1M7KvFUDJCiRJGgalUM3LEMk5R30GEdKAJ0MxZicWQlQEklAyillWVoZgUF1isG0MoDEsF0yBnYkGyNeQa0mNE3CtgiIYnEEBToFwvS+mVPkU20l8JWG1OIHJsE-AcyZmAEpNlbBqEwLo7yVRbY3HatPfCXUOgfCsGSTQmk+hyymorbevSloykeFYCkrxfqrmJiIaRRQ7JbP8Q8PyoQYasEgGsxA2JWiNB-jjdQTRqb1IkP6OwRF8KMSAbnBwQA */
|
|
1969
|
+
id: "Run",
|
|
1970
|
+
initial: "Init",
|
|
1971
|
+
context: ({ input }) => ({
|
|
1972
|
+
setting: input.setting,
|
|
1973
|
+
checkpoint: input.initialCheckpoint,
|
|
1974
|
+
eventListener: input.eventListener,
|
|
1975
|
+
skillManagers: input.skillManagers
|
|
1976
|
+
}),
|
|
1977
|
+
states: {
|
|
1978
|
+
Init: {
|
|
1979
|
+
on: {
|
|
1980
|
+
startRun: {
|
|
1981
|
+
target: "PreparingForStep",
|
|
1982
|
+
actions: assign({
|
|
1983
|
+
checkpoint: ({ event }) => ({
|
|
1984
|
+
...event.initialCheckpoint,
|
|
1985
|
+
messages: [...event.initialCheckpoint.messages, ...event.inputMessages],
|
|
1986
|
+
status: "proceeding"
|
|
1987
|
+
})
|
|
1988
|
+
})
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
PreparingForStep: {
|
|
1993
|
+
on: {
|
|
1994
|
+
startGeneration: {
|
|
1995
|
+
target: "GeneratingToolCall",
|
|
1996
|
+
actions: assign({
|
|
1997
|
+
step: ({ context, event }) => ({
|
|
1998
|
+
stepNumber: context.checkpoint.stepNumber,
|
|
1999
|
+
newMessages: [],
|
|
2000
|
+
usage: createEmptyUsage(),
|
|
2001
|
+
startedAt: event.timestamp
|
|
2002
|
+
})
|
|
2003
|
+
})
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
GeneratingToolCall: {
|
|
2008
|
+
on: {
|
|
2009
|
+
retry: {
|
|
2010
|
+
target: "FinishingStep",
|
|
2011
|
+
actions: assign({
|
|
2012
|
+
checkpoint: ({ context, event }) => ({
|
|
2013
|
+
...context.checkpoint,
|
|
2014
|
+
messages: [...context.checkpoint.messages, ...event.newMessages],
|
|
2015
|
+
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
2016
|
+
}),
|
|
2017
|
+
step: ({ context, event }) => ({
|
|
2018
|
+
...context.step,
|
|
2019
|
+
newMessages: event.newMessages,
|
|
2020
|
+
usage: sumUsage(context.step.usage, event.usage)
|
|
2021
|
+
})
|
|
2022
|
+
})
|
|
2023
|
+
},
|
|
2024
|
+
callTool: {
|
|
2025
|
+
target: "CallingTool",
|
|
2026
|
+
actions: assign({
|
|
2027
|
+
checkpoint: ({ context, event }) => ({
|
|
2028
|
+
...context.checkpoint,
|
|
2029
|
+
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2030
|
+
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
2031
|
+
}),
|
|
2032
|
+
step: ({ context, event }) => ({
|
|
2033
|
+
...context.step,
|
|
2034
|
+
newMessages: [event.newMessage],
|
|
2035
|
+
toolCall: event.toolCall,
|
|
2036
|
+
usage: sumUsage(context.step.usage, event.usage)
|
|
2037
|
+
})
|
|
2038
|
+
})
|
|
2039
|
+
},
|
|
2040
|
+
callInteractiveTool: {
|
|
2041
|
+
target: "CallingInteractiveTool",
|
|
2042
|
+
actions: assign({
|
|
2043
|
+
checkpoint: ({ context, event }) => ({
|
|
2044
|
+
...context.checkpoint,
|
|
2045
|
+
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2046
|
+
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
2047
|
+
}),
|
|
2048
|
+
step: ({ context, event }) => ({
|
|
2049
|
+
...context.step,
|
|
2050
|
+
newMessages: [event.newMessage],
|
|
2051
|
+
toolCall: event.toolCall,
|
|
2052
|
+
usage: sumUsage(context.step.usage, event.usage)
|
|
2053
|
+
})
|
|
2054
|
+
})
|
|
2055
|
+
},
|
|
2056
|
+
callDelegate: {
|
|
2057
|
+
target: "CallingDelegate",
|
|
2058
|
+
actions: assign({
|
|
2059
|
+
checkpoint: ({ context, event }) => ({
|
|
2060
|
+
...context.checkpoint,
|
|
2061
|
+
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2062
|
+
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
2063
|
+
}),
|
|
2064
|
+
step: ({ context, event }) => ({
|
|
2065
|
+
...context.step,
|
|
2066
|
+
newMessages: [event.newMessage],
|
|
2067
|
+
toolCall: event.toolCall,
|
|
2068
|
+
usage: sumUsage(context.step.usage, event.usage)
|
|
2069
|
+
})
|
|
2070
|
+
})
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
},
|
|
2074
|
+
CallingTool: {
|
|
2075
|
+
on: {
|
|
2076
|
+
resolveToolResult: {
|
|
2077
|
+
target: "ResolvingToolResult",
|
|
2078
|
+
actions: assign({
|
|
2079
|
+
step: ({ context, event }) => ({
|
|
2080
|
+
...context.step,
|
|
2081
|
+
toolResult: event.toolResult
|
|
2082
|
+
})
|
|
2083
|
+
})
|
|
2084
|
+
},
|
|
2085
|
+
resolveThought: {
|
|
2086
|
+
target: "ResolvingThought",
|
|
2087
|
+
actions: assign({
|
|
2088
|
+
step: ({ context, event }) => ({
|
|
2089
|
+
...context.step,
|
|
2090
|
+
toolResult: event.toolResult
|
|
2091
|
+
})
|
|
2092
|
+
})
|
|
2093
|
+
},
|
|
2094
|
+
resolvePdfFile: {
|
|
2095
|
+
target: "ResolvingPdfFile",
|
|
2096
|
+
actions: assign({
|
|
2097
|
+
step: ({ context, event }) => ({
|
|
2098
|
+
...context.step,
|
|
2099
|
+
toolResult: event.toolResult
|
|
2100
|
+
})
|
|
2101
|
+
})
|
|
2102
|
+
},
|
|
2103
|
+
resolveImageFile: {
|
|
2104
|
+
target: "ResolvingImageFile",
|
|
2105
|
+
actions: assign({
|
|
2106
|
+
step: ({ context, event }) => ({
|
|
2107
|
+
...context.step,
|
|
2108
|
+
toolResult: event.toolResult
|
|
2109
|
+
})
|
|
2110
|
+
})
|
|
2111
|
+
},
|
|
2112
|
+
attemptCompletion: {
|
|
2113
|
+
target: "GeneratingRunResult",
|
|
2114
|
+
actions: assign({
|
|
2115
|
+
step: ({ context, event }) => ({
|
|
2116
|
+
...context.step,
|
|
2117
|
+
toolResult: event.toolResult
|
|
2118
|
+
})
|
|
2119
|
+
})
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
},
|
|
2123
|
+
ResolvingToolResult: {
|
|
2124
|
+
on: {
|
|
2125
|
+
finishToolCall: {
|
|
2126
|
+
target: "FinishingStep",
|
|
2127
|
+
actions: assign({
|
|
2128
|
+
checkpoint: ({ context, event }) => ({
|
|
2129
|
+
...context.checkpoint,
|
|
2130
|
+
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
2131
|
+
}),
|
|
2132
|
+
step: ({ context, event }) => ({
|
|
2133
|
+
...context.step,
|
|
2134
|
+
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
2135
|
+
})
|
|
2136
|
+
})
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
},
|
|
2140
|
+
ResolvingThought: {
|
|
2141
|
+
on: {
|
|
2142
|
+
finishToolCall: {
|
|
2143
|
+
target: "FinishingStep",
|
|
2144
|
+
actions: assign({
|
|
2145
|
+
checkpoint: ({ context, event }) => ({
|
|
2146
|
+
...context.checkpoint,
|
|
2147
|
+
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
2148
|
+
}),
|
|
2149
|
+
step: ({ context, event }) => ({
|
|
2150
|
+
...context.step,
|
|
2151
|
+
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
2152
|
+
})
|
|
2153
|
+
})
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
},
|
|
2157
|
+
ResolvingPdfFile: {
|
|
2158
|
+
on: {
|
|
2159
|
+
finishToolCall: {
|
|
2160
|
+
target: "FinishingStep",
|
|
2161
|
+
actions: assign({
|
|
2162
|
+
checkpoint: ({ context, event }) => ({
|
|
2163
|
+
...context.checkpoint,
|
|
2164
|
+
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
2165
|
+
}),
|
|
2166
|
+
step: ({ context, event }) => ({
|
|
2167
|
+
...context.step,
|
|
2168
|
+
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
2169
|
+
})
|
|
2170
|
+
})
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
},
|
|
2174
|
+
ResolvingImageFile: {
|
|
2175
|
+
on: {
|
|
2176
|
+
finishToolCall: {
|
|
2177
|
+
target: "FinishingStep",
|
|
2178
|
+
actions: assign({
|
|
2179
|
+
checkpoint: ({ context, event }) => ({
|
|
2180
|
+
...context.checkpoint,
|
|
2181
|
+
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
2182
|
+
}),
|
|
2183
|
+
step: ({ context, event }) => ({
|
|
2184
|
+
...context.step,
|
|
2185
|
+
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
2186
|
+
})
|
|
2187
|
+
})
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
},
|
|
2191
|
+
GeneratingRunResult: {
|
|
2192
|
+
on: {
|
|
2193
|
+
completeRun: {
|
|
2194
|
+
target: "Stopped",
|
|
2195
|
+
actions: assign({
|
|
2196
|
+
checkpoint: ({ event }) => event.checkpoint,
|
|
2197
|
+
step: ({ event }) => event.step
|
|
2198
|
+
})
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
},
|
|
2202
|
+
CallingInteractiveTool: {
|
|
2203
|
+
on: {
|
|
2204
|
+
stopRunByInteractiveTool: {
|
|
2205
|
+
target: "Stopped",
|
|
2206
|
+
actions: assign({
|
|
2207
|
+
checkpoint: ({ event }) => event.checkpoint,
|
|
2208
|
+
step: ({ event }) => event.step
|
|
2209
|
+
})
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
CallingDelegate: {
|
|
2214
|
+
on: {
|
|
2215
|
+
stopRunByDelegate: {
|
|
2216
|
+
target: "Stopped",
|
|
2217
|
+
actions: assign({
|
|
2218
|
+
checkpoint: ({ event }) => event.checkpoint,
|
|
2219
|
+
step: ({ event }) => event.step
|
|
2220
|
+
})
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
},
|
|
2224
|
+
FinishingStep: {
|
|
2225
|
+
on: {
|
|
2226
|
+
continueToNextStep: {
|
|
2227
|
+
target: "PreparingForStep",
|
|
2228
|
+
actions: assign({
|
|
2229
|
+
checkpoint: ({ event }) => event.nextCheckpoint,
|
|
2230
|
+
step: ({ event }) => event.step
|
|
2231
|
+
}),
|
|
2232
|
+
reenter: true
|
|
2233
|
+
},
|
|
2234
|
+
stopRunByExceededMaxSteps: {
|
|
2235
|
+
target: "Stopped",
|
|
2236
|
+
actions: assign({
|
|
2237
|
+
checkpoint: ({ event }) => event.checkpoint,
|
|
2238
|
+
step: ({ event }) => event.step
|
|
2239
|
+
})
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
},
|
|
2243
|
+
Stopped: {
|
|
2244
|
+
type: "final"
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
var StateMachineLogics = {
|
|
2249
|
+
Init: initLogic,
|
|
2250
|
+
PreparingForStep: preparingForStepLogic,
|
|
2251
|
+
GeneratingToolCall: generatingToolCallLogic,
|
|
2252
|
+
CallingTool: callingToolLogic,
|
|
2253
|
+
ResolvingToolResult: resolvingToolResultLogic,
|
|
2254
|
+
ResolvingThought: resolvingThoughtLogic,
|
|
2255
|
+
ResolvingPdfFile: resolvingPdfFileLogic,
|
|
2256
|
+
ResolvingImageFile: resolvingImageFileLogic,
|
|
2257
|
+
GeneratingRunResult: generatingRunResultLogic,
|
|
2258
|
+
CallingInteractiveTool: callingInteractiveToolLogic,
|
|
2259
|
+
CallingDelegate: callingDelegateLogic,
|
|
2260
|
+
FinishingStep: finishingStepLogic
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/schemas/perstack-toml.ts
|
|
2264
|
+
import { z as z4 } from "zod";
|
|
2265
|
+
var PerstackConfigSchema = z4.object({
|
|
2266
|
+
model: z4.string().optional(),
|
|
2267
|
+
temperature: z4.number().optional(),
|
|
2268
|
+
maxSteps: z4.number().optional(),
|
|
2269
|
+
maxRetries: z4.number().optional(),
|
|
2270
|
+
experts: z4.record(
|
|
2271
|
+
z4.string(),
|
|
2272
|
+
z4.object({
|
|
2273
|
+
version: z4.string().optional(),
|
|
2274
|
+
minRuntimeVersion: z4.string().optional(),
|
|
2275
|
+
description: z4.string().optional(),
|
|
2276
|
+
instruction: z4.string(),
|
|
2277
|
+
skills: z4.record(
|
|
2278
|
+
z4.string(),
|
|
2279
|
+
z4.discriminatedUnion("type", [
|
|
2280
|
+
z4.object({
|
|
2281
|
+
type: z4.literal("mcpStdioSkill"),
|
|
2282
|
+
description: z4.string().optional(),
|
|
2283
|
+
rule: z4.string().optional(),
|
|
2284
|
+
pick: z4.array(z4.string()).optional(),
|
|
2285
|
+
omit: z4.array(z4.string()).optional(),
|
|
2286
|
+
command: z4.string(),
|
|
2287
|
+
packageName: z4.string().optional(),
|
|
2288
|
+
args: z4.array(z4.string()).optional(),
|
|
2289
|
+
requiredEnv: z4.array(z4.string()).optional()
|
|
2290
|
+
}),
|
|
2291
|
+
z4.object({
|
|
2292
|
+
type: z4.literal("mcpSseSkill"),
|
|
2293
|
+
description: z4.string().optional(),
|
|
2294
|
+
rule: z4.string().optional(),
|
|
2295
|
+
pick: z4.array(z4.string()).optional(),
|
|
2296
|
+
omit: z4.array(z4.string()).optional(),
|
|
2297
|
+
endpoint: z4.string()
|
|
2298
|
+
}),
|
|
2299
|
+
z4.object({
|
|
2300
|
+
type: z4.literal("interactiveSkill"),
|
|
2301
|
+
description: z4.string().optional(),
|
|
2302
|
+
rule: z4.string().optional(),
|
|
2303
|
+
tools: z4.record(
|
|
2304
|
+
z4.string(),
|
|
2305
|
+
z4.object({
|
|
2306
|
+
description: z4.string().optional(),
|
|
2307
|
+
inputJsonSchema: z4.string()
|
|
2308
|
+
})
|
|
2309
|
+
)
|
|
2310
|
+
})
|
|
2311
|
+
])
|
|
2312
|
+
).optional(),
|
|
2313
|
+
delegates: z4.array(z4.string()).optional()
|
|
2314
|
+
})
|
|
2315
|
+
).optional()
|
|
2316
|
+
});
|
|
71
2317
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
2318
|
+
// src/runtime.ts
|
|
2319
|
+
async function run(runInput, options) {
|
|
2320
|
+
const runParams = RunParamsSchema.parse(runInput);
|
|
2321
|
+
const eventListener = options?.eventListener ?? defaultEventListener;
|
|
2322
|
+
const retrieveCheckpoint = options?.retrieveCheckpoint ?? defaultRetrieveCheckpoint;
|
|
2323
|
+
const storeCheckpoint = options?.storeCheckpoint ?? defaultStoreCheckpoint;
|
|
2324
|
+
const eventEmitter = new RunEventEmitter();
|
|
2325
|
+
eventEmitter.subscribe(eventListener);
|
|
2326
|
+
let { setting, checkpoint } = runParams;
|
|
2327
|
+
if (setting.workspace) {
|
|
2328
|
+
if (!path.isAbsolute(setting.workspace)) {
|
|
2329
|
+
throw new Error(`Workspace path must be absolute: ${setting.workspace}`);
|
|
2330
|
+
}
|
|
2331
|
+
process.chdir(setting.workspace);
|
|
2332
|
+
}
|
|
2333
|
+
await storeRunSetting(setting);
|
|
2334
|
+
while (true) {
|
|
2335
|
+
const { expertToRun, experts } = await setupExperts(setting);
|
|
2336
|
+
printRunSetting(expertToRun.name, experts, setting);
|
|
2337
|
+
const skillManagers = await getSkillManagers(expertToRun, experts);
|
|
2338
|
+
const runActor = createActor(runtimeStateMachine, {
|
|
2339
|
+
input: {
|
|
2340
|
+
setting: {
|
|
2341
|
+
...setting,
|
|
2342
|
+
experts
|
|
2343
|
+
},
|
|
2344
|
+
initialCheckpoint: checkpoint ?? {
|
|
2345
|
+
id: createId7(),
|
|
2346
|
+
runId: setting.runId,
|
|
2347
|
+
expert: {
|
|
2348
|
+
key: setting.expertKey,
|
|
2349
|
+
name: expertToRun.name,
|
|
2350
|
+
version: expertToRun.version
|
|
2351
|
+
},
|
|
2352
|
+
stepNumber: 1,
|
|
2353
|
+
status: "init",
|
|
2354
|
+
messages: [],
|
|
2355
|
+
usage: createEmptyUsage()
|
|
2356
|
+
},
|
|
2357
|
+
eventListener,
|
|
2358
|
+
skillManagers
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
2361
|
+
const runResultCheckpoint = await new Promise((resolve, reject) => {
|
|
2362
|
+
runActor.subscribe(async (runState) => {
|
|
2363
|
+
try {
|
|
2364
|
+
if (runState.value === "Stopped") {
|
|
2365
|
+
const { checkpoint: checkpoint2, skillManagers: skillManagers2 } = runState.context;
|
|
2366
|
+
if (!checkpoint2) {
|
|
2367
|
+
throw new Error("Checkpoint is undefined");
|
|
2368
|
+
}
|
|
2369
|
+
await closeSkillManagers(skillManagers2);
|
|
2370
|
+
resolve(checkpoint2);
|
|
2371
|
+
} else {
|
|
2372
|
+
const event = await StateMachineLogics[runState.value](runState.context);
|
|
2373
|
+
if ("checkpoint" in event) {
|
|
2374
|
+
await storeCheckpoint(event.checkpoint, event.timestamp);
|
|
2375
|
+
}
|
|
2376
|
+
await eventEmitter.emit(event);
|
|
2377
|
+
runActor.send(event);
|
|
2378
|
+
}
|
|
2379
|
+
} catch (error) {
|
|
2380
|
+
reject(error);
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
runActor.start();
|
|
2384
|
+
});
|
|
2385
|
+
switch (runResultCheckpoint.status) {
|
|
2386
|
+
case "completed": {
|
|
2387
|
+
if (runResultCheckpoint.delegatedBy) {
|
|
2388
|
+
const { messages, delegatedBy } = runResultCheckpoint;
|
|
2389
|
+
const { expert, toolCallId, toolName, checkpointId } = delegatedBy;
|
|
2390
|
+
const delegateResultMessage = messages[messages.length - 1];
|
|
2391
|
+
if (delegateResultMessage.type !== "expertMessage") {
|
|
2392
|
+
throw new Error("Delegation error: delegation result message is incorrect");
|
|
2393
|
+
}
|
|
2394
|
+
const delegateText = delegateResultMessage.contents.find(
|
|
2395
|
+
(content) => content.type === "textPart"
|
|
2396
|
+
);
|
|
2397
|
+
if (!delegateText) {
|
|
2398
|
+
throw new Error("Delegation error: delegation result message does not contain a text");
|
|
2399
|
+
}
|
|
2400
|
+
setting = {
|
|
2401
|
+
...setting,
|
|
2402
|
+
expertKey: expert.key,
|
|
2403
|
+
input: {
|
|
2404
|
+
interactiveToolCallResult: {
|
|
2405
|
+
toolCallId,
|
|
2406
|
+
toolName,
|
|
2407
|
+
text: delegateText.text
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
checkpoint = {
|
|
2412
|
+
...await retrieveCheckpoint(setting.runId, checkpointId),
|
|
2413
|
+
id: createId7(),
|
|
2414
|
+
stepNumber: runResultCheckpoint.stepNumber + 1,
|
|
2415
|
+
usage: runResultCheckpoint.usage
|
|
2416
|
+
};
|
|
2417
|
+
break;
|
|
2418
|
+
}
|
|
2419
|
+
return runResultCheckpoint;
|
|
2420
|
+
}
|
|
2421
|
+
case "stoppedByInteractiveTool": {
|
|
2422
|
+
return runResultCheckpoint;
|
|
2423
|
+
}
|
|
2424
|
+
case "stoppedByDelegate": {
|
|
2425
|
+
if (!runResultCheckpoint.delegateTo) {
|
|
2426
|
+
throw new Error("Delegation error: delegate to is undefined");
|
|
2427
|
+
}
|
|
2428
|
+
const { expert, toolCallId, toolName, query } = runResultCheckpoint.delegateTo;
|
|
2429
|
+
setting = {
|
|
2430
|
+
...setting,
|
|
2431
|
+
expertKey: expert.key,
|
|
2432
|
+
input: {
|
|
2433
|
+
text: query
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
checkpoint = {
|
|
2437
|
+
id: createId7(),
|
|
2438
|
+
runId: setting.runId,
|
|
2439
|
+
status: "init",
|
|
2440
|
+
stepNumber: runResultCheckpoint.stepNumber + 1,
|
|
2441
|
+
messages: [],
|
|
2442
|
+
expert: {
|
|
2443
|
+
key: expert.key,
|
|
2444
|
+
name: expert.name,
|
|
2445
|
+
version: expert.version
|
|
2446
|
+
},
|
|
2447
|
+
delegatedBy: {
|
|
2448
|
+
expert: {
|
|
2449
|
+
key: expertToRun.key,
|
|
2450
|
+
name: expertToRun.name,
|
|
2451
|
+
version: expertToRun.version
|
|
2452
|
+
},
|
|
2453
|
+
toolCallId,
|
|
2454
|
+
toolName,
|
|
2455
|
+
checkpointId: runResultCheckpoint.id
|
|
2456
|
+
},
|
|
2457
|
+
usage: runResultCheckpoint.usage
|
|
2458
|
+
};
|
|
2459
|
+
break;
|
|
2460
|
+
}
|
|
2461
|
+
case "stoppedByExceededMaxSteps": {
|
|
2462
|
+
return runResultCheckpoint;
|
|
2463
|
+
}
|
|
2464
|
+
case "stoppedByError": {
|
|
2465
|
+
return runResultCheckpoint;
|
|
2466
|
+
}
|
|
2467
|
+
default:
|
|
2468
|
+
throw new Error("Run stopped by unknown reason");
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
async function setupExperts(setting) {
|
|
2473
|
+
const { expertKey } = setting;
|
|
2474
|
+
const experts = { ...setting.experts };
|
|
2475
|
+
const expertToRun = await resolveExpertToRun(expertKey, experts);
|
|
2476
|
+
for (const delegateName of expertToRun.delegates) {
|
|
2477
|
+
const delegate = await resolveExpertToRun(delegateName, experts);
|
|
2478
|
+
if (!delegate) {
|
|
2479
|
+
throw new Error(`Delegate ${delegateName} not found`);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
return { expertToRun, experts };
|
|
2483
|
+
}
|
|
2484
|
+
function printRunSetting(expertName, experts, setting) {
|
|
2485
|
+
console.log("\u{1F99C} Starting Perstack \u{1F99C}");
|
|
2486
|
+
console.log(`Expert To Run: ${expertName}`);
|
|
2487
|
+
console.log(`Experts: ${Object.keys(experts).join(", ")}`);
|
|
2488
|
+
console.log(`Model: ${setting.model}`);
|
|
2489
|
+
console.log(`Temperature: ${setting.temperature}`);
|
|
2490
|
+
console.log(`Max Steps: ${setting.maxSteps}`);
|
|
2491
|
+
console.log(`Max Retries: ${setting.maxRetries}`);
|
|
2492
|
+
if (setting.input.text) {
|
|
2493
|
+
console.log(`Query: ${setting.input.text}`);
|
|
2494
|
+
}
|
|
2495
|
+
if (setting.input.interactiveToolCallResult) {
|
|
2496
|
+
console.log(`Tool: ${setting.input.interactiveToolCallResult.toolName}`);
|
|
2497
|
+
console.log(`Tool Call ID: ${setting.input.interactiveToolCallResult.toolCallId}`);
|
|
2498
|
+
console.log(`Tool Result: ${setting.input.interactiveToolCallResult.text}`);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
async function storeRunSetting(setting) {
|
|
2502
|
+
const runDir = getRunDir(setting.runId);
|
|
2503
|
+
if (existsSync(runDir)) {
|
|
2504
|
+
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
2505
|
+
const runSetting = JSON.parse(await readFile4(runSettingPath, "utf-8"));
|
|
2506
|
+
runSetting.updatedAt = Date.now();
|
|
2507
|
+
await writeFile2(runSettingPath, JSON.stringify(runSetting, null, 2), "utf-8");
|
|
2508
|
+
} else {
|
|
2509
|
+
await mkdir2(runDir, { recursive: true });
|
|
2510
|
+
await writeFile2(
|
|
2511
|
+
path.resolve(runDir, "run-setting.json"),
|
|
2512
|
+
JSON.stringify(setting, null, 2),
|
|
2513
|
+
"utf-8"
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function getRunDir(runId) {
|
|
2518
|
+
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
2519
|
+
}
|
|
2520
|
+
async function parseConfig(config) {
|
|
2521
|
+
const toml = TOML.parse(config ?? "");
|
|
2522
|
+
return PerstackConfigSchema.parse(toml);
|
|
2523
|
+
}
|
|
76
2524
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
`.trim():o},"")}async function tt({setting:e,checkpoint:t}){let{expertKey:o,experts:r}=e,a=r[o];switch(t.status){case "init":return ne(e,t,{initialCheckpoint:t,inputMessages:[Xe(a,r),et(e.input)]});default:return ne(e,t,{initialCheckpoint:t,inputMessages:[et(e.input)]})}}function et(e){if(e.text)return F([{type:"textPart",text:e.text}]);if(e.interactiveToolCallResult)return C([{type:"toolResultPart",toolCallId:e.interactiveToolCallResult.toolCallId,toolName:e.interactiveToolCallResult.toolName,contents:[{type:"textPart",text:e.interactiveToolCallResult.text}]}]);throw new Error("Input message is undefined")}async function ot({setting:e,checkpoint:t}){return Te(e,t,{messages:t.messages})}async function nt({setting:e,checkpoint:t,step:o}){if(!o?.toolCall||!o?.toolResult)throw new Error("No tool call or tool result found");let{id:r,toolName:a}=o.toolCall,{result:i}=o.toolResult,c=i.filter(u=>u.type==="textPart"),s=[];for(let u of c){let h;try{h=JSON.parse(u.text);}catch{s.push({type:"textPart",text:u.text});continue}let{path:d,mimeType:P,size:R}=h,f=await readFile(d).then(S=>({encodedData:S.toString("base64"),mimeType:P,size:R}));s.push({type:"imageInlinePart",encodedData:f.encodedData,mimeType:f.mimeType});}return $(e,t,{newMessages:[C([{type:"toolResultPart",toolCallId:r,toolName:a,contents:s}])]})}async function rt({setting:e,checkpoint:t,step:o}){if(!o?.toolCall||!o?.toolResult)throw new Error("No tool call or tool result found");let{id:r,toolName:a}=o.toolCall,{result:i}=o.toolResult,c=i.filter(u=>u.type==="textPart"),s=[];for(let u of c){let h;try{h=JSON.parse(u.text);}catch{s.push({type:"textPart",text:u.text});continue}let{path:d,mimeType:P,size:R}=h,f=await readFile(d).then(S=>({encodedData:S.toString("base64"),mimeType:P,size:R}));s.push({type:"fileInlinePart",encodedData:f.encodedData,mimeType:f.mimeType});}return $(e,t,{newMessages:[C([{type:"toolResultPart",toolCallId:r,toolName:a,contents:[{type:"textPart",text:"User uploads PDF file as follows."}]}]),F(s)]})}async function X({setting:e,checkpoint:t,step:o}){if(!o?.toolCall||!o?.toolResult)throw new Error("No tool call or tool result found");let{id:r,toolName:a}=o.toolCall,{result:i}=o.toolResult;return $(e,t,{newMessages:[C([{type:"toolResultPart",toolCallId:r,toolName:a,contents:i.filter(c=>c.type==="textPart"||c.type==="imageInlinePart")}])]})}async function st(e){return X(e)}var at=setup({types:{input:{},context:{},events:{}}}).createMachine({id:"Run",initial:"Init",context:({input:e})=>({setting:e.setting,checkpoint:e.initialCheckpoint,eventListener:e.eventListener,skillManagers:e.skillManagers}),states:{Init:{on:{startRun:{target:"PreparingForStep",actions:assign({checkpoint:({event:e})=>({...e.initialCheckpoint,messages:[...e.initialCheckpoint.messages,...e.inputMessages],status:"proceeding"})})}}},PreparingForStep:{on:{startGeneration:{target:"GeneratingToolCall",actions:assign({step:({context:e,event:t})=>({stepNumber:e.checkpoint.stepNumber,newMessages:[],usage:H(),startedAt:t.timestamp})})}}},GeneratingToolCall:{on:{retry:{target:"FinishingStep",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,...t.newMessages],usage:v(e.checkpoint.usage,t.usage)}),step:({context:e,event:t})=>({...e.step,newMessages:t.newMessages,usage:v(e.step.usage,t.usage)})})},callTool:{target:"CallingTool",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,t.newMessage],usage:v(e.checkpoint.usage,t.usage)}),step:({context:e,event:t})=>({...e.step,newMessages:[t.newMessage],toolCall:t.toolCall,usage:v(e.step.usage,t.usage)})})},callInteractiveTool:{target:"CallingInteractiveTool",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,t.newMessage],usage:v(e.checkpoint.usage,t.usage)}),step:({context:e,event:t})=>({...e.step,newMessages:[t.newMessage],toolCall:t.toolCall,usage:v(e.step.usage,t.usage)})})},callDelegate:{target:"CallingDelegate",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,t.newMessage],usage:v(e.checkpoint.usage,t.usage)}),step:({context:e,event:t})=>({...e.step,newMessages:[t.newMessage],toolCall:t.toolCall,usage:v(e.step.usage,t.usage)})})}}},CallingTool:{on:{resolveToolResult:{target:"ResolvingToolResult",actions:assign({step:({context:e,event:t})=>({...e.step,toolResult:t.toolResult})})},resolveThought:{target:"ResolvingThought",actions:assign({step:({context:e,event:t})=>({...e.step,toolResult:t.toolResult})})},resolvePdfFile:{target:"ResolvingPdfFile",actions:assign({step:({context:e,event:t})=>({...e.step,toolResult:t.toolResult})})},resolveImageFile:{target:"ResolvingImageFile",actions:assign({step:({context:e,event:t})=>({...e.step,toolResult:t.toolResult})})},attemptCompletion:{target:"GeneratingRunResult",actions:assign({step:({context:e,event:t})=>({...e.step,toolResult:t.toolResult})})}}},ResolvingToolResult:{on:{finishToolCall:{target:"FinishingStep",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,...t.newMessages]}),step:({context:e,event:t})=>({...e.step,newMessages:[...e.step.newMessages,...t.newMessages]})})}}},ResolvingThought:{on:{finishToolCall:{target:"FinishingStep",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,...t.newMessages]}),step:({context:e,event:t})=>({...e.step,newMessages:[...e.step.newMessages,...t.newMessages]})})}}},ResolvingPdfFile:{on:{finishToolCall:{target:"FinishingStep",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,...t.newMessages]}),step:({context:e,event:t})=>({...e.step,newMessages:[...e.step.newMessages,...t.newMessages]})})}}},ResolvingImageFile:{on:{finishToolCall:{target:"FinishingStep",actions:assign({checkpoint:({context:e,event:t})=>({...e.checkpoint,messages:[...e.checkpoint.messages,...t.newMessages]}),step:({context:e,event:t})=>({...e.step,newMessages:[...e.step.newMessages,...t.newMessages]})})}}},GeneratingRunResult:{on:{completeRun:{target:"Stopped",actions:assign({checkpoint:({event:e})=>e.checkpoint,step:({event:e})=>e.step})}}},CallingInteractiveTool:{on:{stopRunByInteractiveTool:{target:"Stopped",actions:assign({checkpoint:({event:e})=>e.checkpoint,step:({event:e})=>e.step})}}},CallingDelegate:{on:{stopRunByDelegate:{target:"Stopped",actions:assign({checkpoint:({event:e})=>e.checkpoint,step:({event:e})=>e.step})}}},FinishingStep:{on:{continueToNextStep:{target:"PreparingForStep",actions:assign({checkpoint:({event:e})=>e.nextCheckpoint,step:({event:e})=>e.step}),reenter:true},stopRunByExceededMaxSteps:{target:"Stopped",actions:assign({checkpoint:({event:e})=>e.checkpoint,step:({event:e})=>e.step})}}},Stopped:{type:"final"}}}),it={Init:tt,PreparingForStep:ot,GeneratingToolCall:Qe,CallingTool:Ve,ResolvingToolResult:X,ResolvingThought:st,ResolvingPdfFile:rt,ResolvingImageFile:nt,GeneratingRunResult:He,CallingInteractiveTool:Je,CallingDelegate:qe,FinishingStep:Ye};var lt=z$1.object({model:z$1.string().optional(),temperature:z$1.number().optional(),maxSteps:z$1.number().optional(),maxRetries:z$1.number().optional(),experts:z$1.record(z$1.string(),z$1.object({version:z$1.string().optional(),minRuntimeVersion:z$1.string().optional(),description:z$1.string().optional(),instruction:z$1.string(),skills:z$1.record(z$1.string(),z$1.discriminatedUnion("type",[z$1.object({type:z$1.literal("mcpStdioSkill"),description:z$1.string().optional(),rule:z$1.string().optional(),pick:z$1.array(z$1.string()).optional(),omit:z$1.array(z$1.string()).optional(),command:z$1.string(),packageName:z$1.string().optional(),args:z$1.array(z$1.string()).optional(),requiredEnv:z$1.array(z$1.string()).optional()}),z$1.object({type:z$1.literal("mcpSseSkill"),description:z$1.string().optional(),rule:z$1.string().optional(),pick:z$1.array(z$1.string()).optional(),omit:z$1.array(z$1.string()).optional(),endpoint:z$1.string()}),z$1.object({type:z$1.literal("interactiveSkill"),description:z$1.string().optional(),rule:z$1.string().optional(),tools:z$1.record(z$1.string(),z$1.object({description:z$1.string().optional(),inputJsonSchema:z$1.string()}))})])).optional(),delegates:z$1.array(z$1.string()).optional()})).optional()});async function Br(e,t){let o=ke.parse(e),r=t?.eventListener??se,a=t?.retrieveCheckpoint??De,i=t?.storeCheckpoint??Oe,c=new V;c.subscribe(r);let{setting:s,checkpoint:u}=o;if(s.workspace){if(!pe.isAbsolute(s.workspace))throw new Error(`Workspace path must be absolute: ${s.workspace}`);process.chdir(s.workspace);}for(await Po(s);;){let{expertToRun:h,experts:d}=await Ro(s);So(h.name,d,s);let P=await Le(h,d),R=createActor(at,{input:{setting:{...s,experts:d},initialCheckpoint:u??{id:createId(),runId:s.runId,expert:{key:s.expertKey,name:h.name,version:h.version},stepNumber:1,status:"init",messages:[],usage:H()},eventListener:r,skillManagers:P}}),f=await new Promise((S,j)=>{R.subscribe(async M=>{try{if(M.value==="Stopped"){let{checkpoint:T,skillManagers:ee}=M.context;if(!T)throw new Error("Checkpoint is undefined");await Ge(ee),S(T);}else {let T=await it[M.value](M.context);"checkpoint"in T&&await i(T.checkpoint,T.timestamp),await c.emit(T),R.send(T);}}catch(T){j(T);}}),R.start();});switch(f.status){case "completed":{if(f.delegatedBy){let{messages:S,delegatedBy:j}=f,{expert:M,toolCallId:T,toolName:ee,checkpointId:ct}=j,ce=S[S.length-1];if(ce.type!=="expertMessage")throw new Error("Delegation error: delegation result message is incorrect");let me=ce.contents.find(mt=>mt.type==="textPart");if(!me)throw new Error("Delegation error: delegation result message does not contain a text");s={...s,expertKey:M.key,input:{interactiveToolCallResult:{toolCallId:T,toolName:ee,text:me.text}}},u={...await a(s.runId,ct),id:createId(),stepNumber:f.stepNumber+1,usage:f.usage};break}return f}case "stoppedByInteractiveTool":return f;case "stoppedByDelegate":{if(!f.delegateTo)throw new Error("Delegation error: delegate to is undefined");let{expert:S,toolCallId:j,toolName:M,query:T}=f.delegateTo;s={...s,expertKey:S.key,input:{text:T}},u={id:createId(),runId:s.runId,status:"init",stepNumber:f.stepNumber+1,messages:[],expert:{key:S.key,name:S.name,version:S.version},delegatedBy:{expert:{key:h.key,name:h.name,version:h.version},toolCallId:j,toolName:M,checkpointId:f.id},usage:f.usage};break}case "stoppedByExceededMaxSteps":return f;case "stoppedByError":return f;default:throw new Error("Run stopped by unknown reason")}}}async function Ro(e){let{expertKey:t}=e,o={...e.experts},r=await ie(t,o);for(let a of r.delegates)if(!await ie(a,o))throw new Error(`Delegate ${a} not found`);return {expertToRun:r,experts:o}}function So(e,t,o){console.log("\u{1F99C} Starting Perstack \u{1F99C}"),console.log(`Expert To Run: ${e}`),console.log(`Experts: ${Object.keys(t).join(", ")}`),console.log(`Model: ${o.model}`),console.log(`Temperature: ${o.temperature}`),console.log(`Max Steps: ${o.maxSteps}`),console.log(`Max Retries: ${o.maxRetries}`),o.input.text&&console.log(`Query: ${o.input.text}`),o.input.interactiveToolCallResult&&(console.log(`Tool: ${o.input.interactiveToolCallResult.toolName}`),console.log(`Tool Call ID: ${o.input.interactiveToolCallResult.toolCallId}`),console.log(`Tool Result: ${o.input.interactiveToolCallResult.text}`));}async function Po(e){let t=O(e.runId);if(existsSync(t)){let o=pe.resolve(t,"run-setting.json"),r=JSON.parse(await readFile(o,"utf-8"));r.updatedAt=Date.now(),await writeFile(o,JSON.stringify(r,null,2),"utf-8");}else await mkdir(t,{recursive:true}),await writeFile(pe.resolve(t,"run-setting.json"),JSON.stringify(e,null,2),"utf-8");}function O(e){return `${process.cwd()}/perstack/runs/${e}`}async function _r(e){let t=ko.parse(e??"");return lt.parse(t)}var Lr=z$1.object({expertKey:z$1.string(),query:z$1.string(),options:z$1.object({config:z$1.string().optional(),model:z$1.string().optional(),temperature:z$1.string().optional().transform(e=>{if(e===void 0)return;let t=Number.parseFloat(e);if(!Number.isNaN(t))return t}),maxSteps:z$1.string().optional().transform(e=>{if(e===void 0)return;let t=Number.parseInt(e);if(!Number.isNaN(t))return t}),maxRetries:z$1.string().optional().transform(e=>{if(e===void 0)return;let t=Number.parseInt(e);if(!Number.isNaN(t))return t}),runId:z$1.string().optional()})});export{oe as CheckpointSchema,Ft as CheckpointStatusSchema,wt as ExpertMessageSchema,J as ExpertSchema,Rt as FileBinaryPartSchema,Tt as FileInlinePartSchema,kt as FileUrlPartSchema,xt as ImageBinaryPartSchema,fe as ImageInlinePartSchema,yt as ImageUrlPartSchema,Ct as InstructionMessageSchema,q as InteractiveSkillSchema,ye as InteractiveToolSchema,K as McpSseSkillSchema,G as McpStdioSkillSchema,he as MessageSchema,lt as PerstackConfigSchema,Ae as RegistryV1ExpertsGetResponseSchema,Lr as RunInputSchema,ke as RunParamsSchema,it as StateMachineLogics,A as TextPartSchema,St as ToolCallPartSchema,It as ToolMessageSchema,Pt as ToolResultPartSchema,Nt as UsageSchema,vt as UserMessageSchema,Me as attemptCompletion,Pe as callDelegate,Se as callInteractiveTool,Re as callTool,be as completeRun,Fe as continueToNextStep,k as createEvent,se as defaultEventListener,D as expertKeyRegex,Mt as expertNameRegex,$ as finishToolCall,O as getRunDir,$t as maxNameLength,_r as parseConfig,jo as parseExpertKey,Ie as resolveImageFile,we as resolvePdfFile,ve as resolveThought,Ce as resolveToolResult,re as retry,Br as run,at as runtimeStateMachine,Uo as skillNameRegex,Te as startGeneration,ne as startRun,$e as stopRunByDelegate,Ne as stopRunByExceededMaxSteps,Ee as stopRunByInteractiveTool,Et as tagNameRegex,bt as versionRegex};//# sourceMappingURL=index.js.map
|
|
2525
|
+
// src/schemas/command-run.ts
|
|
2526
|
+
import { z as z5 } from "zod";
|
|
2527
|
+
var RunInputSchema = z5.object({
|
|
2528
|
+
expertKey: z5.string(),
|
|
2529
|
+
query: z5.string(),
|
|
2530
|
+
options: z5.object({
|
|
2531
|
+
config: z5.string().optional(),
|
|
2532
|
+
model: z5.string().optional(),
|
|
2533
|
+
temperature: z5.string().optional().transform((value) => {
|
|
2534
|
+
if (value === void 0) {
|
|
2535
|
+
return void 0;
|
|
2536
|
+
}
|
|
2537
|
+
const parsedValue = Number.parseFloat(value);
|
|
2538
|
+
if (Number.isNaN(parsedValue)) {
|
|
2539
|
+
return void 0;
|
|
2540
|
+
}
|
|
2541
|
+
return parsedValue;
|
|
2542
|
+
}),
|
|
2543
|
+
maxSteps: z5.string().optional().transform((value) => {
|
|
2544
|
+
if (value === void 0) {
|
|
2545
|
+
return void 0;
|
|
2546
|
+
}
|
|
2547
|
+
const parsedValue = Number.parseInt(value);
|
|
2548
|
+
if (Number.isNaN(parsedValue)) {
|
|
2549
|
+
return void 0;
|
|
2550
|
+
}
|
|
2551
|
+
return parsedValue;
|
|
2552
|
+
}),
|
|
2553
|
+
maxRetries: z5.string().optional().transform((value) => {
|
|
2554
|
+
if (value === void 0) {
|
|
2555
|
+
return void 0;
|
|
2556
|
+
}
|
|
2557
|
+
const parsedValue = Number.parseInt(value);
|
|
2558
|
+
if (Number.isNaN(parsedValue)) {
|
|
2559
|
+
return void 0;
|
|
2560
|
+
}
|
|
2561
|
+
return parsedValue;
|
|
2562
|
+
}),
|
|
2563
|
+
runId: z5.string().optional()
|
|
2564
|
+
})
|
|
2565
|
+
});
|
|
2566
|
+
export {
|
|
2567
|
+
CheckpointSchema,
|
|
2568
|
+
CheckpointStatusSchema,
|
|
2569
|
+
ExpertMessageSchema,
|
|
2570
|
+
ExpertSchema,
|
|
2571
|
+
FileBinaryPartSchema,
|
|
2572
|
+
FileInlinePartSchema,
|
|
2573
|
+
FileUrlPartSchema,
|
|
2574
|
+
ImageBinaryPartSchema,
|
|
2575
|
+
ImageInlinePartSchema,
|
|
2576
|
+
ImageUrlPartSchema,
|
|
2577
|
+
InstructionMessageSchema,
|
|
2578
|
+
InteractiveSkillSchema,
|
|
2579
|
+
InteractiveToolSchema,
|
|
2580
|
+
McpSseSkillSchema,
|
|
2581
|
+
McpStdioSkillSchema,
|
|
2582
|
+
MessageSchema,
|
|
2583
|
+
PerstackConfigSchema,
|
|
2584
|
+
RegistryV1ExpertsGetResponseSchema,
|
|
2585
|
+
RunInputSchema,
|
|
2586
|
+
RunParamsSchema,
|
|
2587
|
+
StateMachineLogics,
|
|
2588
|
+
TextPartSchema,
|
|
2589
|
+
ToolCallPartSchema,
|
|
2590
|
+
ToolMessageSchema,
|
|
2591
|
+
ToolResultPartSchema,
|
|
2592
|
+
UsageSchema,
|
|
2593
|
+
UserMessageSchema,
|
|
2594
|
+
attemptCompletion,
|
|
2595
|
+
callDelegate,
|
|
2596
|
+
callInteractiveTool,
|
|
2597
|
+
callTool,
|
|
2598
|
+
completeRun,
|
|
2599
|
+
continueToNextStep,
|
|
2600
|
+
createEvent,
|
|
2601
|
+
defaultEventListener,
|
|
2602
|
+
expertKeyRegex,
|
|
2603
|
+
expertNameRegex,
|
|
2604
|
+
finishToolCall,
|
|
2605
|
+
getRunDir,
|
|
2606
|
+
maxNameLength,
|
|
2607
|
+
parseConfig,
|
|
2608
|
+
parseExpertKey,
|
|
2609
|
+
resolveImageFile,
|
|
2610
|
+
resolvePdfFile,
|
|
2611
|
+
resolveThought,
|
|
2612
|
+
resolveToolResult,
|
|
2613
|
+
retry,
|
|
2614
|
+
run,
|
|
2615
|
+
runtimeStateMachine,
|
|
2616
|
+
skillNameRegex,
|
|
2617
|
+
startGeneration,
|
|
2618
|
+
startRun,
|
|
2619
|
+
stopRunByDelegate,
|
|
2620
|
+
stopRunByExceededMaxSteps,
|
|
2621
|
+
stopRunByInteractiveTool,
|
|
2622
|
+
tagNameRegex,
|
|
2623
|
+
versionRegex
|
|
2624
|
+
};
|
|
80
2625
|
//# sourceMappingURL=index.js.map
|