@routecraft/ai 0.5.0 → 0.6.0-canary.10

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.cjs CHANGED
@@ -1,15 +1,15 @@
1
- 'use strict';var routecraft=require('@routecraft/routecraft'),async_hooks=require('async_hooks'),http=require('http'),crypto$1=require('crypto'),ai=require('ai'),fs=require('fs'),path=require('path');var q=Symbol.for("routecraft.adapter.llm.providers"),ue=Symbol.for("routecraft.adapter.llm.options");var Ot=["openai","anthropic","openrouter","ollama","gemini"];function Nn(t){return Ot.includes(t)}function st(t){if(!t||typeof t!="object")throw new TypeError("llmPlugin: options must be an object");if(!t.providers||typeof t.providers!="object")throw new TypeError("llmPlugin: options.providers must be an object (record of provider id \u2192 options)");for(let[e,n]of Object.entries(t.providers))if(n!==void 0){if(!n||typeof n!="object")throw new TypeError(`llmPlugin: providers["${e}"] must be an object`);if(!Nn(e))throw new TypeError(`llmPlugin: providers["${e}"] is not a supported provider. Supported: ${Ot.join(", ")}`);switch(e){case "openai":case "anthropic":case "openrouter":case "gemini":if(typeof n.apiKey!="string"||!n.apiKey.trim())throw new TypeError(`llmPlugin: providers["${e}"].apiKey is required`);break;case "ollama":if(n.baseURL!==void 0&&typeof n.baseURL!="string")throw new TypeError(`llmPlugin: providers["${e}"].baseURL must be a string when provided`);if(n.modelId!==void 0&&(typeof n.modelId!="string"||!n.modelId.trim()))throw new TypeError(`llmPlugin: providers["${e}"].modelId must be a non-empty string when provided`);break}}if(t.defaultOptions!==void 0&&(typeof t.defaultOptions!="object"||t.defaultOptions===null))throw new TypeError("llmPlugin: defaultOptions must be an object when provided")}var $n=["openai","anthropic","openrouter","ollama","gemini"];function Un(t,e){return {provider:t,...e}}function ve(t={providers:{}}){return st(t),{apply(e){let n=new Map;for(let o of $n){let r=t.providers[o];r!==void 0&&n.set(o,Un(o,r));}e.setStore(q,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(ue,t.defaultOptions);}}}var pe=Symbol.for("routecraft.mcp.plugin.registered"),S=Symbol.for("routecraft.mcp.client.servers"),B=Symbol.for("routecraft.mcp.tool.registry"),me=Symbol.for("routecraft.mcp.stdio.managers"),O=Symbol.for("routecraft.mcp.local-tool-registry"),Ce=(n=>(n.TOOL="routecraft.mcp.tool",n.SESSION="routecraft.mcp.session",n))(Ce||{});function Pe(t){return "provider"in t&&t.provider==="oauth"}var jn="GET, POST, OPTIONS";var Fn="WWW-Authenticate, Mcp-Session-Id, Last-Event-ID",Hn=new Set(["localhost","127.0.0.1","::1","[::1]"]);function Gn(t){if(!t||t==="null")return false;let e;try{e=new URL(t);}catch{return false}return e.protocol!=="http:"&&e.protocol!=="https:"||e.username||e.password||e.pathname!=="/"&&e.pathname!==""||e.search||e.hash?false:Hn.has(e.hostname)?t:false}function at(t){if(t===false)return null;if(t===void 0)return {resolveOrigin:Gn,isWildcard:false};let{origin:e}=t;if(e==="*")return {resolveOrigin:()=>"*",isWildcard:true};if(typeof e=="string"){let n=e;return {resolveOrigin:o=>o===n?n:false,isWildcard:false}}if(Array.isArray(e)){let n=new Set(e);return {resolveOrigin:o=>o!==void 0&&n.has(o)?o:false,isWildcard:false}}if(typeof e=="function")return {resolveOrigin:e,isWildcard:false};throw new TypeError("mcpPlugin: cors.origin must be '*', a string, a string array, or a function")}function Vn(t,e){try{return t.resolveOrigin(e)}catch{return false}}function qn(t,e,n){if(!t)return {};let o={};t.isWildcard||(o.Vary="Origin");let r=Vn(t,e);return r===false||(o["Access-Control-Allow-Origin"]=r,n?(o["Access-Control-Allow-Methods"]=jn,o["Access-Control-Allow-Headers"]="*",o["Access-Control-Allow-Private-Network"]="true"):o["Access-Control-Expose-Headers"]=Fn),o}function fe(t,e,n,o){if(!e)return;let{Vary:r,...i}=qn(e,n,o);for(let[a,s]of Object.entries(i))t.setHeader(a,s);r!==void 0&&t.appendHeader("Vary",r);}var ke="/.well-known/oauth-protected-resource";function lt(t){let e=new Set([ke]),n=t.pathname;return n&&n!=="/"&&n!==""&&e.add(`${ke}${n}`),{ownedPaths:new Set([...e,"/mcp","/mcp/"]),metadataPaths:e}}var bt=t=>`<svg width="162" height="153" viewBox="0 0 162 153" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M141.881 62.9617C141.881 44.2319 126.65 29.0002 107.92 29.0002H30.1636L52.4304 52.2797L71.8879 52.3014L62.5672 42.4107H107.92C119.252 42.4107 128.471 51.6298 128.471 62.9617C128.471 74.293 119.252 83.5128 107.92 83.5128H101.193H88.5983H54.5814C50.8822 83.5128 47.8762 86.5187 47.8762 90.2173C47.8762 93.9278 50.8822 96.9225 54.5814 96.9225H95.4038L120.815 123.997H139.227L113.374 96.4753C129.522 93.8717 141.881 79.8359 141.881 62.9617Z" fill="${t}"/><path d="M54.8718 110.59C43.5966 110.59 34.4105 101.403 34.4105 90.1282C34.4105 78.8411 43.5966 69.6662 54.8718 69.6662H107.115C110.814 69.6662 113.82 66.6714 113.82 62.9617C113.82 59.2624 110.814 56.2564 107.115 56.2564H54.8718C36.1988 56.2564 21 71.4545 21 90.1282C21 108.802 36.1988 124 54.8718 124H113.597L101.014 110.59H54.8718Z" fill="${t}"/></svg>`,Et=t=>`data:image/svg+xml;base64,${Buffer.from(t,"utf8").toString("base64")}`,xt=Object.freeze([Object.freeze({src:Et(bt("#ffffff")),mimeType:"image/svg+xml",theme:"dark"}),Object.freeze({src:Et(bt("#000000")),mimeType:"image/svg+xml",theme:"light"})]);var Wn=["subject","issuer","audience","expiresAt","claims"],Kn=1e4,zn=3600,dt=class{entries=new Map;inFlight=new Map;maxEntries;constructor(e=Kn){this.maxEntries=e;}async getOrCompute(e,n,o){if(n===void 0||!Number.isFinite(n))return o();let r=Yn(e),i=this.entries.get(r);if(i){if(Date.now()/1e3<i.expiresAt)return i.enrichment;this.entries.delete(r);}let a=this.inFlight.get(r);if(a)return a;let s=o().then(l=>(this.store(r,l,n),this.inFlight.delete(r),l),l=>{throw this.inFlight.delete(r),l});return this.inFlight.set(r,s),s}clear(){this.entries.clear(),this.inFlight.clear();}store(e,n,o){for(this.entries.has(e)&&this.entries.delete(e),this.entries.set(e,{enrichment:n,expiresAt:o});this.entries.size>this.maxEntries;){let r=this.entries.keys().next().value;if(r===void 0)break;this.entries.delete(r);}}};function Yn(t){return crypto$1.createHash("sha256").update(t).digest("hex")}function Jn(t){if(!t)return;let e=t.match(/(?:^|,\s*)max-age=(\d+)/i);if(!e)return;let n=Number.parseInt(e[1],10);return Number.isFinite(n)&&n>=0?n:void 0}function Xn(t,e){if(t!==true){let l=new URL(t.toString());return async()=>l}if(e===void 0)throw new TypeError('oauth: `userinfo: true` requires the verify helper to expose an `issuer`. Use `jwks({ issuer, ... })` / `jwt({ issuer, ... })`, pass an explicit `userinfo: "https://idp.example.com/oauth/userinfo"`, or use a function.');if(Array.isArray(e))throw new TypeError("oauth: `userinfo: true` requires a single-string `issuer`; got an array. OIDC Discovery resolves one issuer to one userinfo endpoint. Pass an explicit userinfo URL or function instead.");let n=e.endsWith("/")?e:`${e}/`,o=new URL(".well-known/openid-configuration",n),r=null,i=0,a=null,s=async()=>{let l;try{l=await fetch(o);}catch(u){throw routecraft.rcError("RC5021",u,{message:`OIDC Discovery fetch failed at ${o.toString()}`,suggestion:"Verify network access to the IdP and that the issuer URL is reachable. Pass an explicit `userinfo` URL or function if the IdP does not advertise discovery."})}if(!l.ok)throw routecraft.rcError("RC5021",new Error(`OIDC Discovery fetch returned ${l.status} ${l.statusText}`),{message:`OIDC Discovery fetch failed at ${o.toString()} (status ${l.status})`,suggestion:"Verify the issuer hosts an OIDC Discovery document. Pass an explicit `userinfo` URL or function if it does not."});let d;try{d=await l.json();}catch(u){throw routecraft.rcError("RC5021",u,{message:`OIDC Discovery document at ${o.toString()} is not valid JSON`,suggestion:"Inspect the discovery endpoint manually. Pass an explicit `userinfo` URL or function if the IdP is non-compliant."})}if(typeof d.userinfo_endpoint!="string"||!d.userinfo_endpoint)throw routecraft.rcError("RC5021",new Error("missing userinfo_endpoint in discovery document"),{message:`OIDC Discovery document at ${o.toString()} does not advertise a userinfo_endpoint`,suggestion:"Pass an explicit `userinfo` URL or function for this IdP; it does not advertise auto-discoverable userinfo."});let c=new URL(d.userinfo_endpoint),m=Jn(l.headers.get("cache-control"))??zn;return r=c,i=Date.now()/1e3+m,c};return async()=>r&&Date.now()/1e3<i?r:a||(a=s().finally(()=>{a=null;}),a)}async function Zn(t,e){let n;try{n=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});}catch(o){throw routecraft.rcError("RC5021",o,{message:`userinfo fetch failed at ${t.toString()}`,suggestion:"Verify network access to the IdP's userinfo endpoint and that the bearer token's scopes permit it."})}if(!n.ok)throw routecraft.rcError("RC5021",new Error(`userinfo returned ${n.status} ${n.statusText}`),{message:`userinfo fetch failed at ${t.toString()} (status ${n.status})`,suggestion:"Check the bearer token has the scopes required by the IdP's userinfo endpoint (typically `openid`, `email`, `profile`)."});try{return await n.json()}catch(o){throw routecraft.rcError("RC5021",o,{message:`userinfo response at ${t.toString()} is not valid JSON`,suggestion:"Inspect the userinfo endpoint manually; it must return a JSON object per OIDC Core \xA75.3.2."})}}function Lt(t,e){let n={...t};for(let[o,r]of Object.entries(e))Wn.includes(o)||r!==void 0&&(n[o]=r);return n}function Qn(t){let e={userinfoClaims:t};return typeof t.email=="string"&&(e.email=t.email),typeof t.name=="string"&&(e.name=t.name),Array.isArray(t.roles)&&(e.roles=t.roles.filter(n=>typeof n=="string")),e}function ct(t,e,n){let o=new dt;if(typeof e=="function")return async i=>{let a=await t(i),s=await o.getOrCompute(i,a.expiresAt,()=>e(a,i));return Lt(a,s)};let r=Xn(e,n);return async i=>{let a=await t(i),s=await o.getOrCompute(i,a.expiresAt,async()=>{let l=await r(),d=await Zn(l,i),c=d.sub;if(typeof c!="string"||c.length===0)throw routecraft.rcError("RC5022",new Error("userinfo response is missing `sub`"),{message:"userinfo response is missing `sub` (required per OIDC Core \xA75.3.2)",suggestion:"Inspect the userinfo endpoint manually; the response MUST include a `sub` claim. If the IdP returns a non-standard identifier, use a function-mode `userinfo` and map it yourself."});if(c!==a.subject)throw routecraft.rcError("RC5022",new Error(`userinfo \`sub\` (${c}) does not match token \`sub\` (${a.subject})`),{message:"userinfo response `sub` does not match the verified token's `sub` (OIDC Core \xA75.3.2 violation)",suggestion:"This indicates a compromised userinfo endpoint or a misconfigured userinfo URL. Verify the issuer / userinfo mapping; do not relax this check."});return Qn(d)});return Lt(a,s)}}function ut(t){return typeof t=="object"&&t!==null&&t.code==="ERR_JWT_EXPIRED"}var eo=new Set(["ERR_JWKS_TIMEOUT","ERR_JWKS_INVALID","ERR_JOSE_GENERIC"]),to=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","ETIMEDOUT","UND_ERR_CONNECT_TIMEOUT","UND_ERR_HEADERS_TIMEOUT","UND_ERR_SOCKET"]);function It(t){let e=t;for(let n=0;n<5;n++){if(typeof e!="object"||e===null)return false;let o=e.code;if(typeof o=="string"&&(eo.has(o)||to.has(o)))return true;e=e.cause;}return false}var _t=new async_hooks.AsyncLocalStorage,W=class{context;options;server=null;transport=null;httpServer=null;httpSessions=new Map;running=false;toolsListLogged=false;validatorVerifier=null;constructor(e,n={}){this.context=e,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...n},this.validateResourceConfig();}resolveServerIcons(){return this.options.icons===void 0?xt:this.options.icons}defaultUnlessEmpty(e,n){let o=e??n;return o===""?void 0:o}buildServerInfo(){let e={name:this.options.name,version:this.options.version};this.options.title!==void 0&&(e.title=this.options.title);let n=this.defaultUnlessEmpty(this.options.description,"Powered by Routecraft.dev");n!==void 0&&(e.description=n);let o=this.defaultUnlessEmpty(this.options.websiteUrl,"https://routecraft.dev");o!==void 0&&(e.websiteUrl=o);let r=this.resolveServerIcons();return r.length>0&&(e.icons=r),e}buildServerOptions(){let e={capabilities:{tools:{}}},n=this.defaultUnlessEmpty(this.options.instructions,"");return n!==void 0&&(e.instructions=n),e}validateResourceConfig(){let e=this.options.resource?.url;if(e===void 0)return;if(new URL(e.toString()).protocol!=="https:"&&process.env.NODE_ENV==="production")throw new TypeError("mcpPlugin: resource.url must use HTTPS in production")}async start(){if(this.running){this.context.logger.warn({},"MCP server already running");return}try{let e=this.options.transport;e==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info({name:this.options.name,version:this.options.version,transport:e},"MCP server started"),this.logExposedToolsOnce();}catch(e){let n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Failed to start MCP server";throw this.context.logger.error({err:e},n),e}}async startStdio(){let n=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/index.js'),{adapterName:"mcp (stdio)",packageName:"@modelcontextprotocol/sdk"})).Server,r=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/stdio.js'),{adapterName:"mcp (stdio)",packageName:"@modelcontextprotocol/sdk"})).StdioServerTransport;this.server=new n(this.buildServerInfo(),this.buildServerOptions()),await this.setupRequestHandlers(),this.transport=new r,await this.server.connect(this.transport);}async startHttp(){this.options.auth&&Pe(this.options.auth)?await this.startHttpWithOAuth(this.options.auth):await this.startHttpWithValidator();}async importSdkHttp(){let n=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/index.js'),{adapterName:"mcp (http)",packageName:"@modelcontextprotocol/sdk"})).Server,r=(await import('@modelcontextprotocol/sdk/server/streamableHttp.js').catch(()=>null))?.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");return {ServerCtor:n,TransportClass:r}}async createSession(e,n){let o=new e(this.buildServerInfo(),this.buildServerOptions());await this.setupRequestHandlersOn(o);let r=new n({sessionIdGenerator:()=>crypto.randomUUID(),onsessioninitialized:d=>{this.httpSessions.set(d,{server:o,transport:r}),this.context.logger.debug({sessionId:d},"MCP session created"),this.context.emit("plugin:mcp:session:created",{sessionId:d});},enableJsonResponse:true});await o.connect(r);let a=r,s=a.onclose;a.onclose=()=>{let d=r.sessionId;d&&(this.httpSessions.delete(d),this.context.logger.debug({sessionId:d},"MCP session closed"),this.context.emit("plugin:mcp:session:closed",{sessionId:d})),s?.();};let l=r.handleRequest;if(typeof l!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");return {transport:r,handleRequest:l.bind(r)}}async handleMcpRequest(e,n,o,r){let i=e.headers["mcp-session-id"],a=s=>_t.run(o,s);try{if(i&&this.httpSessions.has(i)){let s=this.httpSessions.get(i),l=s.transport.handleRequest;await a(()=>l.call(s.transport,e,n));}else if(!i||e.method==="POST"){let{handleRequest:s}=await r();await a(()=>s(e,n));}else n.writeHead(404,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Session not found"}));}catch(s){let l=routecraft.isRoutecraftError(s)?s.meta.message:s instanceof Error?s.message:"MCP HTTP request error";this.context.logger.error({err:s},l),n.headersSent||(n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Internal Server Error"})));}}resolveResourceUrl(){let e=this.options.resource?.url;if(e!==void 0)return e.toString();let n=this.options.host,o=this.getHttpPort()??this.options.port;return `http://${n}:${o}/mcp`}resolveResourceName(){return this.options.title??this.options.name}buildProtectedResourceMetadata(){let e={resource:this.resolveResourceUrl(),bearer_methods_supported:["header"]};e.resource_name=this.resolveResourceName();let n=this.options.auth;if(n&&!("provider"in n)&&"issuer"in n){let r=n.issuer;r!==void 0&&(e.authorization_servers=Array.isArray(r)?r:[r]);}let o=this.options.resource;return o?.scopesSupported&&o.scopesSupported.length>0&&(e.scopes_supported=o.scopesSupported),o?.documentationUrl!==void 0&&(e.resource_documentation=o.documentationUrl.toString()),e}resolveResourceMetadataUrl(){return new URL(ke,this.resolveResourceUrl()).toString()}buildWwwAuthenticateHeader(){return `Bearer realm="mcp", resource_metadata="${this.resolveResourceMetadataUrl()}"`}serveProtectedResourceMetadata(e){let n=this.buildProtectedResourceMetadata();e.writeHead(200,{"Content-Type":"application/json","Cache-Control":"public, max-age=3600"}),e.end(JSON.stringify(n));}async startHttpWithValidator(){let{ServerCtor:e,TransportClass:n}=await this.importSdkHttp(),o=this.options.port,r=this.options.host,i=at(this.options.cors);this.validatorVerifier=this.buildValidatorVerifier();let a=()=>this.createSession(e,n);this.httpServer=http.createServer(async(s,l)=>{let d=s.url?.split("?")[0]??"",c=s.headers.origin,m=Array.isArray(c)?c[0]:c,u=new URL(this.resolveResourceUrl()),{ownedPaths:p,metadataPaths:f}=lt(u);if(s.method==="OPTIONS"&&i!==null&&p.has(d)){fe(l,i,m,true),l.writeHead(204),l.end();return}if(s.method!=="OPTIONS"&&fe(l,i,m,false),f.has(d)){this.serveProtectedResourceMetadata(l);return}if(d!=="/mcp"&&d!=="/mcp/"){l.writeHead(404,{"Content-Type":"application/json"}),l.end(JSON.stringify({error:"Not Found",path:d}));return}let h;if(this.options.auth){let g=await this.validateAuth(s);if(!g){l.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":this.buildWwwAuthenticateHeader()}),l.end(JSON.stringify({error:"Unauthorized"}));return}h=g;}await this.handleMcpRequest(s,l,h,a);}),await this.listenHttp(o,r);}async startHttpWithOAuth(e){let{ServerCtor:n,TransportClass:o}=await this.importSdkHttp(),r;try{let P=await import('express');r=P.default??P;}catch{throw new Error('OAuth auth requires "express" (optional peer dependency of @routecraft/ai). Install it with: bun add express')}let a=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/router.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).mcpAuthRouter,l=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).requireBearerAuth,c=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).ProxyOAuthServerProvider,u=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/errors.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).InvalidTokenError,p=this.options.userinfo===void 0?e.verifyAccessToken:ct(e.verifyAccessToken,this.options.userinfo,e.issuer),f=async P=>{let C;try{C=await p(P);}catch(v){let A=ut(v),T={reason:v instanceof Error?v.message:"invalid_token",scheme:"bearer",source:"mcp",path:"oauth"};throw A?this.context.logger.debug({err:v,...T},"Auth rejected: token expired"):this.context.logger.warn({err:v,...T},"Auth rejected: token validation failed"),this.context.emit("auth:rejected",T),routecraft.isRoutecraftError(v)||It(v)?v:new u(A?"Token has expired":"Invalid token")}if(C.expiresAt===void 0){let v={reason:"missing_expires_at",scheme:"bearer",source:"mcp",path:"oauth"};throw this.context.logger.warn(v,"Auth rejected: OAuth principal is missing expiresAt"),this.context.emit("auth:rejected",v),new Error("oauth: verifyAccessToken must return a principal with expiresAt (required by MCP SDK bearer middleware)")}return C.clientId||this.context.logger.debug({subject:C.subject},"oauth: principal missing clientId; using subject as fallback for AuthInfo.clientId"),{token:P,clientId:C.clientId??C.subject,scopes:C.scopes??[],expiresAt:C.expiresAt,extra:{principal:C}}},h=new c({endpoints:{authorizationUrl:e.endpoints.authorizationUrl,tokenUrl:e.endpoints.tokenUrl,revocationUrl:e.endpoints.revocationUrl,registrationUrl:e.endpoints.registrationUrl},verifyAccessToken:f,getClient:e.getClient}),g=this.options.port,R=this.options.host;if(g===0&&this.options.resource?.url===void 0)throw new TypeError('mcpPlugin: OAuth-proxy mode requires either a fixed `port` or an explicit `resource.url`. With `port: 0` (ephemeral) and no `resource.url`, the protected-resource metadata URL would advertise `:0`. Pass `resource: { url: "https://..." }` or a non-zero `port`.');let x=new URL(this.resolveResourceUrl()),{ownedPaths:D,metadataPaths:G}=lt(x),V=r(),it=at(this.options.cors);it!==null&&V.use((P,C,Re)=>{let v=P,A=C,_=v.url?.split("?")[0]??"",T=v.headers.origin,At=Array.isArray(T)?T[0]:T;if(v.method==="OPTIONS"&&D.has(_)){fe(A,it,At,true),A.writeHead(204),A.end();return}v.method!=="OPTIONS"&&fe(A,it,At,false),Re();});let Dn=(P,C)=>{this.serveProtectedResourceMetadata(C);};for(let P of G)V.get(P,Dn);let de={provider:h,issuerUrl:x};e.baseUrl&&(de.baseUrl=new URL(e.baseUrl.toString()));let ce=this.options.resource;ce?.scopesSupported&&ce.scopesSupported.length>0&&(de.scopesSupported=ce.scopesSupported),ce?.documentationUrl!==void 0&&(de.serviceDocumentationUrl=new URL(ce.documentationUrl.toString()));let St=this.resolveResourceName();St&&(de.resourceName=St),V.use(a(de));let Mt={verifier:h,resourceMetadataUrl:this.resolveResourceMetadataUrl()};e.requiredScopes&&(Mt.requiredScopes=e.requiredScopes),V.use("/mcp",l(Mt));let _n=()=>this.createSession(n,o);V.all("/mcp",async(P,C)=>{let Re=P,v=C,A=P.auth,_=this.authInfoToPrincipal(A);if(_){let T={subject:_.subject,scheme:_.scheme,source:"mcp"};this.context.logger.info(T,"Auth succeeded"),this.context.emit("auth:success",T);}await this.handleMcpRequest(Re,v,_,_n);}),this.httpServer=http.createServer(V),await this.listenHttp(g,R);}async listenHttp(e,n){await new Promise((i,a)=>{this.httpServer.listen(e,n,()=>i()),this.httpServer.on("error",s=>{let l=routecraft.isRoutecraftError(s)?s.meta.message:s instanceof Error?s.message:"MCP HTTP server listen failed";this.context.logger.error({err:s},l),a(s);});});let o=this.getHttpPort()??e,r={host:n,port:o,path:"/mcp"};this.context.logger.info(r,"MCP HTTP server listening"),this.context.emit("plugin:mcp:server:listening",r);}authInfoToPrincipal(e){if(!e)return;let n=e.extra?.principal;if(n)return n;let o={kind:"oauth",scheme:"bearer",subject:e.clientId,clientId:e.clientId,scopes:e.scopes};return e.expiresAt!==void 0&&(o.expiresAt=e.expiresAt),o}getHttpPort(){let e=this.httpServer?.address();if(e&&typeof e=="object"&&"port"in e)return e.port}buildValidatorVerifier(){let e=this.options.auth;if(!e||!("validator"in e))return null;let n=o=>Promise.resolve(e.validator(o));return this.options.userinfo===void 0?n:ct(n,this.options.userinfo,e.issuer)}async validateAuth(e){let n=this.options.auth;if(!n||!("validator"in n))return null;let o=this.validatorVerifier??this.buildValidatorVerifier();if(!o)return null;let r=e.headers.authorization;if(!r||Array.isArray(r)){let s={reason:"missing_header",scheme:"bearer",source:"mcp"};return this.context.logger.debug(s,"Auth rejected: missing or malformed Authorization header"),this.context.emit("auth:rejected",s),null}let i=/^bearer\s+(.+)$/i.exec(r);if(!i){let s={reason:"unsupported_scheme",scheme:"bearer",source:"mcp"};return this.context.logger.debug(s,"Auth rejected: unsupported authorization scheme"),this.context.emit("auth:rejected",s),null}let a=i[1];try{let s=await o(a),l={subject:s.subject,scheme:s.scheme,source:"mcp"};return this.context.logger.info(l,"Auth succeeded"),this.context.emit("auth:success",l),s}catch(s){let d={reason:s instanceof Error?s.message:"invalid_token",scheme:"bearer",source:"mcp"};return ut(s)?this.context.logger.debug({err:s,...d},"Auth rejected: token expired"):this.context.logger.warn({err:s,...d},"Auth rejected: token validation failed"),this.context.emit("auth:rejected",d),null}}async setupRequestHandlers(){await this.setupRequestHandlersOn(this.server);}async setupRequestHandlersOn(e){let o=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/types.js'),{adapterName:"mcp",packageName:"@modelcontextprotocol/sdk"}),r=o.ListToolsRequestSchema,i=o.CallToolRequestSchema;if(!r||!i)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let a=e;a.setRequestHandler(r,async()=>{let s=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:s}}),a.setRequestHandler(i,async s=>{let d=s.params;return await this.handleToolCall(d.name||"",d.arguments||{})});}async stop(){if(this.running)try{if(this.httpServer){for(let[,n]of this.httpSessions){let o=n.transport;typeof o.close=="function"&&await o.close();}this.httpSessions.clear();let e=this.httpServer;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await new Promise(n=>{this.httpServer.close(()=>n());}),this.httpServer=null;}if(this.transport){let e=this.transport;typeof e.close=="function"&&await e.close();}this.running=!1,this.context.logger.info({},"MCP server stopped");}catch(e){let n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Error stopping MCP server";this.context.logger.error({err:e},n);}}logExposedToolsOnce(){if(this.toolsListLogged)return;let e=this.getAvailableTools();if(e.length===0)return;let n=e.map(r=>r.name),o={tools:n,count:n.length};this.context.logger.info(o,"Exposing MCP tools"),this.context.emit("plugin:mcp:server:tools:exposed",o),this.toolsListLogged=true;}getAvailableTools(){let e=this.context.getStore(O);if(!e)return [];let n=Array.from(e.values()),o=this.options.tools;if(o)if(Array.isArray(o)){let r=new Set(o);n=n.filter(i=>r.has(i.endpoint));}else typeof o=="function"&&(n=n.filter(o));return n.map(r=>this.entryToMcpTool(r))}entryToMcpTool(e){let n={name:e.endpoint,description:e.description,inputSchema:this.schemaToJsonSchema(e.input?.body)};e.title!==void 0&&(n.title=e.title),e.output?.body!==void 0&&(n.outputSchema=this.schemaToJsonSchema(e.output.body)),e.annotations!==void 0&&(n.annotations=e.annotations);let o=e.icons??this.resolveServerIcons();return o.length>0&&(n.icons=o),n}schemaToJsonSchema(e){if(!e||typeof e!="object")return {type:"object"};let n=e["~standard"];if(n?.jsonSchema?.input)try{let o=n.jsonSchema.input({target:"draft-2020-12"});return typeof o=="object"&&o!==null?o:{type:"object"}}catch(o){return this.context.logger.debug(o,"Standard JSON Schema conversion failed"),{type:"object"}}return "~standard"in e?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(e,n){try{let r=this.context.getStore(O)?.get(e);if(!r){let m=new Error(`Tool not found: ${e}`);return this.context.emit("plugin:mcp:tool:failed",{tool:e,error:m.message}),{isError:!0,content:[{type:"text",text:`Error: Tool not found: ${e}`}]}}let i=typeof n=="string"?(()=>{try{return JSON.parse(n)||{}}catch{return {input:n}}})():n&&typeof n=="object"?n:{};this.context.logger.debug({bodyType:typeof i,body:i},"MCP tool call exchange body");let a=_t.getStore(),s={"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`};a&&(s[routecraft.HeadersKeys.AUTH_PRINCIPAL]=routecraft.markAuthentic(a));let l=new routecraft.DefaultExchange(this.context,{body:i,headers:s});this.context.emit("plugin:mcp:tool:called",{tool:e,args:n});let d=await r.handler(l),c=typeof d.body=="string"?d.body:JSON.stringify(d.body);return this.context.emit("plugin:mcp:tool:completed",{tool:e}),{content:[{type:"text",text:c}]}}catch(o){let r=routecraft.isRoutecraftError(o)?o.meta.message:o instanceof Error?o.message:String(o);this.context.logger.error({tool:e,err:o},r),this.context.emit("plugin:mcp:tool:failed",{tool:e,error:r});let i=r;if(routecraft.isRoutecraftError(o)){let a=o.cause;a?.message&&(i=`${r}: ${a.message}`);}return {content:[{type:"text",text:`Error: ${i}`}],isError:true}}}};function Nt(t){if(t.transport==="http"){if(t.port!==void 0){if(typeof t.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(t.port<0||t.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(t.host!==void 0&&typeof t.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}if(t.transport==="http"&&t.cors!==void 0&&t.cors!==false){let{origin:e}=t.cors;if(!(e==="*"||typeof e=="string"||Array.isArray(e)||typeof e=="function"))throw new TypeError("mcpPlugin: cors.origin must be '*', a string, a string array, or a function")}if(t.auth!==void 0)if("provider"in t.auth){if(t.auth.provider!=="oauth")throw new TypeError('mcpPlugin: auth.provider must be "oauth". Use the oauth() helper.')}else if("validator"in t.auth){if(typeof t.auth.validator!="function")throw new TypeError("mcpPlugin: auth.validator must be a function that returns a Principal (throw to reject)")}else throw new TypeError("mcpPlugin: auth must have either a 'validator' function or 'provider' set to 'oauth'. Use jwt(), jwks(), oauth(), or a custom { validator } object.");if(t.clients){for(let[e,n]of Object.entries(t.clients))if(typeof n=="object"&&n!==null&&"transport"in n&&n.transport==="stdio"&&(!n.command||typeof n.command!="string"))throw new TypeError(`mcpPlugin: stdio client "${e}" must have a non-empty command string`)}if(t.maxRestarts!==void 0&&(typeof t.maxRestarts!="number"||!Number.isInteger(t.maxRestarts)||t.maxRestarts<0))throw new TypeError("mcpPlugin: maxRestarts must be a non-negative integer");if(t.restartDelayMs!==void 0&&(typeof t.restartDelayMs!="number"||t.restartDelayMs<=0))throw new TypeError("mcpPlugin: restartDelayMs must be a positive number");if(t.restartBackoffMultiplier!==void 0&&(typeof t.restartBackoffMultiplier!="number"||t.restartBackoffMultiplier<1))throw new TypeError("mcpPlugin: restartBackoffMultiplier must be >= 1");if(t.toolRefreshIntervalMs!==void 0&&(typeof t.toolRefreshIntervalMs!="number"||!Number.isInteger(t.toolRefreshIntervalMs)||t.toolRefreshIntervalMs<0))throw new TypeError("mcpPlugin: toolRefreshIntervalMs must be a non-negative integer")}async function $t(t,e){let n=e["~standard"];if(!n?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let o=n.validate(t);if(o instanceof Promise&&(o=await o),o.issues)throw new Error(`mcpPlugin options validation failed: ${routecraft.formatSchemaIssues(o.issues)}`);if(o.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return o.value}function Te(t){let e=t?.content;if(!Array.isArray(e)||e.length===0)return t;if(e.length===1){let n=e[0];if(n.type==="text"&&typeof n.text=="string")return n.text;if(typeof n.data=="string")return n.data}return e}var Se=class{constructor(e,n,o,r){this.options=e;this.logger=n;this.onEvent=o;this.onToolsUpdated=r;}options;logger;onEvent;onToolsUpdated;client=null;transport=null;running=false;stopping=false;restartCount=0;restartTimer=null;stabilityTimer=null;tools=[];async start(){if(this.running)return;this.stopping=false,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null);let e=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (stdio client)",packageName:"@modelcontextprotocol/sdk"}),n=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/stdio.js'),{adapterName:"mcp (stdio client)",packageName:"@modelcontextprotocol/sdk"}),{serverId:o,command:r,args:i,env:a,cwd:s}=this.options;this.transport=new n.StdioClientTransport({command:r,args:i??[],...a?{env:a}:{},...s?{cwd:s}:{},stderr:"pipe"});let l=this.transport;l.onerror=d=>{this.logger.error({err:d,serverId:o},"Stdio client transport error"),this.onEvent(`plugin:mcp:client:${o}:error`,{serverId:o,error:d});},l.onclose=()=>{this.stopping||(this.running=false,this.handleDisconnect());},l.stderr?.on&&l.stderr.on("data",d=>{this.logger.debug({serverId:o,stderr:String(d).trimEnd()},"Stdio client stderr output");}),this.client=new e.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{},listChanged:{tools:{onChanged:(d,c)=>{c&&Array.isArray(c.tools)?(this.tools=c.tools,this.onToolsUpdated(o,this.tools),this.onEvent(`plugin:mcp:client:${o}:tools:listed`,{serverId:o,toolCount:this.tools.length})):this.refreshTools();}}}}),await this.client.connect(this.transport),this.running=true,await this.refreshTools(),this.logger.info({serverId:o,toolCount:this.tools.length},"Stdio client started"),this.onEvent(`plugin:mcp:client:${o}:started`,{serverId:o,toolCount:this.tools.length});}async stop(){this.stopping=true,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null),this.stabilityTimer&&(clearTimeout(this.stabilityTimer),this.stabilityTimer=null);let{serverId:e}=this.options;if(this.client){try{await this.client.close();}catch{}this.client=null;}if(this.transport){let n=this.transport;if(typeof n.close=="function")try{await Promise.resolve(n.close());}catch{}this.transport=null;}this.running=false,this.logger.info({serverId:e},"Stdio client stopped"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"graceful"});}async callTool(e,n){if(!this.running||!this.client)throw new Error(`Stdio client "${this.options.serverId}" is not running. Cannot call tool "${e}".`);let o=await this.client.callTool({name:e,arguments:n});return Te(o)}getTools(){return [...this.tools]}isRunning(){return this.running}async refreshTools(){if(!this.client)return;let{serverId:e}=this.options;try{let n=await this.client.listTools();this.tools=n.tools??[],this.onToolsUpdated(e,this.tools),this.onEvent(`plugin:mcp:client:${e}:tools:listed`,{serverId:e,toolCount:this.tools.length});}catch(n){this.logger.warn({err:n,serverId:e},"Failed to list tools for stdio client");}}handleDisconnect(){let{serverId:e,maxRestarts:n,restartDelayMs:o,restartBackoffMultiplier:r}=this.options;if(this.tools.length>0&&(this.tools=[],this.onToolsUpdated(e,[])),this.logger.warn({serverId:e},"Stdio client disconnected unexpectedly"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"unexpected"}),this.restartCount>=n){this.logger.error({serverId:e,restartCount:this.restartCount,maxRestarts:n},"Stdio client exceeded max restarts, giving up"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:new Error(`Max restarts (${n}) exceeded for stdio client "${e}"`)});return}let i=o*Math.pow(r,this.restartCount);this.logger.info({serverId:e,restartCount:this.restartCount,delayMs:i},"Scheduling stdio client restart"),this.restartTimer=setTimeout(()=>{this.restartTimer=null,this.restart();},i);}async restart(){let{serverId:e}=this.options;this.restartCount++,this.client=null,this.transport=null;try{await this.start(),this.logger.info({serverId:e,restartCount:this.restartCount},"Stdio client restarted successfully"),this.onEvent(`plugin:mcp:client:${e}:restarted`,{serverId:e,restartCount:this.restartCount});let n=this.options.restartDelayMs*2;this.stabilityTimer=setTimeout(()=>{this.stabilityTimer=null,this.running&&(this.restartCount=0);},n);}catch(n){this.logger.error({err:n,serverId:e,restartCount:this.restartCount},"Stdio client restart failed"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:n}),this.handleDisconnect();}}};function ao(t){if(!t)return [];let e=[];return t.readOnlyHint&&e.push("read-only"),t.destructiveHint&&e.push("destructive"),t.idempotentHint&&e.push("idempotent"),t.openWorldHint&&e.push("open-world"),e}var K=class{tools=new Map;setToolsForSource(e,n,o){let r=new Map;for(let i of o){let a={name:i.name,inputSchema:i.inputSchema,source:e,transport:n};i.description!==void 0&&(a.description=i.description),i.annotations!==void 0&&(a.annotations=i.annotations);let s=ao(i.annotations);s.length>0&&(a.tags=s),r.set(i.name,a);}this.tools.set(e,r);}removeSource(e){this.tools.delete(e);}getTools(){let e=[];for(let n of this.tools.values())for(let o of n.values())e.push(o);return e}getToolsByServer(e){let n=this.tools.get(e);return n?Array.from(n.values()):[]}getTool(e){for(let n of this.tools.values()){let o=n.get(e);if(o)return o}}getToolBySource(e,n){return this.tools.get(e)?.get(n)}};var jt=new WeakMap;async function lo(t){if(typeof t=="function")return t();if(Array.isArray(t)){if(t.length===0)throw new Error("McpClientAuthOptions.token array must not be empty");let e=(jt.get(t)??0)%t.length;return jt.set(t,e+1),t[e]}return t}async function Me(t){if(!t)return;let e={},n=t.headers??{},o=Object.entries(n).find(([r])=>r.toLowerCase()==="authorization")?.[1];if(o)e.Authorization=o;else if(t.token!==void 0){let r=await lo(t.token);if(r.length===0)throw new Error("McpClientAuthOptions.token must be a non-empty string when provided");e.Authorization=`Bearer ${r}`;}for(let[r,i]of Object.entries(n))r.toLowerCase()!=="authorization"&&(e[r]=i);return Object.keys(e).length>0?e:void 0}function Ht(t){return "transport"in t&&t.transport==="stdio"}function Ae(t={}){Nt(t);let e=null,n=new Map,o=new Map,r=[],i=null;return {async apply(c){if(c.setStore(pe,true),i=new K,c.setStore(B,i),c.setStore(me,n),t.clients&&Object.keys(t.clients).length>0){let m=Object.entries(t.clients),u=new Map;for(let[p,f]of m)u.set(p,f);c.setStore(S,u);for(let[p,f]of m){if(Ht(f))await a(c,p,f,i);else {let g=f;await l(c,p,g.url,i,g.auth),d(c,p,g.url,i,g.auth);}let h=Ht(f)?"stdio":"http";c.emit(`plugin:mcp:client:${p}:registered`,{serverId:p,transport:h});}}e=new W(c,t),await e.start();},async teardown(c){for(let m of r)clearInterval(m);r.length=0;for(let[m,u]of o)try{await u.close();}catch(p){c.logger.error({err:p,serverId:m,operation:"close"},"Failed to close HTTP client");}o.clear();for(let[m,u]of n)try{await u.stop();}catch(p){c.logger.error({err:p,serverId:m,operation:"stop"},"Failed to stop stdio client");}if(n.clear(),e){try{await e.stop();}catch(m){c.logger.error({err:m,operation:"stop"},"Failed to stop MCP server plugin");}e=null;}i=null;}};async function a(c,m,u,p){let f={serverId:m,command:u.command,args:u.args??[],maxRestarts:t.maxRestarts??5,restartDelayMs:t.restartDelayMs??1e3,restartBackoffMultiplier:t.restartBackoffMultiplier??2};u.env!==void 0&&(f.env=u.env),u.cwd!==void 0&&(f.cwd=u.cwd);let h=new Se(f,c.logger,(g,R)=>{c.emit(g,R);},(g,R)=>{p.setToolsForSource(g,"stdio",R);});n.set(m,h);try{await h.start();}catch(g){c.logger.error({err:g,serverId:m,operation:"start"},"Failed to start stdio client"),c.emit(`plugin:mcp:client:${m}:error`,{serverId:m,error:g});}}async function s(c,m,u){let p=o.get(c);if(p)return p;let{Client:f}=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),{StreamableHTTPClientTransport:h}=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/streamableHttp.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),g=await Me(u),R=g?{requestInit:{headers:g}}:void 0,x=new h(new URL(m),R),D=new f({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}});await D.connect(x);let G=D;return o.set(c,G),G}async function l(c,m,u,p,f){try{let R=(await(await s(m,u,f)).listTools()).tools??[];p.setToolsForSource(m,"http",R),c.emit(`plugin:mcp:client:${m}:tools:listed`,{serverId:m,toolCount:R.length});}catch(h){o.delete(m),c.logger.warn({err:h,serverId:m,url:u,operation:"listTools"},"Failed to list tools from HTTP client");}}function d(c,m,u,p,f){let h=t.toolRefreshIntervalMs??6e4;if(h<=0)return;let g=setInterval(()=>{l(c,m,u,p,f);},h);r.push(g);}}var Oe=new Map;async function be(){let t=[...Oe.entries()];Oe.clear(),t.length!==0&&await Promise.all(t.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function co(t){return t.includes("/")?t:`Xenova/${t}`}var uo='The Hugging Face embedding provider requires "@huggingface/transformers". Install it with: bun add @huggingface/transformers';function po(t,e){if(!(t instanceof Error))return false;let n=t.message??"";return (n.includes("ERR_MODULE_NOT_FOUND")||n.includes("Cannot find module")||n.includes("Cannot find package"))&&n.includes(e)}async function mo(t){let e=co(t),n=Oe.get(e);if(n)return n.run;let o;try{o=(await import('@huggingface/transformers')).pipeline;}catch(s){throw po(s,"@huggingface/transformers")?new Error(uo):s}let r=await o("feature-extraction",e,{dtype:"fp32"}),i=(s,l)=>r(s,{pooling:l?.pooling??"mean",normalize:l?.normalize??true}),a={run:i,dispose:typeof r.dispose=="function"?()=>Promise.resolve(r.dispose()):void 0};return Oe.set(e,a),i}function Gt(t){if(t===null)return "null";if(typeof t!="object")return String(t);if(Array.isArray(t))return `array(${t.length})`;try{let e=JSON.stringify(t);return e.length>100?e.slice(0,100)+"...":e}catch{return "object"}}function fo(t){if(Array.isArray(t))return t;if(t instanceof Float32Array)return Array.from(t);if(t&&typeof t=="object"&&"data"in t){let e=t.data;if(Array.isArray(e))return e;if(e instanceof Float32Array)return Array.from(e);throw new Error(`Embedding output .data must be an Array or Float32Array; got ${typeof e}: ${Gt(e)}`)}throw new Error(`Embedding output must be an Array, Float32Array, or object with .data; got ${typeof t}: ${Gt(t)}`)}async function Vt(t){let{config:e,modelName:n,text:o}=t;if(e.provider==="mock"){let i=[];for(let a=0;a<8;a++)i.push((o.length+a)%100/100);return i}if(e.provider==="huggingface"){let i=await(await mo(n))(o,{pooling:"mean",normalize:true}),a=i&&typeof i=="object"&&"data"in i?i.data:i;return fo(a)}throw e.provider==="ollama"||e.provider==="openai"?new Error(`Embedding provider "${e.provider}" is not yet implemented. Use huggingface for now.`):new Error(`Unknown embedding provider: ${e.provider}`)}var Ee=Symbol.for("routecraft.adapter.embedding.providers"),xe=Symbol.for("routecraft.adapter.embedding.options");function go(t,e){return {...e,provider:t}}function Le(t={providers:{}}){return {apply(e){let n=new Map;for(let[o,r]of Object.entries(t.providers))r!==void 0&&n.set(o,go(o,r));e.setStore(Ee,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(xe,t.defaultOptions);},async teardown(){await be();}}}function $(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`LLM model id must be "providerId:modelName" (e.g. ollama:lfm2.5-thinking). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function Ie(t,e){if(typeof t!="string"){let a=t.modelId??"";if(a.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`LLM model: inline LlmModelConfig for provider "${t.provider}" did not resolve to a model name. Either pass the model as a "providerId:modelName" string (e.g. "${t.provider}:<model>") or set "modelId" on the config. Providers "openai", "anthropic", and "gemini" do not carry a modelId field, so those must use the string form.`});return {config:t,modelName:a}}if(!e)throw new Error(`LLM model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${q.description}" can be read.`);let n=e.getStore(q);if(!n)throw new Error('LLM provider not found: no providers registered. Add llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }) to your config.');let{providerId:o,modelName:r}=$(t),i=n.get(o);if(!i)throw new Error(`LLM provider "${o}" not found. Register it with llmPlugin({ providers: { "${o}": { provider, apiKey?, baseURL? } } }).`);return {config:i,modelName:r}}function U(t,e){return t===void 0||t===""?"":typeof t=="function"?t(e):t}function De(t){let e=t.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}var qt={ollama:{baseURL:"http://localhost:11434/api"}};function z(t,e){throw new Error(`The ${e} LLM provider requires the "${t}" package. Install it with: bun add ${t}`)}function Y(t,e){if(!(t instanceof Error))return false;let n=t.message??"";return (n.includes("ERR_MODULE_NOT_FOUND")||n.includes("Cannot find module")||n.includes("Cannot find package"))&&n.includes(e)}function pt(t,e,n){if(t===null||typeof t!="object")throw new Error(`[${e}] Invalid model: expected an object, got ${typeof t}. Model id: ${n}`);let o=t;if(typeof o.doGenerate!="function")throw new Error(`[${e}] Invalid model: missing or invalid doGenerate method. Model id: ${n}. Ensure the provider returns an AI SDK-compatible language model.`);if(typeof o.doStream!="function")throw new Error(`[${e}] Invalid model: missing or invalid doStream method. Model id: ${n}. Ensure the provider returns an AI SDK-compatible language model.`)}function _e(t){return {...t.inputTokens!==void 0&&{inputTokens:t.inputTokens},...t.outputTokens!==void 0&&{outputTokens:t.outputTokens},...t.totalTokens!==void 0&&{totalTokens:t.totalTokens}}}function Bt(t){try{if("output"in t&&t.output!==void 0)return t.output}catch{}}function Wt(t){let e=t;if(typeof e.reasoningText=="string"&&e.reasoningText.length>0)return e.reasoningText}function Ne(t){if(t===null||typeof t!="object")return [];let e=t.steps;if(!Array.isArray(e))return [];let n=[];for(let o of e){if(o===null||typeof o!="object")continue;let r=o.toolCalls,i=o.toolResults;if(Array.isArray(r))for(let a of r){if(a===null||typeof a!="object")continue;let s=a,l=typeof s.toolCallId=="string"?s.toolCallId:"",d=typeof s.toolName=="string"?s.toolName:"",c=s.input??s.args,m={toolCallId:l,toolName:d,input:c};if(Array.isArray(i)){let u=i.find(p=>p.toolCallId===l);u&&(("output"in u||"result"in u)&&(m.output=u.output??u.result),"error"in u&&u.error!==void 0&&(m.error=u.error));}n.push(m);}}return n}function $e(t){let e={};return t.output!==void 0&&(e.output=t.output),t.tools!==void 0&&Object.keys(t.tools).length>0&&(e.tools=t.tools,t.stopWhen!==void 0&&(e.stopWhen=t.stopWhen)),t.abortSignal!==void 0&&(e.abortSignal=t.abortSignal),e}function Ue(t,e,n,o,r){let i={model:t,prompt:o,temperature:e.temperature};return e.maxTokens!==void 0&&(i.maxOutputTokens=e.maxTokens),n&&(i.system=n),e.topP!==void 0&&(i.topP=e.topP),e.frequencyPenalty!==void 0&&(i.frequencyPenalty=e.frequencyPenalty),e.presencePenalty!==void 0&&(i.presencePenalty=e.presencePenalty),{...i,...r}}async function je(t,e){switch(t.provider){case "openai":return yo(t,e);case "anthropic":return wo(t,e);case "gemini":return Ro(t,e);case "openrouter":return vo(t,e);case "ollama":return Co(t,e);default:{let n=t;throw new Error(`LLM provider not implemented: ${n.provider}`)}}}async function yo(t,e){let n;try{n=(await import('@ai-sdk/openai')).createOpenAI;}catch(i){throw Y(i,"@ai-sdk/openai")&&z("@ai-sdk/openai","OpenAI"),i}let o={apiKey:t.apiKey};return t.baseURL!==void 0&&(o.baseURL=t.baseURL),n(o)(e)}async function wo(t,e){let n;try{n=(await import('@ai-sdk/anthropic')).createAnthropic;}catch(r){throw Y(r,"@ai-sdk/anthropic")&&z("@ai-sdk/anthropic","Anthropic"),r}return n({apiKey:t.apiKey})(e)}async function Ro(t,e){let n;try{n=(await import('@ai-sdk/google')).createGoogleGenerativeAI;}catch(r){throw Y(r,"@ai-sdk/google")&&z("@ai-sdk/google","Gemini"),r}return n({apiKey:t.apiKey})(e)}async function vo(t,e){let n;try{n=(await import('@openrouter/ai-sdk-provider')).createOpenRouter;}catch(a){throw Y(a,"@openrouter/ai-sdk-provider")&&z("@openrouter/ai-sdk-provider","OpenRouter"),a}let o=n({apiKey:t.apiKey}),r=t.modelId??e,i=o.chat(r);return pt(i,"OpenRouter",r),i}async function Co(t,e){let n;try{n=(await import('ollama-ai-provider-v2')).createOllama;}catch(a){throw Y(a,"ollama-ai-provider-v2")&&z("ollama-ai-provider-v2","Ollama"),a}let o=n({baseURL:t.baseURL??qt.ollama.baseURL}),r=t.modelId??e,i=o(r);return pt(i,"Ollama",r),i}function Kt(t){if(t===null||typeof t!="object")return null;let e=t;switch(e.type){case "text-delta":{let o=Fe(e,"text")??Fe(e,"textDelta");return o===void 0||o.length===0?null:{type:"text-delta",text:o}}case "reasoning-delta":{let o=Fe(e,"text")??Fe(e,"delta");return o===void 0||o.length===0?null:{type:"reasoning-delta",text:o}}default:return null}}function Fe(t,e){let n=t[e];return typeof n=="string"?n:void 0}async function Yt(t){let{config:e,modelId:n,options:o,system:r,user:i,onDelta:a}=t,s=await je(e,n);return Po(s,o,r,i,$e(t),a)}async function Po(t,e,n,o,r,i){let{streamText:a}=await import('ai'),s=Ue(t,e,n,o,r),l=a(s);for await(let x of l.fullStream){let D=Kt(x);if(D!==null)try{await i(D);}catch(G){routecraft.logger.warn({err:G},"agent.onDelta listener threw; ignoring and continuing stream");}}let c={text:await l.text??"",raw:l},m=await J(l.usage);m&&(c.usage=_e(m));let u=await J(l.reasoningText);typeof u=="string"&&u.length>0&&(c.reasoning=u);let p=await J(l.output);p!==void 0&&(c.output=p);let f=await J(l.finishReason);typeof f=="string"&&(c.finishReason=f);let h=await J(l.steps),g=Ne({steps:h});g.length>0&&(c.toolCalls=g),Array.isArray(h)&&(c.stepsCount=h.length);let R=await J(l.response);return R&&Array.isArray(R.messages)&&(c.responseMessages=R.messages),c}async function J(t){if(t!==void 0)try{return await t}catch(e){routecraft.logger.debug({err:e},"llm.streamLlm: optional accessor rejected; treating as absent");return}}async function Jt(t){return Yt(t)}async function He(t){let{config:e,modelId:n,options:o,system:r,user:i}=t,a=await je(e,n);return ko(a,o,r,i,$e(t))}async function ko(t,e,n,o,r){let{generateText:i}=await import('ai'),a=Ue(t,e,n,o,r),s=await i(a),l={text:s.text??"",raw:s};s.usage&&(l.usage=_e(s.usage));let d=Bt(s);d!==void 0&&(l.output=d);let c=Wt(s);c&&(l.reasoning=c);let m=s.finishReason;typeof m=="string"&&(l.finishReason=m);let u=Ne(s);u.length>0&&(l.toolCalls=u);let p=s.steps;Array.isArray(p)&&(l.stepsCount=p.length);let f=s.response?.messages;return Array.isArray(f)&&(l.responseMessages=f),l}function Xt(t,e,n){let o=t["~standard"];if(!o?.validate)throw new Error(`${n} must be a StandardSchemaV1 with ~standard.validate`);let r=e==="input"?o.jsonSchema?.input:o.jsonSchema?.output,i=e==="input"?o.jsonSchema?.output:o.jsonSchema?.input,a=r?.({target:"draft-2020-12"})??i?.({target:"draft-2020-12"});if(!a||typeof a!="object")throw new Error(`${n} must expose ~standard.jsonSchema.input or .output for AI SDK use`);function s(l){let d;try{d=o.validate(l);}catch(m){return {success:false,error:m instanceof Error?m:new Error(String(m))}}return d instanceof Promise?{success:false,error:new Error(`${n}: async schema validation is not supported`)}:d.issues!=null&&(Array.isArray(d.issues)?d.issues.length>0:typeof d.issues=="object"&&d.issues!==null?Object.keys(d.issues).length>0:!!d.issues)?{success:false,error:new Error(routecraft.formatSchemaIssues(d.issues))}:{success:true,value:d.value}}return ai.jsonSchema(a,{validate:s})}function Zt(t){return Xt(t,"input","Tool input schema")}function Ge(t){let e=Xt(t,"output","LLM output schema");return ai.Output.object({schema:e})}async function Qt(t,e,n,o,r){if(t.length===0)return Object.create(null);let{tool:i}=await import('ai'),a=Object.create(null);for(let s of t){let l=s.guard,d=s.handler,c=xo(s.name,n,r);a[s.name]=i({description:s.description,inputSchema:Zt(s.input),execute:async(m,u)=>{let p={...c,abortSignal:u?.abortSignal??c.abortSignal},f=u?.toolCallId??crypto$1.randomUUID(),h=Date.now();e&&o&&e.emit(`route:${o.routeId}:agent:tool:invoked`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:f,toolName:s.name,input:m});try{l&&await l(m,p);let g=await d(m,p);return e&&o&&e.emit(`route:${o.routeId}:agent:tool:result`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:f,toolName:s.name,output:g,duration:Date.now()-h}),g}catch(g){throw e&&o&&e.emit(`route:${o.routeId}:agent:tool:error`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:f,toolName:s.name,error:g,duration:Date.now()-h}),g}}});}return a}function xo(t,e,n){return {logger:routecraft.logger.child({tool:t}),abortSignal:e,...n?{principal:Lo(n)}:{}}}function Lo(t){let e=routecraft.isAuthentic(t),n={...t};return n.audience&&(n.audience=[...n.audience]),n.scopes&&(n.scopes=[...n.scopes]),n.roles&&(n.roles=[...n.roles]),n.claims&&(n.claims=structuredClone(n.claims)),en(n),e?routecraft.markAuthentic(n):n}function en(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t)||ArrayBuffer.isView(t))return t;e.add(t);for(let n of Object.keys(t)){let o=t[n];o!==null&&typeof o=="object"&&en(o,e);}return Object.freeze(t)}function nn(t,e){if(e===void 0)return;let n=t.headers[routecraft.HeadersKeys.CORRELATION_ID];return {exchangeId:t.id,correlationId:typeof n=="string"?n:t.id,routeId:e}}var Do=0,_o=1024,No=20,Ve=class{constructor(e){this.input=e;}input;async runUntilDone(e){return this.runWithValidation(e,void 0)}async runStream(e,n){return this.runWithValidation(e,n)}async runWithValidation(e,n){let{options:o,exchange:r}=this.input,i=o.validate,a=o.maxTurns??No,s=await this.prepare(e),l=0,d=this.input.user,c,m=[];try{for(;;){let u=a-l;if(u<=0)throw routecraft.rcError("RC5003",void 0,{message:c?`agent: maxTurns (${a}) reached while "validate" was still rejecting; last validator message: "${c}"`:`agent: maxTurns (${a}) reached.`});let p=await $o(s,d,u,e,n);if(l+=p.stepsCount??1,p.toolCalls&&p.toolCalls.length>0&&m.push(...p.toolCalls),!i)return this.emitFinished(p),mt(p,m);let f=await Promise.resolve(i(mt(p,m),{exchange:r,turnsUsed:l}));if(f==null)return this.emitFinished(p),mt(p,m);if(typeof f!="string"||f.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agent: "validate" returned a non-string, non-void value (${typeof f}). Return void to accept, a non-empty string to retry.`});c=f,d=jo(this.input.user,d,p,f);}}catch(u){throw this.emitError(u),u}}emitFinished(e){let n=this.input.dispatchIdentity,o=this.input.context;if(!n||!o)return;let r=e.finishReason??"unknown";o.emit(`route:${n.routeId}:agent:finished`,{routeId:n.routeId,exchangeId:n.exchangeId,correlationId:n.correlationId,finishReason:r,...e.usage?.inputTokens!==void 0&&{inputTokens:e.usage.inputTokens},...e.usage?.outputTokens!==void 0&&{outputTokens:e.usage.outputTokens},...e.usage?.totalTokens!==void 0&&{totalTokens:e.usage.totalTokens}});}emitError(e){let n=this.input.dispatchIdentity,o=this.input.context;!n||!o||o.emit(`route:${n.routeId}:agent:error`,{routeId:n.routeId,exchangeId:n.exchangeId,correlationId:n.correlationId,error:e});}async prepare(e){let{options:n,modelConfig:o,modelName:r,tools:i,system:a,context:s,exchange:l,dispatchIdentity:d}=this.input,c=await Qt(i,s,e,d,l.principal),m={modelConfig:o,modelName:r,system:a,vercelTools:c};return n.output!==void 0?{...m,output:Ge(n.output)}:m}};async function $o(t,e,n,o,r){let i=Object.keys(t.vercelTools).length>0?{tools:t.vercelTools,stopWhen:await Uo(n)}:{},a={config:t.modelConfig,modelId:t.modelName,options:{temperature:Do,maxTokens:_o},system:t.system,user:e,abortSignal:o,...t.output!==void 0?{output:t.output}:{},...i};return r?Jt({...a,onDelta:r}):He(a)}async function Uo(t){let{stepCountIs:e}=await import('ai');return e(t)}function jo(t,e,n,o){let r=typeof e=="string"?[{role:"user",content:t}]:e,i=n.responseMessages??[];return [...r,...i,{role:"user",content:`Validator: ${o}`}]}function mt(t,e){let n={text:t.text};return t.output!==void 0&&(n.output=t.output),t.reasoning!==void 0&&(n.reasoning=t.reasoning),t.usage&&(n.usage=t.usage),e.length>0&&(n.toolCalls=e),n}function on(t,e){return t.user!==void 0?U(t.user,e):De(e)}var j=Symbol.for("routecraft.adapter.agent.registry"),X=Symbol.for("routecraft.adapter.agent.default-options");var Z=Symbol.for("routecraft.adapter.skill.registry");var Go=j.description??"routecraft.adapter.agent.registry",F=class{constructor(e){this.binding=e;}binding;adapterId="routecraft.adapter.agent";async send(e){let n=routecraft.getExchangeContext(e),o=this.resolveOptions(n),r=Vo(o,n);if(r.model===void 0)throw routecraft.rcError("RC5003",void 0,{message:'Agent: no "model" supplied and no agentPlugin({ defaultOptions: { model } }) is set on this context. Specify "model" on the agent or set a context-level default.'});let{config:i,modelName:a}=Ie(r.model,n),s=qo(r,n),l=on(r,e),d=U(r.system,e);if(d.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" resolved to an empty string. When "system" is a function, it must return a non-empty string for the incoming exchange.'});let c=Bo(d,r.skills,n),m=Wo(c,r.principal,e.principal,e),u=routecraft.getExchangeRoute(e),p=nn(e,u?.definition.id),f=new Ve({options:r,modelConfig:i,modelName:a,tools:s,user:l,system:m,context:n,exchange:e,dispatchIdentity:p}),h=u?.signal??new AbortController().signal,g=this.binding.kind==="by-name"?this.binding.perCall?.onDelta??r.onDelta:r.onDelta;return g!==void 0?await f.runStream(h,g):await f.runUntilDone(h)}resolveOptions(e){if(this.binding.kind==="inline")return this.binding.options;if(!e)throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so the "${Go}" store can be read.`});let n=e.getStore(j);if(!n)throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" not found: no agents registered. Add agentPlugin({ agents: { "${this.binding.name}": {...} } }) to your config.`});let o=n.get(this.binding.name);if(!o){let r=Array.from(n.keys()).join(", ")||"<none>";throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" not found in registry. Known agents: ${r}.`})}return o}getMetadata(e){let n=e,o={};if(this.binding.kind==="by-name"&&(o.agent=this.binding.name),this.binding.kind==="inline"){let r=this.binding.options.model;typeof r=="string"&&(o.model=r);}return n.usage?.inputTokens!==void 0&&(o.inputTokens=n.usage.inputTokens),n.usage?.outputTokens!==void 0&&(o.outputTokens=n.usage.outputTokens),o}};function Vo(t,e){let n=e?.getStore(X);if(!n)return t;let o={...t};return o.model===void 0&&n.model!==void 0&&(o.model=n.model),o.tools===void 0&&n.tools!==void 0&&(o.tools=n.tools),o.maxTurns===void 0&&n.maxTurns!==void 0&&(o.maxTurns=n.maxTurns),o.principal===void 0&&n.principal!==void 0&&(o.principal=n.principal),o}function qo(t,e){if(t.tools===void 0)return [];if(!e)throw routecraft.rcError("RC5003",void 0,{message:"Agent: cannot resolve tools without a CraftContext."});return t.tools.resolve(e)}function Bo(t,e,n){if(!e||e.length===0)return t;if(!n)throw routecraft.rcError("RC5003",void 0,{message:'Agent: cannot resolve "skills" without a CraftContext.'});let o=n.getStore(Z);if(!o||o.size===0)throw routecraft.rcError("RC5003",void 0,{message:`Agent: declared "skills" (${e.map(i=>`"${i}"`).join(", ")}) but no skills are registered in this context. Install agentPlugin({ skills }) with the relevant entries.`});let r=[t];for(let i of e){let a=o.get(i);if(!a){let s=[...o.keys()].sort();throw routecraft.rcError("RC5003",void 0,{message:`Agent: unknown skill "${i}". `+(s.length>0?`Known skill names: ${s.map(l=>`"${l}"`).join(", ")}.`:"No skills are registered in this context.")})}r.push(`
1
+ 'use strict';var routecraft=require('@routecraft/routecraft'),async_hooks=require('async_hooks'),http=require('http'),crypto$1=require('crypto'),ai=require('ai'),fs=require('fs'),path=require('path'),promises=require('fs/promises');var z=Symbol.for("routecraft.adapter.llm.providers"),pe=Symbol.for("routecraft.adapter.llm.options");var at={ollama:{baseURL:"http://localhost:11434/api"},lmstudio:{baseURL:"http://localhost:1234/v1"}};function N(t,e,n){if(t===null||typeof t!="object")throw new Error(`[${e}] Invalid model: expected an object, got ${typeof t}. Model id: ${n}`);let o=t;if(typeof o.doGenerate!="function")throw new Error(`[${e}] Invalid model: missing or invalid doGenerate method. Model id: ${n}. Ensure the provider returns an AI SDK-compatible language model.`);if(typeof o.doStream!="function")throw new Error(`[${e}] Invalid model: missing or invalid doStream method. Model id: ${n}. Ensure the provider returns an AI SDK-compatible language model.`)}function ve(t){return {...t.inputTokens!==void 0&&{inputTokens:t.inputTokens},...t.outputTokens!==void 0&&{outputTokens:t.outputTokens},...t.totalTokens!==void 0&&{totalTokens:t.totalTokens}}}function Dt(t){try{if("output"in t&&t.output!==void 0)return t.output}catch{}}function _t(t){let e=t;if(typeof e.reasoningText=="string"&&e.reasoningText.length>0)return e.reasoningText}function ke(t){if(t===null||typeof t!="object")return [];let e=t.steps;if(!Array.isArray(e))return [];let n=[];for(let o of e){if(o===null||typeof o!="object")continue;let r=o.toolCalls,i=o.toolResults;if(Array.isArray(r))for(let a of r){if(a===null||typeof a!="object")continue;let s=a,l=typeof s.toolCallId=="string"?s.toolCallId:"",d=typeof s.toolName=="string"?s.toolName:"",c=s.input??s.args,u={toolCallId:l,toolName:d,input:c};if(Array.isArray(i)){let p=i.find(m=>m.toolCallId===l);p&&(("output"in p||"result"in p)&&(u.output=p.output??p.result),"error"in p&&p.error!==void 0&&(u.error=p.error));}n.push(u);}}return n}function Ce(t){let e={};return t.output!==void 0&&(e.output=t.output),t.tools!==void 0&&Object.keys(t.tools).length>0&&(e.tools=t.tools,t.stopWhen!==void 0&&(e.stopWhen=t.stopWhen)),t.abortSignal!==void 0&&(e.abortSignal=t.abortSignal),e}function be(t,e,n,o,r){let i={model:t,prompt:o,temperature:e.temperature};return e.maxTokens!==void 0&&(i.maxOutputTokens=e.maxTokens),n&&(i.system=n),e.topP!==void 0&&(i.topP=e.topP),e.frequencyPenalty!==void 0&&(i.frequencyPenalty=e.frequencyPenalty),e.presencePenalty!==void 0&&(i.presencePenalty=e.presencePenalty),{...i,...r}}var Nt=["openai","anthropic","openrouter","ollama","gemini","lmstudio","custom"];function eo(t){return Nt.includes(t)}function lt(t){if(!t||typeof t!="object")throw new TypeError("llmPlugin: options must be an object");if(!t.providers||typeof t.providers!="object")throw new TypeError("llmPlugin: options.providers must be an object (record of provider id \u2192 options)");for(let[e,n]of Object.entries(t.providers))if(n!==void 0){if(!n||typeof n!="object")throw new TypeError(`llmPlugin: providers["${e}"] must be an object`);if(!eo(e))throw new TypeError(`llmPlugin: providers["${e}"] is not a supported provider. Supported: ${Nt.join(", ")}`);switch(e){case "openai":case "anthropic":case "openrouter":case "gemini":if(typeof n.apiKey!="string"||!n.apiKey.trim())throw new TypeError(`llmPlugin: providers["${e}"].apiKey is required`);break;case "ollama":case "lmstudio":if(n.baseURL!==void 0&&typeof n.baseURL!="string")throw new TypeError(`llmPlugin: providers["${e}"].baseURL must be a string when provided`);if(n.modelId!==void 0&&(typeof n.modelId!="string"||!n.modelId.trim()))throw new TypeError(`llmPlugin: providers["${e}"].modelId must be a non-empty string when provided`);if(e==="lmstudio"&&n.apiKey!==void 0&&typeof n.apiKey!="string")throw new TypeError('llmPlugin: providers["lmstudio"].apiKey must be a string when provided');break;case "custom":{let o=n.model;if(typeof o=="function")break;if(o===null||typeof o!="object")throw new TypeError('llmPlugin: providers["custom"].model must be an AI SDK language model or a factory function');N(o,"custom","(custom)");break}}}if(t.defaultOptions!==void 0&&(typeof t.defaultOptions!="object"||t.defaultOptions===null))throw new TypeError("llmPlugin: defaultOptions must be an object when provided")}var to=["openai","anthropic","openrouter","ollama","gemini","lmstudio","custom"];function no(t,e){return {provider:t,...e}}function Pe(t={providers:{}}){return lt(t),{apply(e){let n=new Map;for(let o of to){let r=t.providers[o];r!==void 0&&n.set(o,no(o,r));}e.setStore(z,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(pe,t.defaultOptions);}}}var me=Symbol.for("routecraft.mcp.plugin.registered"),M=Symbol.for("routecraft.mcp.client.servers"),J=Symbol.for("routecraft.mcp.tool.registry"),fe=Symbol.for("routecraft.mcp.stdio.managers"),E=Symbol.for("routecraft.mcp.local-tool-registry"),Oe=(n=>(n.TOOL="routecraft.mcp.tool",n.SESSION="routecraft.mcp.session",n))(Oe||{});function Ae(t){return "provider"in t&&t.provider==="oauth"}var oo="GET, POST, OPTIONS";var ro="WWW-Authenticate, Mcp-Session-Id, Last-Event-ID",io=new Set(["localhost","127.0.0.1","::1","[::1]"]);function so(t){if(!t||t==="null")return false;let e;try{e=new URL(t);}catch{return false}return e.protocol!=="http:"&&e.protocol!=="https:"||e.username||e.password||e.pathname!=="/"&&e.pathname!==""||e.search||e.hash?false:io.has(e.hostname)?t:false}function dt(t){if(t===false)return null;if(t===void 0)return {resolveOrigin:so,isWildcard:false};let{origin:e}=t;if(e==="*")return {resolveOrigin:()=>"*",isWildcard:true};if(typeof e=="string"){let n=e;return {resolveOrigin:o=>o===n?n:false,isWildcard:false}}if(Array.isArray(e)){let n=new Set(e);return {resolveOrigin:o=>o!==void 0&&n.has(o)?o:false,isWildcard:false}}if(typeof e=="function")return {resolveOrigin:e,isWildcard:false};throw new TypeError("mcpPlugin: cors.origin must be '*', a string, a string array, or a function")}function ao(t,e){try{return t.resolveOrigin(e)}catch{return false}}function lo(t,e,n){if(!t)return {};let o={};t.isWildcard||(o.Vary="Origin");let r=ao(t,e);return r===false||(o["Access-Control-Allow-Origin"]=r,n?(o["Access-Control-Allow-Methods"]=oo,o["Access-Control-Allow-Headers"]="*",o["Access-Control-Allow-Private-Network"]="true"):o["Access-Control-Expose-Headers"]=ro),o}function ge(t,e,n,o){if(!e)return;let{Vary:r,...i}=lo(e,n,o);for(let[a,s]of Object.entries(i))t.setHeader(a,s);r!==void 0&&t.appendHeader("Vary",r);}var Te="/.well-known/oauth-protected-resource";function ct(t){let e=new Set([Te]),n=t.pathname;return n&&n!=="/"&&n!==""&&e.add(`${Te}${n}`),{ownedPaths:new Set([...e,"/mcp","/mcp/"]),metadataPaths:e}}var $t=t=>`<svg width="200" height="200" viewBox="0 0 200 200" fill="${t}" xmlns="http://www.w3.org/2000/svg"><path d="M125 175H75V125L125 175ZM175 175H125V125L175 175ZM125 25C152.614 25 175 47.3858 175 75C175 102.614 152.614 125 125 125V75H75L125 125H75L25 75V25H125Z" /></svg>`,jt=t=>`data:image/svg+xml;base64,${Buffer.from(t,"utf8").toString("base64")}`,Ut=Object.freeze([Object.freeze({src:jt($t("#ffffff")),mimeType:"image/svg+xml",theme:"dark"}),Object.freeze({src:jt($t("#000000")),mimeType:"image/svg+xml",theme:"light"})]);var uo=["subject","issuer","audience","expiresAt","claims"],po=1e4,mo=3600,ut=class{entries=new Map;inFlight=new Map;maxEntries;constructor(e=po){this.maxEntries=e;}async getOrCompute(e,n,o){if(n===void 0||!Number.isFinite(n))return o();let r=fo(e),i=this.entries.get(r);if(i){if(Date.now()/1e3<i.expiresAt)return i.enrichment;this.entries.delete(r);}let a=this.inFlight.get(r);if(a)return a;let s=o().then(l=>(this.store(r,l,n),this.inFlight.delete(r),l),l=>{throw this.inFlight.delete(r),l});return this.inFlight.set(r,s),s}clear(){this.entries.clear(),this.inFlight.clear();}store(e,n,o){for(this.entries.has(e)&&this.entries.delete(e),this.entries.set(e,{enrichment:n,expiresAt:o});this.entries.size>this.maxEntries;){let r=this.entries.keys().next().value;if(r===void 0)break;this.entries.delete(r);}}};function fo(t){return crypto$1.createHash("sha256").update(t).digest("hex")}function go(t){if(!t)return;let e=t.match(/(?:^|,\s*)max-age=(\d+)/i);if(!e)return;let n=Number.parseInt(e[1],10);return Number.isFinite(n)&&n>=0?n:void 0}function ho(t,e){if(t!==true){let l=new URL(t.toString());return async()=>l}if(e===void 0)throw new TypeError('oauth: `userinfo: true` requires the verify helper to expose an `issuer`. Use `jwks({ issuer, ... })` / `jwt({ issuer, ... })`, pass an explicit `userinfo: "https://idp.example.com/oauth/userinfo"`, or use a function.');if(Array.isArray(e))throw new TypeError("oauth: `userinfo: true` requires a single-string `issuer`; got an array. OIDC Discovery resolves one issuer to one userinfo endpoint. Pass an explicit userinfo URL or function instead.");let n=e.endsWith("/")?e:`${e}/`,o=new URL(".well-known/openid-configuration",n),r=null,i=0,a=null,s=async()=>{let l;try{l=await fetch(o);}catch(p){throw routecraft.rcError("RC5021",p,{message:`OIDC Discovery fetch failed at ${o.toString()}`,suggestion:"Verify network access to the IdP and that the issuer URL is reachable. Pass an explicit `userinfo` URL or function if the IdP does not advertise discovery."})}if(!l.ok)throw routecraft.rcError("RC5021",new Error(`OIDC Discovery fetch returned ${l.status} ${l.statusText}`),{message:`OIDC Discovery fetch failed at ${o.toString()} (status ${l.status})`,suggestion:"Verify the issuer hosts an OIDC Discovery document. Pass an explicit `userinfo` URL or function if it does not."});let d;try{d=await l.json();}catch(p){throw routecraft.rcError("RC5021",p,{message:`OIDC Discovery document at ${o.toString()} is not valid JSON`,suggestion:"Inspect the discovery endpoint manually. Pass an explicit `userinfo` URL or function if the IdP is non-compliant."})}if(typeof d.userinfo_endpoint!="string"||!d.userinfo_endpoint)throw routecraft.rcError("RC5021",new Error("missing userinfo_endpoint in discovery document"),{message:`OIDC Discovery document at ${o.toString()} does not advertise a userinfo_endpoint`,suggestion:"Pass an explicit `userinfo` URL or function for this IdP; it does not advertise auto-discoverable userinfo."});let c=new URL(d.userinfo_endpoint),u=go(l.headers.get("cache-control"))??mo;return r=c,i=Date.now()/1e3+u,c};return async()=>r&&Date.now()/1e3<i?r:a||(a=s().finally(()=>{a=null;}),a)}async function yo(t,e){let n;try{n=await fetch(t,{headers:{Authorization:`Bearer ${e}`}});}catch(o){throw routecraft.rcError("RC5021",o,{message:`userinfo fetch failed at ${t.toString()}`,suggestion:"Verify network access to the IdP's userinfo endpoint and that the bearer token's scopes permit it."})}if(!n.ok)throw routecraft.rcError("RC5021",new Error(`userinfo returned ${n.status} ${n.statusText}`),{message:`userinfo fetch failed at ${t.toString()} (status ${n.status})`,suggestion:"Check the bearer token has the scopes required by the IdP's userinfo endpoint (typically `openid`, `email`, `profile`)."});try{return await n.json()}catch(o){throw routecraft.rcError("RC5021",o,{message:`userinfo response at ${t.toString()} is not valid JSON`,suggestion:"Inspect the userinfo endpoint manually; it must return a JSON object per OIDC Core \xA75.3.2."})}}function Bt(t,e){let n={...t};for(let[o,r]of Object.entries(e))uo.includes(o)||r!==void 0&&(n[o]=r);return n}function wo(t){let e={userinfoClaims:t};return typeof t.email=="string"&&(e.email=t.email),typeof t.name=="string"&&(e.name=t.name),Array.isArray(t.roles)&&(e.roles=t.roles.filter(n=>typeof n=="string")),e}function pt(t,e,n){let o=new ut;if(typeof e=="function")return async i=>{let a=await t(i),s=await o.getOrCompute(i,a.expiresAt,()=>e(a,i));return Bt(a,s)};let r=ho(e,n);return async i=>{let a=await t(i),s=await o.getOrCompute(i,a.expiresAt,async()=>{let l=await r(),d=await yo(l,i),c=d.sub;if(typeof c!="string"||c.length===0)throw routecraft.rcError("RC5022",new Error("userinfo response is missing `sub`"),{message:"userinfo response is missing `sub` (required per OIDC Core \xA75.3.2)",suggestion:"Inspect the userinfo endpoint manually; the response MUST include a `sub` claim. If the IdP returns a non-standard identifier, use a function-mode `userinfo` and map it yourself."});if(c!==a.subject)throw routecraft.rcError("RC5022",new Error(`userinfo \`sub\` (${c}) does not match token \`sub\` (${a.subject})`),{message:"userinfo response `sub` does not match the verified token's `sub` (OIDC Core \xA75.3.2 violation)",suggestion:"This indicates a compromised userinfo endpoint or a misconfigured userinfo URL. Verify the issuer / userinfo mapping; do not relax this check."});return wo(d)});return Bt(a,s)}}function mt(t){return typeof t=="object"&&t!==null&&t.code==="ERR_JWT_EXPIRED"}var Ro=new Set(["ERR_JWKS_TIMEOUT","ERR_JWKS_INVALID","ERR_JOSE_GENERIC"]),vo=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","ETIMEDOUT","UND_ERR_CONNECT_TIMEOUT","UND_ERR_HEADERS_TIMEOUT","UND_ERR_SOCKET"]);function Ft(t){let e=t;for(let n=0;n<5;n++){if(typeof e!="object"||e===null)return false;let o=e.code;if(typeof o=="string"&&(Ro.has(o)||vo.has(o)))return true;e=e.cause;}return false}var Vt=new async_hooks.AsyncLocalStorage,Y=class{context;options;server=null;transport=null;httpServer=null;httpSessions=new Map;running=false;toolsListLogged=false;validatorVerifier=null;constructor(e,n={}){this.context=e,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...n},this.validateResourceConfig();}resolveServerIcons(){return this.options.icons===void 0?Ut:this.options.icons}defaultUnlessEmpty(e,n){let o=e??n;return o===""?void 0:o}buildServerInfo(){let e={name:this.options.name,version:this.options.version};this.options.title!==void 0&&(e.title=this.options.title);let n=this.defaultUnlessEmpty(this.options.description,"Powered by Routecraft.dev");n!==void 0&&(e.description=n);let o=this.defaultUnlessEmpty(this.options.websiteUrl,"https://routecraft.dev");o!==void 0&&(e.websiteUrl=o);let r=this.resolveServerIcons();return r.length>0&&(e.icons=r),e}buildServerOptions(){let e={capabilities:{tools:{}}},n=this.defaultUnlessEmpty(this.options.instructions,"");return n!==void 0&&(e.instructions=n),e}validateResourceConfig(){let e=this.options.resource?.url;if(e===void 0)return;if(new URL(e.toString()).protocol!=="https:"&&process.env.NODE_ENV==="production")throw new TypeError("mcpPlugin: resource.url must use HTTPS in production")}async start(){if(this.running){this.context.logger.warn({},"MCP server already running");return}try{let e=this.options.transport;e==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info({name:this.options.name,version:this.options.version,transport:e},"MCP server started"),this.logExposedToolsOnce();}catch(e){let n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Failed to start MCP server";throw this.context.logger.error({err:e},n),e}}async startStdio(){let n=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/index.js'),{adapterName:"mcp (stdio)",packageName:"@modelcontextprotocol/sdk"})).Server,r=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/stdio.js'),{adapterName:"mcp (stdio)",packageName:"@modelcontextprotocol/sdk"})).StdioServerTransport;this.server=new n(this.buildServerInfo(),this.buildServerOptions()),await this.setupRequestHandlers(),this.transport=new r,await this.server.connect(this.transport);}async startHttp(){this.options.auth&&Ae(this.options.auth)?await this.startHttpWithOAuth(this.options.auth):await this.startHttpWithValidator();}async importSdkHttp(){let n=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/index.js'),{adapterName:"mcp (http)",packageName:"@modelcontextprotocol/sdk"})).Server,r=(await import('@modelcontextprotocol/sdk/server/streamableHttp.js').catch(()=>null))?.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");return {ServerCtor:n,TransportClass:r}}async createSession(e,n){let o=new e(this.buildServerInfo(),this.buildServerOptions());await this.setupRequestHandlersOn(o);let r=new n({sessionIdGenerator:()=>crypto.randomUUID(),onsessioninitialized:d=>{this.httpSessions.set(d,{server:o,transport:r}),this.context.logger.debug({sessionId:d},"MCP session created"),this.context.emit("plugin:mcp:session:created",{sessionId:d});},enableJsonResponse:true});await o.connect(r);let a=r,s=a.onclose;a.onclose=()=>{let d=r.sessionId;d&&(this.httpSessions.delete(d),this.context.logger.debug({sessionId:d},"MCP session closed"),this.context.emit("plugin:mcp:session:closed",{sessionId:d})),s?.();};let l=r.handleRequest;if(typeof l!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");return {transport:r,handleRequest:l.bind(r)}}async handleMcpRequest(e,n,o,r){let i=e.headers["mcp-session-id"],a=s=>Vt.run(o,s);try{if(i&&this.httpSessions.has(i)){let s=this.httpSessions.get(i),l=s.transport.handleRequest;await a(()=>l.call(s.transport,e,n));}else if(!i||e.method==="POST"){let{handleRequest:s}=await r();await a(()=>s(e,n));}else n.writeHead(404,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Session not found"}));}catch(s){let l=routecraft.isRoutecraftError(s)?s.meta.message:s instanceof Error?s.message:"MCP HTTP request error";this.context.logger.error({err:s},l),n.headersSent||(n.writeHead(500,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Internal Server Error"})));}}resolveResourceUrl(){let e=this.options.resource?.url;if(e!==void 0)return e.toString();let n=this.options.host,o=this.getHttpPort()??this.options.port;return `http://${n}:${o}/mcp`}resolveResourceName(){return this.options.title??this.options.name}buildProtectedResourceMetadata(){let e={resource:this.resolveResourceUrl(),bearer_methods_supported:["header"]};e.resource_name=this.resolveResourceName();let n=this.options.auth;if(n&&!("provider"in n)&&"issuer"in n){let r=n.issuer;r!==void 0&&(e.authorization_servers=Array.isArray(r)?r:[r]);}let o=this.options.resource;return o?.scopesSupported&&o.scopesSupported.length>0&&(e.scopes_supported=o.scopesSupported),o?.documentationUrl!==void 0&&(e.resource_documentation=o.documentationUrl.toString()),e}resolveResourceMetadataUrl(){return new URL(Te,this.resolveResourceUrl()).toString()}buildWwwAuthenticateHeader(){return `Bearer realm="mcp", resource_metadata="${this.resolveResourceMetadataUrl()}"`}serveProtectedResourceMetadata(e){let n=this.buildProtectedResourceMetadata();e.writeHead(200,{"Content-Type":"application/json","Cache-Control":"public, max-age=3600"}),e.end(JSON.stringify(n));}async startHttpWithValidator(){let{ServerCtor:e,TransportClass:n}=await this.importSdkHttp(),o=this.options.port,r=this.options.host,i=dt(this.options.cors);this.validatorVerifier=this.buildValidatorVerifier();let a=()=>this.createSession(e,n);this.httpServer=http.createServer(async(s,l)=>{let d=s.url?.split("?")[0]??"",c=s.headers.origin,u=Array.isArray(c)?c[0]:c,p=new URL(this.resolveResourceUrl()),{ownedPaths:m,metadataPaths:f}=ct(p);if(s.method==="OPTIONS"&&i!==null&&m.has(d)){ge(l,i,u,true),l.writeHead(204),l.end();return}if(s.method!=="OPTIONS"&&ge(l,i,u,false),f.has(d)){this.serveProtectedResourceMetadata(l);return}if(d!=="/mcp"&&d!=="/mcp/"){l.writeHead(404,{"Content-Type":"application/json"}),l.end(JSON.stringify({error:"Not Found",path:d}));return}let h;if(this.options.auth){let g=await this.validateAuth(s);if(!g){l.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":this.buildWwwAuthenticateHeader()}),l.end(JSON.stringify({error:"Unauthorized"}));return}h=g;}await this.handleMcpRequest(s,l,h,a);}),await this.listenHttp(o,r);}async startHttpWithOAuth(e){let{ServerCtor:n,TransportClass:o}=await this.importSdkHttp(),r;try{let P=await import('express');r=P.default??P;}catch{throw new Error('OAuth auth requires "express" (optional peer dependency of @routecraft/ai). Install it with: bun add express')}let a=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/router.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).mcpAuthRouter,l=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).requireBearerAuth,c=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).ProxyOAuthServerProvider,p=(await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/server/auth/errors.js'),{adapterName:"mcp (oauth)",packageName:"@modelcontextprotocol/sdk"})).InvalidTokenError,m=this.options.userinfo===void 0?e.verifyAccessToken:pt(e.verifyAccessToken,this.options.userinfo,e.issuer),f=async P=>{let C;try{C=await m(P);}catch(R){let S=mt(R),T={reason:R instanceof Error?R.message:"invalid_token",scheme:"bearer",source:"mcp",path:"oauth"};throw S?this.context.logger.debug({err:R,...T},"Auth rejected: token expired"):this.context.logger.warn({err:R,...T},"Auth rejected: token validation failed"),this.context.emit("auth:rejected",T),routecraft.isRoutecraftError(R)||Ft(R)?R:new p(S?"Token has expired":"Invalid token")}if(C.expiresAt===void 0){let R={reason:"missing_expires_at",scheme:"bearer",source:"mcp",path:"oauth"};throw this.context.logger.warn(R,"Auth rejected: OAuth principal is missing expiresAt"),this.context.emit("auth:rejected",R),new Error("oauth: verifyAccessToken must return a principal with expiresAt (required by MCP SDK bearer middleware)")}return C.clientId||this.context.logger.debug({subject:C.subject},"oauth: principal missing clientId; using subject as fallback for AuthInfo.clientId"),{token:P,clientId:C.clientId??C.subject,scopes:C.scopes??[],expiresAt:C.expiresAt,extra:{principal:C}}},h=new c({endpoints:{authorizationUrl:e.endpoints.authorizationUrl,tokenUrl:e.endpoints.tokenUrl,revocationUrl:e.endpoints.revocationUrl,registrationUrl:e.endpoints.registrationUrl},verifyAccessToken:f,getClient:e.getClient}),g=this.options.port,w=this.options.host;if(g===0&&this.options.resource?.url===void 0)throw new TypeError('mcpPlugin: OAuth-proxy mode requires either a fixed `port` or an explicit `resource.url`. With `port: 0` (ephemeral) and no `resource.url`, the protected-resource metadata URL would advertise `:0`. Pass `resource: { url: "https://..." }` or a non-zero `port`.');let v=new URL(this.resolveResourceUrl()),{ownedPaths:A,metadataPaths:K}=ct(v),W=r(),st=dt(this.options.cors);st!==null&&W.use((P,C,Re)=>{let R=P,S=C,_=R.url?.split("?")[0]??"",T=R.headers.origin,It=Array.isArray(T)?T[0]:T;if(R.method==="OPTIONS"&&A.has(_)){ge(S,st,It,true),S.writeHead(204),S.end();return}R.method!=="OPTIONS"&&ge(S,st,It,false),Re();});let Zn=(P,C)=>{this.serveProtectedResourceMetadata(C);};for(let P of K)W.get(P,Zn);let ce={provider:h,issuerUrl:v};e.baseUrl&&(ce.baseUrl=new URL(e.baseUrl.toString()));let ue=this.options.resource;ue?.scopesSupported&&ue.scopesSupported.length>0&&(ce.scopesSupported=ue.scopesSupported),ue?.documentationUrl!==void 0&&(ce.serviceDocumentationUrl=new URL(ue.documentationUrl.toString()));let xt=this.resolveResourceName();xt&&(ce.resourceName=xt),W.use(a(ce));let Lt={verifier:h,resourceMetadataUrl:this.resolveResourceMetadataUrl()};e.requiredScopes&&(Lt.requiredScopes=e.requiredScopes),W.use("/mcp",l(Lt));let Qn=()=>this.createSession(n,o);W.all("/mcp",async(P,C)=>{let Re=P,R=C,S=P.auth,_=this.authInfoToPrincipal(S);if(_){let T={subject:_.subject,scheme:_.scheme,source:"mcp"};this.context.logger.info(T,"Auth succeeded"),this.context.emit("auth:success",T);}await this.handleMcpRequest(Re,R,_,Qn);}),this.httpServer=http.createServer(W),await this.listenHttp(g,w);}async listenHttp(e,n){await new Promise((i,a)=>{this.httpServer.listen(e,n,()=>i()),this.httpServer.on("error",s=>{let l=routecraft.isRoutecraftError(s)?s.meta.message:s instanceof Error?s.message:"MCP HTTP server listen failed";this.context.logger.error({err:s},l),a(s);});});let o=this.getHttpPort()??e,r={host:n,port:o,path:"/mcp"};this.context.logger.info(r,"MCP HTTP server listening"),this.context.emit("plugin:mcp:server:listening",r);}authInfoToPrincipal(e){if(!e)return;let n=e.extra?.principal;if(n)return n;let o={kind:"oauth",scheme:"bearer",subject:e.clientId,clientId:e.clientId,scopes:e.scopes};return e.expiresAt!==void 0&&(o.expiresAt=e.expiresAt),o}getHttpPort(){let e=this.httpServer?.address();if(e&&typeof e=="object"&&"port"in e)return e.port}buildValidatorVerifier(){let e=this.options.auth;if(!e||!("validator"in e))return null;let n=o=>Promise.resolve(e.validator(o));return this.options.userinfo===void 0?n:pt(n,this.options.userinfo,e.issuer)}async validateAuth(e){let n=this.options.auth;if(!n||!("validator"in n))return null;let o=this.validatorVerifier??this.buildValidatorVerifier();if(!o)return null;let r=e.headers.authorization;if(!r||Array.isArray(r)){let s={reason:"missing_header",scheme:"bearer",source:"mcp"};return this.context.logger.debug(s,"Auth rejected: missing or malformed Authorization header"),this.context.emit("auth:rejected",s),null}let i=/^bearer\s+(.+)$/i.exec(r);if(!i){let s={reason:"unsupported_scheme",scheme:"bearer",source:"mcp"};return this.context.logger.debug(s,"Auth rejected: unsupported authorization scheme"),this.context.emit("auth:rejected",s),null}let a=i[1];try{let s=await o(a),l={subject:s.subject,scheme:s.scheme,source:"mcp"};return this.context.logger.info(l,"Auth succeeded"),this.context.emit("auth:success",l),s}catch(s){let d={reason:s instanceof Error?s.message:"invalid_token",scheme:"bearer",source:"mcp"};return mt(s)?this.context.logger.debug({err:s,...d},"Auth rejected: token expired"):this.context.logger.warn({err:s,...d},"Auth rejected: token validation failed"),this.context.emit("auth:rejected",d),null}}async setupRequestHandlers(){await this.setupRequestHandlersOn(this.server);}async setupRequestHandlersOn(e){let o=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/types.js'),{adapterName:"mcp",packageName:"@modelcontextprotocol/sdk"}),r=o.ListToolsRequestSchema,i=o.CallToolRequestSchema;if(!r||!i)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let a=e;a.setRequestHandler(r,async()=>{let s=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:s}}),a.setRequestHandler(i,async s=>{let d=s.params;return await this.handleToolCall(d.name||"",d.arguments||{})});}async stop(){if(this.running)try{if(this.httpServer){for(let[,n]of this.httpSessions){let o=n.transport;typeof o.close=="function"&&await o.close();}this.httpSessions.clear();let e=this.httpServer;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await new Promise(n=>{this.httpServer.close(()=>n());}),this.httpServer=null;}if(this.transport){let e=this.transport;typeof e.close=="function"&&await e.close();}this.running=!1,this.context.logger.info({},"MCP server stopped");}catch(e){let n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Error stopping MCP server";this.context.logger.error({err:e},n);}}logExposedToolsOnce(){if(this.toolsListLogged)return;let e=this.getAvailableTools();if(e.length===0)return;let n=e.map(r=>r.name),o={tools:n,count:n.length};this.context.logger.info(o,"Exposing MCP tools"),this.context.emit("plugin:mcp:server:tools:exposed",o),this.toolsListLogged=true;}getAvailableTools(){let e=this.context.getStore(E);if(!e)return [];let n=Array.from(e.values()),o=this.options.tools;if(o)if(Array.isArray(o)){let r=new Set(o);n=n.filter(i=>r.has(i.endpoint));}else typeof o=="function"&&(n=n.filter(o));return n.map(r=>this.entryToMcpTool(r))}entryToMcpTool(e){let n={name:e.endpoint,description:e.description,inputSchema:this.schemaToJsonSchema(e.input?.body)};e.title!==void 0&&(n.title=e.title),e.output?.body!==void 0&&(n.outputSchema=this.schemaToJsonSchema(e.output.body)),e.annotations!==void 0&&(n.annotations=e.annotations);let o=e.icons??this.resolveServerIcons();return o.length>0&&(n.icons=o),n}schemaToJsonSchema(e){if(!e||typeof e!="object")return {type:"object"};let n=e["~standard"];if(n?.jsonSchema?.input)try{let o=n.jsonSchema.input({target:"draft-2020-12"});return typeof o=="object"&&o!==null?o:{type:"object"}}catch(o){return this.context.logger.debug(o,"Standard JSON Schema conversion failed"),{type:"object"}}return "~standard"in e?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(e,n){try{let r=this.context.getStore(E)?.get(e);if(!r){let u=new Error(`Tool not found: ${e}`);return this.context.emit("plugin:mcp:tool:failed",{tool:e,error:u.message}),{isError:!0,content:[{type:"text",text:`Error: Tool not found: ${e}`}]}}let i=typeof n=="string"?(()=>{try{return JSON.parse(n)||{}}catch{return {input:n}}})():n&&typeof n=="object"?n:{};this.context.logger.debug({bodyType:typeof i,body:i},"MCP tool call exchange body");let a=Vt.getStore(),s={"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`};a&&(s[routecraft.HeadersKeys.AUTH_PRINCIPAL]=routecraft.markAuthentic(a));let l=new routecraft.DefaultExchange(this.context,{body:i,headers:s});this.context.emit("plugin:mcp:tool:called",{tool:e,args:n});let d=await r.handler(l),c=typeof d.body=="string"?d.body:JSON.stringify(d.body);return this.context.emit("plugin:mcp:tool:completed",{tool:e}),{content:[{type:"text",text:c}]}}catch(o){let r=routecraft.isRoutecraftError(o)?o.meta.message:o instanceof Error?o.message:String(o);this.context.logger.error({tool:e,err:o},r),this.context.emit("plugin:mcp:tool:failed",{tool:e,error:r});let i=r;if(routecraft.isRoutecraftError(o)){let a=o.cause;a?.message&&(i=`${r}: ${a.message}`);}return {content:[{type:"text",text:`Error: ${i}`}],isError:true}}}};function Gt(t){if(t.transport==="http"){if(t.port!==void 0){if(typeof t.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(t.port<0||t.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(t.host!==void 0&&typeof t.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}if(t.transport==="http"&&t.cors!==void 0&&t.cors!==false){let{origin:e}=t.cors;if(!(e==="*"||typeof e=="string"||Array.isArray(e)||typeof e=="function"))throw new TypeError("mcpPlugin: cors.origin must be '*', a string, a string array, or a function")}if(t.auth!==void 0)if("provider"in t.auth){if(t.auth.provider!=="oauth")throw new TypeError('mcpPlugin: auth.provider must be "oauth". Use the oauth() helper.')}else if("validator"in t.auth){if(typeof t.auth.validator!="function")throw new TypeError("mcpPlugin: auth.validator must be a function that returns a Principal (throw to reject)")}else throw new TypeError("mcpPlugin: auth must have either a 'validator' function or 'provider' set to 'oauth'. Use jwt(), jwks(), oauth(), or a custom { validator } object.");if(t.clients){for(let[e,n]of Object.entries(t.clients))if(typeof n=="object"&&n!==null&&"transport"in n&&n.transport==="stdio"&&(!n.command||typeof n.command!="string"))throw new TypeError(`mcpPlugin: stdio client "${e}" must have a non-empty command string`)}if(t.maxRestarts!==void 0&&(typeof t.maxRestarts!="number"||!Number.isInteger(t.maxRestarts)||t.maxRestarts<0))throw new TypeError("mcpPlugin: maxRestarts must be a non-negative integer");if(t.restartDelayMs!==void 0&&(typeof t.restartDelayMs!="number"||t.restartDelayMs<=0))throw new TypeError("mcpPlugin: restartDelayMs must be a positive number");if(t.restartBackoffMultiplier!==void 0&&(typeof t.restartBackoffMultiplier!="number"||t.restartBackoffMultiplier<1))throw new TypeError("mcpPlugin: restartBackoffMultiplier must be >= 1");if(t.toolRefreshIntervalMs!==void 0&&(typeof t.toolRefreshIntervalMs!="number"||!Number.isInteger(t.toolRefreshIntervalMs)||t.toolRefreshIntervalMs<0))throw new TypeError("mcpPlugin: toolRefreshIntervalMs must be a non-negative integer")}async function qt(t,e){let n=e["~standard"];if(!n?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let o=n.validate(t);if(o instanceof Promise&&(o=await o),o.issues)throw new Error(`mcpPlugin options validation failed: ${routecraft.formatSchemaIssues(o.issues)}`);if(o.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return o.value}function Me(t){let e=t?.content;if(!Array.isArray(e)||e.length===0)return t;if(e.length===1){let n=e[0];if(n.type==="text"&&typeof n.text=="string")return n.text;if(typeof n.data=="string")return n.data}return e}var Se=class{constructor(e,n,o,r){this.options=e;this.logger=n;this.onEvent=o;this.onToolsUpdated=r;}options;logger;onEvent;onToolsUpdated;client=null;transport=null;running=false;stopping=false;restartCount=0;restartTimer=null;stabilityTimer=null;tools=[];async start(){if(this.running)return;this.stopping=false,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null);let e=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (stdio client)",packageName:"@modelcontextprotocol/sdk"}),n=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/stdio.js'),{adapterName:"mcp (stdio client)",packageName:"@modelcontextprotocol/sdk"}),{serverId:o,command:r,args:i,env:a,cwd:s}=this.options;this.transport=new n.StdioClientTransport({command:r,args:i??[],...a?{env:a}:{},...s?{cwd:s}:{},stderr:"pipe"});let l=this.transport;l.onerror=d=>{this.logger.error({err:d,serverId:o},"Stdio client transport error"),this.onEvent(`plugin:mcp:client:${o}:error`,{serverId:o,error:d});},l.onclose=()=>{this.stopping||(this.running=false,this.handleDisconnect());},l.stderr?.on&&l.stderr.on("data",d=>{this.logger.debug({serverId:o,stderr:String(d).trimEnd()},"Stdio client stderr output");}),this.client=new e.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{},listChanged:{tools:{onChanged:(d,c)=>{c&&Array.isArray(c.tools)?(this.tools=c.tools,this.onToolsUpdated(o,this.tools),this.onEvent(`plugin:mcp:client:${o}:tools:listed`,{serverId:o,toolCount:this.tools.length})):this.refreshTools();}}}}),await this.client.connect(this.transport),this.running=true,await this.refreshTools(),this.logger.info({serverId:o,toolCount:this.tools.length},"Stdio client started"),this.onEvent(`plugin:mcp:client:${o}:started`,{serverId:o,toolCount:this.tools.length});}async stop(){this.stopping=true,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null),this.stabilityTimer&&(clearTimeout(this.stabilityTimer),this.stabilityTimer=null);let{serverId:e}=this.options;if(this.client){try{await this.client.close();}catch{}this.client=null;}if(this.transport){let n=this.transport;if(typeof n.close=="function")try{await Promise.resolve(n.close());}catch{}this.transport=null;}this.running=false,this.logger.info({serverId:e},"Stdio client stopped"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"graceful"});}async callTool(e,n){if(!this.running||!this.client)throw new Error(`Stdio client "${this.options.serverId}" is not running. Cannot call tool "${e}".`);let o=await this.client.callTool({name:e,arguments:n});return Me(o)}getTools(){return [...this.tools]}isRunning(){return this.running}async refreshTools(){if(!this.client)return;let{serverId:e}=this.options;try{let n=await this.client.listTools();this.tools=n.tools??[],this.onToolsUpdated(e,this.tools),this.onEvent(`plugin:mcp:client:${e}:tools:listed`,{serverId:e,toolCount:this.tools.length});}catch(n){this.logger.warn({err:n,serverId:e},"Failed to list tools for stdio client");}}handleDisconnect(){let{serverId:e,maxRestarts:n,restartDelayMs:o,restartBackoffMultiplier:r}=this.options;if(this.tools.length>0&&(this.tools=[],this.onToolsUpdated(e,[])),this.logger.warn({serverId:e},"Stdio client disconnected unexpectedly"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"unexpected"}),this.restartCount>=n){this.logger.error({serverId:e,restartCount:this.restartCount,maxRestarts:n},"Stdio client exceeded max restarts, giving up"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:new Error(`Max restarts (${n}) exceeded for stdio client "${e}"`)});return}let i=o*Math.pow(r,this.restartCount);this.logger.info({serverId:e,restartCount:this.restartCount,delayMs:i},"Scheduling stdio client restart"),this.restartTimer=setTimeout(()=>{this.restartTimer=null,this.restart();},i);}async restart(){let{serverId:e}=this.options;this.restartCount++,this.client=null,this.transport=null;try{await this.start(),this.logger.info({serverId:e,restartCount:this.restartCount},"Stdio client restarted successfully"),this.onEvent(`plugin:mcp:client:${e}:restarted`,{serverId:e,restartCount:this.restartCount});let n=this.options.restartDelayMs*2;this.stabilityTimer=setTimeout(()=>{this.stabilityTimer=null,this.running&&(this.restartCount=0);},n);}catch(n){this.logger.error({err:n,serverId:e,restartCount:this.restartCount},"Stdio client restart failed"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:n}),this.handleDisconnect();}}};function Ao(t){if(!t)return [];let e=[];return t.readOnlyHint&&e.push("read-only"),t.destructiveHint&&e.push("destructive"),t.idempotentHint&&e.push("idempotent"),t.openWorldHint&&e.push("open-world"),e}var X=class{tools=new Map;setToolsForSource(e,n,o){let r=new Map;for(let i of o){let a={name:i.name,inputSchema:i.inputSchema,source:e,transport:n};i.description!==void 0&&(a.description=i.description),i.annotations!==void 0&&(a.annotations=i.annotations);let s=Ao(i.annotations);s.length>0&&(a.tags=s),r.set(i.name,a);}this.tools.set(e,r);}removeSource(e){this.tools.delete(e);}getTools(){let e=[];for(let n of this.tools.values())for(let o of n.values())e.push(o);return e}getToolsByServer(e){let n=this.tools.get(e);return n?Array.from(n.values()):[]}getTool(e){for(let n of this.tools.values()){let o=n.get(e);if(o)return o}}getToolBySource(e,n){return this.tools.get(e)?.get(n)}};var Wt=new WeakMap;async function To(t){if(typeof t=="function")return t();if(Array.isArray(t)){if(t.length===0)throw new Error("McpClientAuthOptions.token array must not be empty");let e=(Wt.get(t)??0)%t.length;return Wt.set(t,e+1),t[e]}return t}async function Ee(t){if(!t)return;let e={},n=t.headers??{},o=Object.entries(n).find(([r])=>r.toLowerCase()==="authorization")?.[1];if(o)e.Authorization=o;else if(t.token!==void 0){let r=await To(t.token);if(r.length===0)throw new Error("McpClientAuthOptions.token must be a non-empty string when provided");e.Authorization=`Bearer ${r}`;}for(let[r,i]of Object.entries(n))r.toLowerCase()!=="authorization"&&(e[r]=i);return Object.keys(e).length>0?e:void 0}function Jt(t){return "transport"in t&&t.transport==="stdio"}function xe(t={}){Gt(t);let e=null,n=new Map,o=new Map,r=[],i=null;return {async apply(c){if(c.setStore(me,true),i=new X,c.setStore(J,i),c.setStore(fe,n),t.clients&&Object.keys(t.clients).length>0){let u=Object.entries(t.clients),p=new Map;for(let[m,f]of u)p.set(m,f);c.setStore(M,p);for(let[m,f]of u){if(Jt(f))await a(c,m,f,i);else {let g=f;await l(c,m,g.url,i,g.auth),d(c,m,g.url,i,g.auth);}let h=Jt(f)?"stdio":"http";c.emit(`plugin:mcp:client:${m}:registered`,{serverId:m,transport:h});}}e=new Y(c,t),await e.start();},async teardown(c){for(let u of r)clearInterval(u);r.length=0;for(let[u,p]of o)try{await p.close();}catch(m){c.logger.error({err:m,serverId:u,operation:"close"},"Failed to close HTTP client");}o.clear();for(let[u,p]of n)try{await p.stop();}catch(m){c.logger.error({err:m,serverId:u,operation:"stop"},"Failed to stop stdio client");}if(n.clear(),e){try{await e.stop();}catch(u){c.logger.error({err:u,operation:"stop"},"Failed to stop MCP server plugin");}e=null;}i=null;}};async function a(c,u,p,m){let f={serverId:u,command:p.command,args:p.args??[],maxRestarts:t.maxRestarts??5,restartDelayMs:t.restartDelayMs??1e3,restartBackoffMultiplier:t.restartBackoffMultiplier??2};p.env!==void 0&&(f.env=p.env),p.cwd!==void 0&&(f.cwd=p.cwd);let h=new Se(f,c.logger,(g,w)=>{c.emit(g,w);},(g,w)=>{m.setToolsForSource(g,"stdio",w);});n.set(u,h);try{await h.start();}catch(g){c.logger.error({err:g,serverId:u,operation:"start"},"Failed to start stdio client"),c.emit(`plugin:mcp:client:${u}:error`,{serverId:u,error:g});}}async function s(c,u,p){let m=o.get(c);if(m)return m;let{Client:f}=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),{StreamableHTTPClientTransport:h}=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/streamableHttp.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),g=await Ee(p),w=g?{requestInit:{headers:g}}:void 0,v=new h(new URL(u),w),A=new f({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}});await A.connect(v);let K=A;return o.set(c,K),K}async function l(c,u,p,m,f){try{let w=(await(await s(u,p,f)).listTools()).tools??[];m.setToolsForSource(u,"http",w),c.emit(`plugin:mcp:client:${u}:tools:listed`,{serverId:u,toolCount:w.length});}catch(h){o.delete(u),c.logger.warn({err:h,serverId:u,url:p,operation:"listTools"},"Failed to list tools from HTTP client");}}function d(c,u,p,m,f){let h=t.toolRefreshIntervalMs??6e4;if(h<=0)return;let g=setInterval(()=>{l(c,u,p,m,f);},h);r.push(g);}}var Le=new Map;async function Ie(){let t=[...Le.entries()];Le.clear(),t.length!==0&&await Promise.all(t.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function So(t){return t.includes("/")?t:`Xenova/${t}`}async function Eo(t){let e=So(t),n=Le.get(e);if(n)return n.run;let r=await(await routecraft.loadOptionalPeer(()=>import('@huggingface/transformers'),{adapterName:"Hugging Face embedding",packageName:"@huggingface/transformers"})).pipeline("feature-extraction",e,{dtype:"fp32"}),i=(s,l)=>r(s,{pooling:l?.pooling??"mean",normalize:l?.normalize??true}),a={run:i,dispose:typeof r.dispose=="function"?()=>Promise.resolve(r.dispose()):void 0};return Le.set(e,a),i}function Yt(t){if(t===null)return "null";if(typeof t!="object")return String(t);if(Array.isArray(t))return `array(${t.length})`;try{let e=JSON.stringify(t);return e.length>100?e.slice(0,100)+"...":e}catch{return "object"}}function xo(t){if(Array.isArray(t))return t;if(t instanceof Float32Array)return Array.from(t);if(t&&typeof t=="object"&&"data"in t){let e=t.data;if(Array.isArray(e))return e;if(e instanceof Float32Array)return Array.from(e);throw new Error(`Embedding output .data must be an Array or Float32Array; got ${typeof e}: ${Yt(e)}`)}throw new Error(`Embedding output must be an Array, Float32Array, or object with .data; got ${typeof t}: ${Yt(t)}`)}async function Xt(t){let{config:e,modelName:n,text:o}=t;if(e.provider==="mock"){let i=[];for(let a=0;a<8;a++)i.push((o.length+a)%100/100);return i}if(e.provider==="huggingface"){let i=await(await Eo(n))(o,{pooling:"mean",normalize:true}),a=i&&typeof i=="object"&&"data"in i?i.data:i;return xo(a)}throw e.provider==="ollama"||e.provider==="openai"?new Error(`Embedding provider "${e.provider}" is not yet implemented. Use huggingface for now.`):new Error(`Unknown embedding provider: ${e.provider}`)}var De=Symbol.for("routecraft.adapter.embedding.providers"),_e=Symbol.for("routecraft.adapter.embedding.options");function Lo(t,e){return {...e,provider:t}}function Ne(t={providers:{}}){return {apply(e){let n=new Map;for(let[o,r]of Object.entries(t.providers))r!==void 0&&n.set(o,Lo(o,r));e.setStore(De,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(_e,t.defaultOptions);},async teardown(){await Ie();}}}function Zt(t){let e=routecraft.getExchangeRoute(t);return {forward:e?e.getForward():async()=>{throw routecraft.rcError("RC5025",void 0,{message:"Block resolver: client.forward() called but the exchange has no bound route. Block resolvers can only forward when invoked through a real route dispatch."})}}}var ft="__",nn=Symbol.for("routecraft.ai.block.loader-tool"),U="_block_",j="_block_load_";function on(t){return t[nn]===true}async function rn(t,e,n){if(!t)return {systemAppend:"",loaderTools:[]};let o=_o(t);if(o.size===0)return {systemAppend:"",loaderTools:[]};let r=Zt(e),i=[],a=[];for(let[s,l]of o){if(l.mode==="inject"){let d=await an(s,l,e,n,r);i.push(`
2
2
 
3
- ## Skill: ${a.name}
3
+ ## ${s}
4
4
 
5
- ${a.content}`);}return r.join("")}function Wo(t,e,n,o){if(e===void 0||e===false)return t;let r=typeof e=="function"?e(n,o):Ko(n);return r.trim()===""?t:`${t}
5
+ ${d}`);continue}a.push($o(s,l,e,n,r));}return {systemAppend:i.join(""),loaderTools:a}}function gt(t){return typeof t.mode!="string"}var ht=/^[A-Za-z0-9_-]+$/,yt=64;function wt(t){return routecraft.rcError("RC5026",void 0,{message:`Agent block "${t}": two blocks resolve to the same name after flattening nested groups. Rename one of them.`})}function Rt(t){return routecraft.rcError("RC5026",void 0,{message:`Agent block "${t}": blocks form a cycle (a group contains itself). Block trees must be finite.`})}function _o(t){let e=new Map;return sn(t,"",e,new WeakSet),e}function sn(t,e,n,o){if(o.has(t))throw Rt(e);o.add(t);for(let[r,i]of Object.entries(t)){if(i===false)continue;let a=e?`${e}${ft}${r}`:r;if(gt(i)){sn(i,a,n,o);continue}if(n.has(a))throw wt(a);n.set(a,i);}o.delete(t);}var Qt=new WeakMap;async function an(t,e,n,o,r){if((e.lifetime??"dispatch")==="context"&&o){let a=Qt.get(o);if(a?.has(e))return a.get(e);a||(a=new Map,Qt.set(o,a));let s=en(t,e,n,o,r);return a.set(e,s),s.catch(()=>a.delete(e)),s}return en(t,e,n,o,r)}async function en(t,e,n,o,r){let{value:i}=e;if(typeof i=="string")return i;if(typeof i!="function")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${t}": "value" must be a string or a function returning a string.`});if(!o)throw routecraft.rcError("RC5025",void 0,{message:`Agent block "${t}": resolver function requires a CraftContext but the exchange has no bound context.`});let a;try{a=await Promise.resolve(i(n,o,[],r));}catch(s){throw routecraft.rcError("RC5025",s,{message:`Agent block "${t}": resolver function threw: ${s?.message??String(s)}`})}if(typeof a!="string")throw routecraft.rcError("RC5025",void 0,{message:`Agent block "${t}": resolver function must return a string (got ${typeof a}).`});return a}var tn={type:"object",properties:{},additionalProperties:false},No={"~standard":{version:1,vendor:"routecraft",validate(t){return t===null||typeof t!="object"||Array.isArray(t)||Object.keys(t).length>0?{issues:[{message:"Expected an empty object {}."}]}:{value:{}}},jsonSchema:{input:()=>tn,output:()=>tn}}};function $o(t,e,n,o,r){let i=typeof e.description=="string"&&e.description.trim()!==""?e.description:`Load the "${t}" block.`,a=async()=>an(t,e,n,o,r);return {name:`${j}${t}`,description:i,input:No,handler:a,[nn]:true,blockName:t}}function ln(t){let e=[];for(let n of t){if(!n.toolName.startsWith(j))continue;let r={blockName:n.toolName.slice(j.length),toolName:n.toolName,toolCallId:n.toolCallId};n.output!==void 0&&(r.output=n.output),n.error!==void 0&&(r.error=n.error),e.push(r);}return e}function dn(t){return t.startsWith(j)}function B(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`LLM model id must be "providerId:modelName" (e.g. ollama:lfm2.5-thinking). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function $e(t,e){if(typeof t!="string"){let a=t.modelId??"",s=t.provider==="custom"&&typeof t.model!="function";if(a.trim()===""&&!s)throw routecraft.rcError("RC5003",void 0,{message:`LLM model: inline LlmModelConfig for provider "${t.provider}" did not resolve to a model name. Either pass the model as a "providerId:modelName" string (e.g. "${t.provider}:<model>") or set "modelId" on the config.`});return {config:t,modelName:a}}if(!e)throw new Error(`LLM model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${z.description}" can be read.`);let n=e.getStore(z);if(!n)throw new Error('LLM provider not found: no providers registered. Add llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }) to your config.');let{providerId:o,modelName:r}=B(t),i=n.get(o);if(!i)throw new Error(`LLM provider "${o}" not found. Register it with llmPlugin({ providers: { "${o}": { provider, apiKey?, baseURL? } } }).`);return {config:i,modelName:r}}function F(t,e){return t===void 0||t===""?"":typeof t=="function"?t(e):t}function je(t){let e=t.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}async function Ue(t,e){switch(t.provider){case "openai":return Uo(t,e);case "anthropic":return Bo(t,e);case "gemini":return Fo(t,e);case "openrouter":return Ho(t,e);case "ollama":return Vo(t,e);case "lmstudio":return Go(t,e);case "custom":return qo(t,e);default:{let n=t;throw new Error(`LLM provider not implemented: ${n.provider}`)}}}async function Uo(t,e){let n=await routecraft.loadOptionalPeer(()=>import('@ai-sdk/openai'),{adapterName:"OpenAI LLM",packageName:"@ai-sdk/openai"}),o={apiKey:t.apiKey};return t.baseURL!==void 0&&(o.baseURL=t.baseURL),n.createOpenAI(o)(e)}async function Bo(t,e){return (await routecraft.loadOptionalPeer(()=>import('@ai-sdk/anthropic'),{adapterName:"Anthropic LLM",packageName:"@ai-sdk/anthropic"})).createAnthropic({apiKey:t.apiKey})(e)}async function Fo(t,e){return (await routecraft.loadOptionalPeer(()=>import('@ai-sdk/google'),{adapterName:"Gemini LLM",packageName:"@ai-sdk/google"})).createGoogleGenerativeAI({apiKey:t.apiKey})(e)}async function Ho(t,e){let o=(await routecraft.loadOptionalPeer(()=>import('@openrouter/ai-sdk-provider'),{adapterName:"OpenRouter LLM",packageName:"@openrouter/ai-sdk-provider"})).createOpenRouter({apiKey:t.apiKey}),r=t.modelId??e,i=o.chat(r);return N(i,"OpenRouter",r),i}async function Vo(t,e){let o=(await routecraft.loadOptionalPeer(()=>import('ollama-ai-provider-v2'),{adapterName:"Ollama LLM",packageName:"ollama-ai-provider-v2"})).createOllama({baseURL:t.baseURL??at.ollama.baseURL}),r=t.modelId??e,i=o(r);return N(i,"Ollama",r),i}async function Go(t,e){let n=await routecraft.loadOptionalPeer(()=>import('@ai-sdk/openai-compatible'),{adapterName:"LM Studio LLM",packageName:"@ai-sdk/openai-compatible"}),o={name:"lmstudio",baseURL:t.baseURL??at.lmstudio.baseURL,includeUsage:true};t.apiKey!==void 0&&(o.apiKey=t.apiKey);let r=n.createOpenAICompatible(o),i=t.modelId??e,a=r(i);return N(a,"LM Studio",i),a}function qo(t,e){let{model:n}=t,o=typeof n=="function"?n(e):n;return N(o,"Custom",e||"(custom)"),o}function cn(t){if(t===null||typeof t!="object")return null;let e=t;switch(e.type){case "text-delta":{let o=Be(e,"text")??Be(e,"textDelta");return o===void 0||o.length===0?null:{type:"text-delta",text:o}}case "reasoning-delta":{let o=Be(e,"text")??Be(e,"delta");return o===void 0||o.length===0?null:{type:"reasoning-delta",text:o}}default:return null}}function Be(t,e){let n=t[e];return typeof n=="string"?n:void 0}async function pn(t){let{config:e,modelId:n,options:o,system:r,user:i,onDelta:a}=t,s=await Ue(e,n);return Ko(s,o,r,i,Ce(t),a)}async function Ko(t,e,n,o,r,i){let{streamText:a}=await import('ai'),s=be(t,e,n,o,r),l=a(s);for await(let v of l.fullStream){let A=cn(v);if(A!==null)try{await i(A);}catch(K){routecraft.logger.warn({err:K},"agent.onDelta listener threw; ignoring and continuing stream");}}let c={text:await l.text??"",raw:l},u=await ee(l.usage);u&&(c.usage=ve(u));let p=await ee(l.reasoningText);typeof p=="string"&&p.length>0&&(c.reasoning=p);let m=await ee(l.output);m!==void 0&&(c.output=m);let f=await ee(l.finishReason);typeof f=="string"&&(c.finishReason=f);let h=await ee(l.steps),g=ke({steps:h});g.length>0&&(c.toolCalls=g),Array.isArray(h)&&(c.stepsCount=h.length);let w=await ee(l.response);return w&&Array.isArray(w.messages)&&(c.responseMessages=w.messages),c}async function ee(t){if(t!==void 0)try{return await t}catch(e){routecraft.logger.debug({err:e},"llm.streamLlm: optional accessor rejected; treating as absent");return}}async function mn(t){return pn(t)}async function Fe(t){let{config:e,modelId:n,options:o,system:r,user:i}=t,a=await Ue(e,n);return Wo(a,o,r,i,Ce(t))}async function Wo(t,e,n,o,r){let{generateText:i}=await import('ai'),a=be(t,e,n,o,r),s=await i(a),l={text:s.text??"",raw:s};s.usage&&(l.usage=ve(s.usage));let d=Dt(s);d!==void 0&&(l.output=d);let c=_t(s);c&&(l.reasoning=c);let u=s.finishReason;typeof u=="string"&&(l.finishReason=u);let p=ke(s);p.length>0&&(l.toolCalls=p);let m=s.steps;Array.isArray(m)&&(l.stepsCount=m.length);let f=s.response?.messages;return Array.isArray(f)&&(l.responseMessages=f),l}function fn(t,e,n){let o=t["~standard"];if(!o?.validate)throw new Error(`${n} must be a StandardSchemaV1 with ~standard.validate`);let r=e==="input"?o.jsonSchema?.input:o.jsonSchema?.output,i=e==="input"?o.jsonSchema?.output:o.jsonSchema?.input,a=r?.({target:"draft-2020-12"})??i?.({target:"draft-2020-12"});if(!a||typeof a!="object")throw new Error(`${n} must expose ~standard.jsonSchema.input or .output for AI SDK use`);function s(l){let d;try{d=o.validate(l);}catch(u){return {success:false,error:u instanceof Error?u:new Error(String(u))}}return d instanceof Promise?{success:false,error:new Error(`${n}: async schema validation is not supported`)}:d.issues!=null&&(Array.isArray(d.issues)?d.issues.length>0:typeof d.issues=="object"&&d.issues!==null?Object.keys(d.issues).length>0:!!d.issues)?{success:false,error:new Error(routecraft.formatSchemaIssues(d.issues))}:{success:true,value:d.value}}return ai.jsonSchema(a,{validate:s})}function gn(t){return fn(t,"input","Tool input schema")}function He(t){let e=fn(t,"output","LLM output schema");return ai.Output.object({schema:e})}async function hn(t,e,n,o,r){if(t.length===0)return Object.create(null);let{tool:i}=await import('ai'),a=Object.create(null);for(let s of t){let l=s.guard,d=s.handler,c=tr(s.name,n,r),u=on(s),p=u?s.blockName:void 0;a[s.name]=i({description:s.description,inputSchema:gn(s.input),execute:async(m,f)=>{let h={...c,abortSignal:f?.abortSignal??c.abortSignal},g=f?.toolCallId??crypto$1.randomUUID(),w=Date.now();!u&&e&&o&&e.emit(`route:${o.routeId}:agent:tool:invoked`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:g,toolName:s.name,input:m});try{l&&await l(m,h);let v=await d(m,h);return e&&o&&(u?e.emit(`route:${o.routeId}:agent:block:loaded`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:g,blockName:p,output:v,duration:Date.now()-w}):e.emit(`route:${o.routeId}:agent:tool:result`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:g,toolName:s.name,output:v,duration:Date.now()-w})),v}catch(v){throw e&&o&&(u?e.emit(`route:${o.routeId}:agent:block:error`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:g,blockName:p,error:v,duration:Date.now()-w}):e.emit(`route:${o.routeId}:agent:tool:error`,{routeId:o.routeId,exchangeId:o.exchangeId,correlationId:o.correlationId,toolCallId:g,toolName:s.name,error:v,duration:Date.now()-w})),v}}});}return a}function tr(t,e,n){return {logger:routecraft.logger.child({tool:t}),abortSignal:e,...n?{principal:nr(n)}:{}}}function nr(t){let e=routecraft.isAuthentic(t),n={...t};return n.audience&&(n.audience=[...n.audience]),n.scopes&&(n.scopes=[...n.scopes]),n.roles&&(n.roles=[...n.roles]),n.claims&&(n.claims=structuredClone(n.claims)),yn(n),e?routecraft.markAuthentic(n):n}function yn(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t)||ArrayBuffer.isView(t))return t;e.add(t);for(let n of Object.keys(t)){let o=t[n];o!==null&&typeof o=="object"&&yn(o,e);}return Object.freeze(t)}function Rn(t,e){if(e===void 0)return;let n=t.headers[routecraft.HeadersKeys.CORRELATION_ID];return {exchangeId:t.id,correlationId:typeof n=="string"?n:t.id,routeId:e}}var rr=0,ir=1024,sr=20,Ve=class{constructor(e){this.input=e;}input;async runUntilDone(e){return this.runWithValidation(e,void 0)}async runStream(e,n){return this.runWithValidation(e,n)}async runWithValidation(e,n){let{options:o,exchange:r}=this.input,i=o.validate,a=o.maxTurns??sr,s=await this.prepare(e),l=0,d=this.input.user,c,u=[];try{for(;;){let p=a-l;if(p<=0)throw routecraft.rcError("RC5003",void 0,{message:c?`agent: maxTurns (${a}) reached while "validate" was still rejecting; last validator message: "${c}"`:`agent: maxTurns (${a}) reached.`});let m=await ar(s,d,p,e,n);if(l+=m.stepsCount??1,m.toolCalls&&m.toolCalls.length>0&&u.push(...m.toolCalls),!i)return this.emitFinished(m),vt(m,u);let f=await Promise.resolve(i(vt(m,u),{exchange:r,turnsUsed:l}));if(f==null)return this.emitFinished(m),vt(m,u);if(typeof f!="string"||f.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agent: "validate" returned a non-string, non-void value (${typeof f}). Return void to accept, a non-empty string to retry.`});c=f,d=dr(this.input.user,d,m,f);}}catch(p){throw this.emitError(p),p}}emitFinished(e){let n=this.input.dispatchIdentity,o=this.input.context;if(!n||!o)return;let r=e.finishReason??"unknown";o.emit(`route:${n.routeId}:agent:finished`,{routeId:n.routeId,exchangeId:n.exchangeId,correlationId:n.correlationId,finishReason:r,...e.usage?.inputTokens!==void 0&&{inputTokens:e.usage.inputTokens},...e.usage?.outputTokens!==void 0&&{outputTokens:e.usage.outputTokens},...e.usage?.totalTokens!==void 0&&{totalTokens:e.usage.totalTokens}});}emitError(e){let n=this.input.dispatchIdentity,o=this.input.context;!n||!o||o.emit(`route:${n.routeId}:agent:error`,{routeId:n.routeId,exchangeId:n.exchangeId,correlationId:n.correlationId,error:e});}async prepare(e){let{options:n,modelConfig:o,modelName:r,tools:i,system:a,context:s,exchange:l,dispatchIdentity:d}=this.input,c=await hn(i,s,e,d,l.principal),u={modelConfig:o,modelName:r,system:a,vercelTools:c};return n.output!==void 0?{...u,output:He(n.output)}:u}};async function ar(t,e,n,o,r){let i=Object.keys(t.vercelTools).length>0?{tools:t.vercelTools,stopWhen:await lr(n)}:{},a={config:t.modelConfig,modelId:t.modelName,options:{temperature:rr,maxTokens:ir},system:t.system,user:e,abortSignal:o,...t.output!==void 0?{output:t.output}:{},...i};return r?mn({...a,onDelta:r}):Fe(a)}async function lr(t){let{stepCountIs:e}=await import('ai');return e(t)}function dr(t,e,n,o){let r=typeof e=="string"?[{role:"user",content:t}]:e,i=n.responseMessages??[];return [...r,...i,{role:"user",content:`Validator: ${o}`}]}function vt(t,e){let n={text:t.text};t.output!==void 0&&(n.output=t.output),t.reasoning!==void 0&&(n.reasoning=t.reasoning),t.usage&&(n.usage=t.usage);let o=[],r=[];for(let i of e)dn(i.toolName)?r.push(i):o.push(i);return o.length>0&&(n.toolCalls=o),r.length>0&&(n.blocksLoaded=ln(r)),n}function vn(t,e){return t.user!==void 0?F(t.user,e):je(e)}var H=Symbol.for("routecraft.adapter.agent.registry"),te=Symbol.for("routecraft.adapter.agent.default-options");var pr=H.description??"routecraft.adapter.agent.registry",G=class{constructor(e){this.binding=e;}binding;adapterId="routecraft.adapter.agent";async send(e){let n=routecraft.getExchangeContext(e),o=this.resolveOptions(n),r=mr(o,n);if(r.model===void 0)throw routecraft.rcError("RC5003",void 0,{message:'Agent: no "model" supplied and no agentPlugin({ defaultOptions: { model } }) is set on this context. Specify "model" on the agent or set a context-level default.'});let{config:i,modelName:a}=$e(r.model,n),s=hr(r,n),l=vn(r,e),d=F(r.system,e);if(d.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" resolved to an empty string. When "system" is a function, it must return a non-empty string for the incoming exchange.'});let{systemAppend:c,loaderTools:u}=await rn(r.blocks,e,n),p=gr(s,u),m=`${d}${c}`,f=yr(m,r.principal,e.principal,e),h=routecraft.getExchangeRoute(e),g=Rn(e,h?.definition.id),w=new Ve({options:r,modelConfig:i,modelName:a,tools:p,user:l,system:f,context:n,exchange:e,dispatchIdentity:g}),v=h?.signal??new AbortController().signal,A=this.binding.kind==="by-name"?this.binding.perCall?.onDelta??r.onDelta:r.onDelta;return A!==void 0?await w.runStream(v,A):await w.runUntilDone(v)}resolveOptions(e){if(this.binding.kind==="inline")return this.binding.options;if(!e)throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so the "${pr}" store can be read.`});let n=e.getStore(H);if(!n)throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" not found: no agents registered. Add agentPlugin({ agents: { "${this.binding.name}": {...} } }) to your config.`});let o=n.get(this.binding.name);if(!o){let r=Array.from(n.keys()).join(", ")||"<none>";throw routecraft.rcError("RC5004",void 0,{message:`Agent "${this.binding.name}" not found in registry. Known agents: ${r}.`})}return o}getMetadata(e){let n=e,o={};if(this.binding.kind==="by-name"&&(o.agent=this.binding.name),this.binding.kind==="inline"){let r=this.binding.options.model;typeof r=="string"&&(o.model=r);}return n.usage?.inputTokens!==void 0&&(o.inputTokens=n.usage.inputTokens),n.usage?.outputTokens!==void 0&&(o.outputTokens=n.usage.outputTokens),o}};function mr(t,e){let n=e?.getStore(te);if(!n)return t;let o={...t};return o.model===void 0&&n.model!==void 0&&(o.model=n.model),o.tools===void 0&&n.tools!==void 0&&(o.tools=n.tools),o.maxTurns===void 0&&n.maxTurns!==void 0&&(o.maxTurns=n.maxTurns),o.principal===void 0&&n.principal!==void 0&&(o.principal=n.principal),n.blocks!==void 0&&(o.blocks=fr(n.blocks,t.blocks)),o}function fr(t,e){let n=Object.create(null);if(!e){for(let[o,r]of Object.entries(t))n[o]=r;return n}for(let[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(e,o)){let i=e[o];if(i===false||i===void 0)continue;n[o]=i;}else n[o]=r;for(let[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(n,o)||r!==false&&(n[o]=r);return n}function gr(t,e){for(let n of t)if(n.name.startsWith(U))throw routecraft.rcError("RC5026",void 0,{message:`Agent tool "${n.name}": names starting with "${U}" are reserved for synthetic block tools. Rename the fn or route.`});return [...t,...e]}function hr(t,e){if(t.tools===void 0)return [];if(!e)throw routecraft.rcError("RC5003",void 0,{message:"Agent: cannot resolve tools without a CraftContext."});return t.tools.resolve(e)}function yr(t,e,n,o){if(e===void 0||e===false)return t;let r=typeof e=="function"?e(n,o):wr(n);return r.trim()===""?t:`${t}
6
6
 
7
- ${r}`}function Ko(t){if(!t)return `## Caller
7
+ ${r}`}function wr(t){if(!t)return `## Caller
8
8
 
9
- The current request is not authenticated. No verified user identity is available. Do not assume, infer, or invent the caller's name, email, or permissions.`;let e=[];t.name&&e.push(`- Name: ${qe(t.name)}`),t.email&&e.push(`- Email: ${qe(t.email)}`),e.push(`- Subject: ${qe(t.subject)}`);let n=t.roles?.map(o=>qe(o)).filter(o=>o.length>0);return n&&n.length>0&&e.push(`- Roles: ${n.join(", ")}`),`## Caller
9
+ The current request is not authenticated. No verified user identity is available. Do not assume, infer, or invent the caller's name, email, or permissions.`;let e=[];t.name&&e.push(`- Name: ${Ge(t.name)}`),t.email&&e.push(`- Email: ${Ge(t.email)}`),e.push(`- Subject: ${Ge(t.subject)}`);let n=t.roles?.map(o=>Ge(o)).filter(o=>o.length>0);return n&&n.length>0&&e.push(`- Roles: ${n.join(", ")}`),`## Caller
10
10
 
11
11
  The current request is authenticated.
12
12
  ${e.join(`
13
- `)}`}function qe(t){return t.replace(/\s*[\r\n]+\s*/g," ").trim()}var I=Symbol.for("routecraft.adapter.fn.registry");async function Be(t,e,n,o){let i=t.getStore(me)?.get(e);if(i)try{return await i.callTool(n,o)}catch(d){throw routecraft.isRoutecraftError(d)?d:routecraft.rcError("RC5003",d,{message:`mcp dispatch: stdio call to "${e}:${n}" failed.`})}let s=t.getStore(S)?.get(e);if(!s)throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" is not registered. Register it via defineConfig.mcp / mcpPlugin({ clients }).`});if(typeof s!="object"||s===null)throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" config is a string shorthand and cannot be called directly. Use a full HTTP config with a url.`});if("transport"in s&&s.transport==="stdio")throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: stdio server "${e}" is registered but its client is not running. Ensure mcpPlugin started successfully.`});let l=s;if(typeof l.url!="string"||l.url.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" has no url. Cannot dispatch over HTTP.`});return ft(l.url,n,o,l.auth)}async function ft(t,e,n,o){let r=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),i=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/streamableHttp.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),a,s;try{let l=new URL(t),d=await Me(o),c=d?{requestInit:{headers:d}}:void 0;a=new i.StreamableHTTPClientTransport(l,c),s=new r.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}}),await s.connect(a);let u=await s.callTool.call(s,{name:e,arguments:n});return Te(u)}catch(l){throw routecraft.isRoutecraftError(l)?l:routecraft.rcError("RC5003",l,{message:`mcp dispatch: failed to call tool "${e}" at "${t}".`})}finally{if(s){let l=s,d=l.close??l.disconnect;if(typeof d=="function")try{await Promise.resolve(d.call(s));}catch{}}if(a){let l=a,d=l.close??l.destroy;if(typeof d=="function")try{await Promise.resolve(d.call(a));}catch{}}}}var ge=Symbol.for("routecraft.ai.fn.deferred");function ee(t){return typeof t=="object"&&t!==null&&ge in t&&t[ge]===true}function er(t){let e={...t};return t.audience&&(e.audience=[...t.audience]),t.scopes&&(e.scopes=[...t.scopes]),t.roles&&(e.roles=[...t.roles]),t.claims&&(e.claims=structuredClone(t.claims)),routecraft.isAuthentic(t)?routecraft.markAuthentic(e):e}var ln={type:"object",properties:{},additionalProperties:false},cn={"~standard":{version:1,vendor:"routecraft",validate(t){return t===null||typeof t!="object"||Array.isArray(t)||Object.keys(t).length>0?{issues:[{message:"Expected an empty object {}."}]}:{value:{}}},jsonSchema:{input:()=>ln,output:()=>ln}}};function he(t,e){if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"directTool: routeId must be a non-empty string."});let n=tr(e?.tags,t);return {[ge]:true,kind:"direct",targetId:t,...n!==void 0?{overrideTags:n}:{},resolve(o,r){let i=nr(o,t,r),a=e?.description??i.description;if(typeof a!="string"||a.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`directTool: route "${t}" has no .description() and no override was provided (referenced as fn "${r}").`});let s=e?.input??i.input?.body;if(!s)throw routecraft.rcError("RC5003",void 0,{message:`directTool: route "${t}" has no .input(...) schema and no override was provided (referenced as fn "${r}").`});let l=n??i.tags,d=((c,m)=>or(o,m,t,c));return {description:a,input:s,...l&&l.length>0?{tags:[...l]}:{},handler:d}}}}function tr(t,e){if(t===void 0)return;if(!Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:`directTool("${e}"): override "tags" must be an array of non-empty strings.`});let n=[];for(let o of t){if(typeof o!="string"||o.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`directTool("${e}"): override "tags" must contain only non-empty strings.`});let r=o.trim();n.includes(r)||n.push(r);}return n}function nr(t,e,n){let o=t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY),r=routecraft.sanitizeEndpoint(e),i=o?.get(r);if(!i){let a=o?[...o.keys()].sort():[];throw routecraft.rcError("RC5003",void 0,{message:`directTool: unknown direct route id "${e}" (referenced as fn "${n}"). `+(a.length>0?`Known route ids: ${a.join(", ")}.`:"No direct routes are registered in this context.")})}return i}async function or(t,e,n,o){let r=routecraft.sanitizeEndpoint(n),i={};e.correlationId&&(i[routecraft.HeadersKeys.CORRELATION_ID]=e.correlationId),e.principal&&(i[routecraft.HeadersKeys.AUTH_PRINCIPAL]=er(e.principal));let a=new routecraft.DefaultExchange(t,{body:o,...Object.keys(i).length>0?{headers:i}:{}});return (await routecraft.getDirectChannel(t,r,{}).send(r,a)).body}function un(){return {description:"Returns the current UTC timestamp in ISO 8601 format.",input:cn,handler:()=>new Date().toISOString(),tags:["read-only","idempotent"]}}function pn(){return {description:"Generates a fresh random UUID v4.",input:cn,handler:()=>crypto$1.randomUUID(),tags:["read-only"]}}var We=Symbol.for("routecraft.ai.tools.selection");function oe(t){return typeof t=="object"&&t!==null&&We in t&&t[We]===true}function Ke(t){if(!Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:"tools(items): items must be an array."});return {[We]:true,resolve(e){let n=new Map;for(let r of t){if(typeof r=="string"){if(gn(r)&&!mn(e,r)){for(let a of hn(e,r,void 0))n.set(a.name,a);continue}let i=fn(e,r,void 0);n.set(i.name,i);continue}if(r===null||typeof r!="object")throw routecraft.rcError("RC5003",void 0,{message:"tools(): each item must be a string, { name, guard?, description? }, or { tagged, from?, guard? }."});if("name"in r){if(typeof r.name!="string"||r.name.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"tools(): { name } must be a non-empty string."});if(gn(r.name)&&!mn(e,r.name)){if(r.description!==void 0)throw routecraft.rcError("RC5003",void 0,{message:`tools(): { name: "${r.name}", description } is not supported for MCP tools. The MCP server is the source of truth for description and schema; do not override.`});for(let s of hn(e,r.name,r.guard))n.set(s.name,s);continue}if(r.description!==void 0&&(typeof r.description!="string"||r.description.trim()===""))throw routecraft.rcError("RC5003",void 0,{message:`tools(): { name: "${r.name}", description } must be a non-empty string when present.`});let i=fn(e,r.name,r.guard),a=r.description!==void 0?{...i,description:r.description}:i;n.set(a.name,a);}else if(!("tagged"in r))throw routecraft.rcError("RC5003",void 0,{message:'tools(): each object item must include "name" or "tagged".'})}let o=new Map(n);for(let r of t)if(typeof r=="object"&&r!==null&&"tagged"in r){let i=cr(r.tagged);if(i.length===0)continue;let a=lr(e,i,r.guard,r.from);if(a.length===0)throw routecraft.rcError("RC5003",void 0,{message:r.from?`tools(): tagged selector matched no tools (tagged: ${yn(i)}, from: "${r.from}").`:`tools(): tagged selector matched no tools (tagged: ${yn(i)}).`});for(let s of a)o.has(s.name)||o.set(s.name,s);}return [...o.values()]}}}function mn(t,e){return t.getStore(I)?.has(e)??false}function fn(t,e,n){if(typeof e!="string"||e.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"tools(): tool name must be a non-empty string."});let r=t.getStore(I)?.get(e);if(r)return rr(t,e,r,n);let i=/^Direct\((.*)\)$/.exec(e);if(i){let s=i[1].trim();if(s==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): "${e}" has an empty route id; use "Direct(<routeId>)".`});let l=`direct_${s}`,c=he(s).resolve(t,l);return ne(l,c,n)}let a=ur(t);throw routecraft.rcError("RC5003",void 0,{message:`tools(): unknown tool "${e}". `+(a.length>0?`Available: ${a.join(", ")}.`:"No fns or direct routes are registered in this context.")})}function rr(t,e,n,o){if(ee(n)){let r=n.resolve(t,e);return ne(e,r,o)}return ne(e,n,o)}function ne(t,e,n){return {name:t,description:e.description,input:e.input,...e.tags&&e.tags.length>0?{tags:e.tags}:{},...n?{guard:n}:{},handler:e.handler}}function gn(t){return t.startsWith("mcp__")?true:t.startsWith("MCP(")&&t.endsWith(")")}function ir(t){if(t.startsWith("MCP(")&&t.endsWith(")")){let e=t.slice(4,-1).trim(),n=e.indexOf(":"),o=n===-1?e:e.slice(0,n).trim(),r=n===-1?"*":e.slice(n+1).trim();if(o===""||r==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "MCP(server:tool)" or "MCP(server)"; got an empty server or tool segment.`});return {clientName:o,toolName:r}}if(t.startsWith("mcp__")){let e=t.slice(5),n=e.indexOf("__"),o=n===-1?e:e.slice(0,n),r=n===-1?"*":e.slice(n+2);if(o.trim()===""||r.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "mcp__server__tool" or "mcp__server"; got an empty server or tool segment.`});return {clientName:o,toolName:r}}throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "MCP(server:tool)", "MCP(server)", or the raw "mcp__server__tool" form.`})}function hn(t,e,n){let{clientName:o,toolName:r}=ir(e),i=t.getStore(B);if(!i)throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but no MCP_TOOL_REGISTRY is present. Install mcpPlugin (defineConfig.mcp) so external clients populate the registry.`});let a=i.getToolsByServer(o);if(a.length===0){let l=Rn(i);throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but client "${o}" has no registered tools. `+(l.length>0?`Known MCP clients: ${l.map(d=>`"${d}"`).join(", ")}.`:"No MCP clients are registered in this context.")})}if(r==="*")return a.map(l=>gt(t,l,n));let s=a.find(l=>l.name===r);if(!s){let l=a.map(d=>d.name).sort();throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but tool "${r}" is not registered under client "${o}". Known tools on "${o}": ${l.map(d=>`"${d}"`).join(", ")}.`})}return [gt(t,s,n)]}function gt(t,e,n){let o=`mcp__${e.source}__${e.name}`,r=e.description&&e.description.trim()!==""?e.description:`MCP tool "${e.name}" on client "${e.source}".`,i=sr(e.inputSchema),s={name:o,description:r,input:i,handler:async l=>{if(l==null||typeof l!="object"||Array.isArray(l))throw routecraft.rcError("RC5003",void 0,{message:`mcp tool "${o}" expects an object argument; received ${l===null?"null":Array.isArray(l)?"array":typeof l}.`});let d=l;return Be(t,e.source,e.name,d)}};return e.tags&&e.tags.length>0&&(s.tags=[...e.tags]),n&&(s.guard=n),s}function sr(t){return {"~standard":{version:1,vendor:"routecraft",validate(e){return {value:e}},jsonSchema:{input:()=>t,output:()=>t}}}}function Rn(t){let e=new Set;for(let n of t.getTools())e.add(n.source);return [...e].sort()}function ar(t){if(t===void 0)return {kind:"all"};if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'tools(): "from" must be a non-empty string when present.'});if(t.startsWith("mcp__")){let e=t.slice(5);if(e.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): from "${t}" has an empty server name; use "mcp__<server>".`});return {kind:"mcp",mcpClient:e}}throw routecraft.rcError("RC5003",void 0,{message:`tools(): from "${t}" is not a supported source filter. Use "mcp__<server>".`})}function lr(t,e,n,o){let r=new Set(e),i=ar(o),a=[],s=new Set,l=new Set;if(i.kind==="all"){let c=t.getStore(I);if(c)for(let[u,p]of c){if(ee(p)){if(p.kind!=="direct"||!(p.overrideTags??dr(t,p.targetId)).some(R=>r.has(R)))continue;let g=p.resolve(t,u);a.push(ne(u,g,n)),s.add(u),l.add(routecraft.sanitizeEndpoint(p.targetId));continue}(p.tags??[]).some(h=>r.has(h))&&(a.push(ne(u,p,n)),s.add(u));}let m=t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY);if(m)for(let[u,p]of m){if(l.has(u)||!(p.tags??[]).some(x=>r.has(x)))continue;let h=`direct_${u}`;if(s.has(h)||typeof p.description!="string"||p.description.trim()===""||!p.input?.body)continue;let R=he(u).resolve(t,h);a.push(ne(h,R,n)),s.add(h);}}let d=t.getStore(B);if(d){let c;if(i.kind==="mcp"){if(c=d.getToolsByServer(i.mcpClient),c.length===0){let m=Rn(d);throw routecraft.rcError("RC5003",void 0,{message:`tools(): from "mcp__${i.mcpClient}" has no registered tools. `+(m.length>0?`Known MCP clients: ${m.map(u=>`"${u}"`).join(", ")}.`:"No MCP clients are registered in this context.")})}}else c=d.getTools();for(let m of c){if(!(m.tags??[]).some(f=>r.has(f)))continue;let p=gt(t,m,n);s.has(p.name)||(a.push(p),s.add(p.name));}}else if(i.kind==="mcp")throw routecraft.rcError("RC5003",void 0,{message:`tools(): from "mcp__${i.mcpClient}" but no MCP_TOOL_REGISTRY is present. Install mcpPlugin (defineConfig.mcp).`});return a}function yn(t){return t.length===1?`"${t[0]}"`:`[${t.map(e=>`"${e}"`).join(", ")}]`}function dr(t,e){return t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY)?.get(routecraft.sanitizeEndpoint(e))?.tags??[]}function cr(t){return (Array.isArray(t)?t:[t]).filter(e=>typeof e=="string"&&e.trim()!=="")}function ur(t){let e=[...(t.getStore(I)??new Map).keys()],n=[...(t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY)??new Map).keys()].map(o=>`Direct(${o})`);return [...e,...n].sort()}function wt(t){if(typeof t.system=="string"){if(t.system.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" is required and must be a non-empty string (or a function that returns one).'})}else if(typeof t.system!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" must be a string or a function (exchange) => string.'});if(t.user!==void 0&&typeof t.user!="string"&&typeof t.user!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "user" must be a string or a function (exchange) => string when present.'});if(t.model!==void 0){if(typeof t.model!="string"||t.model.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "model" must be a non-empty "providerId:modelName" string when present.'});try{$(t.model);}catch{throw routecraft.rcError("RC5003",void 0,{message:`Agent: "model" string must be in "providerId:modelName" form (e.g. ollama:llama3). Got: "${t.model}"`})}}if(t.tools!==void 0&&!oe(t.tools))throw routecraft.rcError("RC5003",void 0,{message:'Agent: "tools" must be the result of tools([...]).'});if(t.principal!==void 0&&typeof t.principal!="boolean"&&typeof t.principal!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "principal" must be a boolean or a function (principal, exchange) => string when present.'});if(t.output!==void 0){if(t.output===null||typeof t.output!="object")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "output" must be a Standard Schema (Zod/Valibot/ArkType/etc.).'});if(typeof t.output["~standard"]?.validate!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "output" must be a Standard Schema (Zod/Valibot/ArkType/etc.).'})}}function ze(t,e){if(typeof t=="string"){if(t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"Agent: name must be a non-empty string."});return routecraft.tagAdapter(new F({kind:"by-name",name:t,...e?{perCall:e}:{}}),ze,e?routecraft.factoryArgs(t,e):routecraft.factoryArgs(t))}return wt(t),routecraft.tagAdapter(new F({kind:"inline",options:t}),ze,routecraft.factoryArgs(t))}function Cn(t,e){if(e===null||typeof e!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" entry must be an object with description, input, and handler.`});if(typeof e.description!="string"||e.description.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" is missing a non-empty "description".`});if(e.input===null||typeof e.input!="object"||typeof e.input["~standard"]!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "input" is required and must be a Standard Schema value (Zod/Valibot/ArkType/etc.).`});if(typeof e.input["~standard"]?.validate!="function")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "input" must be a Standard Schema with a callable validate.`});if(typeof e.handler!="function")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "handler" is required and must be a function.`});if(e.tags!==void 0){if(!Array.isArray(e.tags))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "tags" must be an array of non-empty strings.`});for(let o=0;o<e.tags.length;o++){let r=e.tags[o];if(typeof r!="string"||r.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "tags" must contain only non-empty strings.`});e.tags[o]=r.trim();}}}function pr(t,e){if(typeof e.description!="string"||e.description.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${t}" is missing a non-empty "description". Registered agents carry their own description because they are not backed by a route.`});wt(e);}function Ye(t={}){let e=t.agents??{},n=t.functions??{},o=t.skills??{},r=mr(t.defaultOptions);return {apply(i){let a=i.getStore(j),s=a??new Map;for(let[u,p]of Object.entries(e)){if(u.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"agentPlugin: agent id must be a non-empty string."});if(p===null||typeof p!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${u}" entry must be an object with description, model, and system.`});if(p.tools!==void 0&&!oe(p.tools))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${u}" "tools" must be the result of tools([...]).`});if(pr(u,p),s.has(u))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: duplicate agent id "${u}". Each agent id must be unique within a context.`});s.set(u,p);}a||i.setStore(j,s);let l=i.getStore(I),d=l??new Map;for(let[u,p]of Object.entries(n)){if(u.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"agentPlugin: fn id must be a non-empty string."});if(p===null||typeof p!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${u}" entry must be an object with description, input, and handler.`});if(ee(p)||Cn(u,p),d.has(u))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: duplicate fn id "${u}". Each fn id must be unique within a context.`});d.set(u,p);}l||i.setStore(I,d);let c=i.getStore(Z),m=c??new Map;for(let[u,p]of Object.entries(o)){if(typeof u!="string"||u.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"agentPlugin: skill name must be a non-empty string."});if(p===null||typeof p!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: skill "${u}" entry must be an object with name, description, and content.`});if(p.name!==u)throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: skill key "${u}" does not match its "name" field "${String(p.name)}". Keys and names must match so the registry has one canonical id per skill.`});if(typeof p.description!="string"||p.description.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: skill "${u}" is missing a non-empty "description".`});if(typeof p.content!="string"||p.content.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: skill "${u}" is missing non-empty "content". Skills inject their content into the agent's system prompt; an empty content would not change behaviour.`});if(m.has(u))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: duplicate skill name "${u}". Each skill name must be unique within a context.`});m.set(u,p);}if(!c&&m.size>0&&i.setStore(Z,m),r!==void 0){let u=i.getStore(X),p=fr(u,r);i.setStore(X,p);}}}}function mr(t){if(t!==void 0){if(t===null||typeof t!="object"||Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions" must be an object with optional "model" / "tools".'});if(t.model!==void 0){if(typeof t.model!="string"||t.model.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.model" must be a non-empty "providerId:modelName" string.'});try{$(t.model);}catch{throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: "defaultOptions.model" must be in "providerId:modelName" form (e.g. anthropic:claude-opus-4-7). Got: "${t.model}"`})}}if(t.tools!==void 0&&!oe(t.tools))throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.tools" must be the result of tools([...]).'});return t}}function fr(t,e){if(!t)return {...e};if(e.model!==void 0&&t.model!==void 0)throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.model" is already set on this context. A context can have only one default model.'});if(e.tools!==void 0&&t.tools!==void 0)throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.tools" is already set on this context. Combine selectors into a single tools([...]) call.'});return {...t,...e.model!==void 0?{model:e.model}:{},...e.tools!==void 0?{tools:e.tools}:{}}}routecraft.registerConfigApplier("llm",t=>ve(t));routecraft.registerConfigApplier("mcp",t=>Ae(t));routecraft.registerConfigApplier("embedding",t=>Le(t));routecraft.registerConfigApplier("agent",t=>Ye(t));var Pn={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function gr(t,e){return typeof t=="object"&&t!==null&&t[e]===true}function hr(t){return gr(t,Pn.McpAdapter)}async function wr(t,e){let n;try{n=JSON.parse(t);}catch{return}let o=e["~standard"];if(!o?.validate)return;let r=o.validate(n);if(r instanceof Promise&&(r=await r),!(r&&typeof r=="object"&&"issues"in r&&r.issues))return r&&typeof r=="object"&&"value"in r?r.value:void 0}var Rr=0,vr=1024,re=class{constructor(e,n={}){this.modelId=e;this.options=n;}modelId;adapterId="routecraft.adapter.llm";options;mergedOptions(e){let n=e.getStore(ue);return {temperature:Rr,maxTokens:vr,...n,...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:o,modelName:r}=Ie(this.modelId,n),i=this.mergedOptions(n),a=U(i.system,e),s=U(i.user,e)||De(e),l={temperature:i.temperature,maxTokens:i.maxTokens};i.topP!==void 0&&(l.topP=i.topP),i.frequencyPenalty!==void 0&&(l.frequencyPenalty=i.frequencyPenalty),i.presencePenalty!==void 0&&(l.presencePenalty=i.presencePenalty);let d=i.output!==void 0?Ge(i.output):void 0,c=await He({config:o,modelId:r,options:l,system:a,user:s,output:d});if(c.output===void 0&&c.text&&i.output!==void 0){let m=await wr(c.text,i.output);m!==void 0&&(c.output=m);}return c}getMetadata(e){let n=e,{providerId:o}=$(this.modelId),r={model:this.modelId,provider:o};return n.usage&&(n.usage.inputTokens!==void 0&&(r.inputTokens=n.usage.inputTokens),n.usage.outputTokens!==void 0&&(r.outputTokens=n.usage.outputTokens)),r}};function kn(t,e){return new re(t,e)}var ie=Symbol.for("routecraft.mcp.adapter");var Cr=/^[A-Za-z0-9_-]{1,64}$/;function Pr(t){if(!Cr.test(t))throw routecraft.rcError("RC5003",void 0,{message:`Invalid MCP tool name "${t}"`,suggestion:"MCP tool names must match /^[A-Za-z0-9_-]{1,64}$/ for client interoperability (OpenAI, Anthropic, etc.). Use alphanumerics, underscore, or hyphen in the route's .id()."})}var Ze=class{adapterId="routecraft.adapter.mcp";options;constructor(e={}){this[ie]=true,this.options=e;}async subscribe(e,n,o,r,i){if(!i?.routeId)throw routecraft.rcError("RC5003",void 0,{message:"McpSourceAdapter requires a route id from the engine (missing SourceMeta.routeId)",suggestion:"MCP source adapters take their tool name from the route id. Call .id('tool-name') on the route before .from(mcp(...))."});let a=i.routeId;Pr(a);let s=i.discovery,l=s?.description;if(typeof l!="string"||l.length===0)throw routecraft.rcError("RC5003",void 0,{message:`MCP route "${a}" requires a description`,suggestion:"Set .description('...') on the route builder before .from(mcp()); the MCP protocol requires a non-empty description for each tool."});if(e.getStore(pe)!==true)throw new Error("MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].");let c=e.getStore(O);if(c||(c=new Map,e.setStore(O,c)),c.has(a))throw routecraft.rcError("RC5003",void 0,{message:`Duplicate MCP tool endpoint "${a}": another .from(mcp(...)) route already registered this endpoint in the same context`,suggestion:"Each MCP tool endpoint must be unique within a context. Rename one of the mcp() routes to a different route id."});let u={endpoint:a,description:l,handler:async p=>n(p.body,p.headers,void 0,void 0,p.principal)};s?.title!==void 0&&(u.title=s.title),s?.input!==void 0&&(u.input=s.input),s?.output!==void 0&&(u.output=s.output),this.options.annotations!==void 0&&(u.annotations=this.options.annotations),this.options.icons!==void 0&&(u.icons=this.options.icons),o.signal.addEventListener("abort",()=>{e.getStore(O)?.delete(a);},{once:true}),!o.signal.aborted&&(c.set(a,u),r?.(),await new Promise(p=>{if(o.signal.aborted){p();return}o.signal.addEventListener("abort",()=>p(),{once:true});}));}};function Tr(t){let e=t.trim().toLowerCase();if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error(`MCP client: url must be HTTP or HTTPS. Stdio is not supported in routes; register stdio clients via mcpPlugin({ clients: { name: { command, args } } }). Got: "${t.slice(0,50)}${t.length>50?"...":""}"`)}function Sr(t,e){if(!t.serverId||!e)return;let o=e.getStore(S)?.get(t.serverId);if(!(o&&typeof o=="object"&&"transport"in o))return o}function Mr(t,e){if(t.url)return Tr(t.url),{url:t.url,auth:t.auth};if(t.serverId&&!e)throw new Error(`MCP client: serverId "${t.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${String(S)}" can be read.`);if(t.serverId&&e){let n=Sr(t,e);if(!n)throw new Error(`MCP client: serverId "${t.serverId}" not found in context store. Register it with context store key "${String(S)}".`);let o=typeof n=="string"?n:n.url,r=t.auth??(typeof n=="object"&&"auth"in n?n.auth:void 0);return {url:o,auth:r}}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var Qe=t=>typeof t.body=="object"&&t.body!==null?t.body:{input:t.body},ye=class{constructor(e){this.options=e;if(this[ie]=true,!e.url&&!e.serverId)throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.");if(e.url&&e.serverId)throw new Error("MCP client: cannot provide both url and serverId. Use either url for direct HTTP or serverId for registered servers.")}options;adapterId="routecraft.adapter.mcp";async send(e){let n=routecraft.getExchangeContext(e),o=this.options.tool??(typeof e.body=="object"&&e.body!==null&&"tool"in e.body&&typeof e.body.tool=="string"?e.body.tool:void 0);if(!o)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let i=(this.options.args??Qe)(e);if(this.options.serverId){if(!n)throw new Error(`MCP client: serverId "${this.options.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route).`);let d=Ar(n,this.options.serverId),c=await Be(n,this.options.serverId,o,i);return c&&typeof c=="object"&&(c.metadata={toolName:o,transport:d,serverId:this.options.serverId}),c}let{url:a,auth:s}=Mr(this.options,n),l=await ft(a,o,i,s);return l&&typeof l=="object"&&(l.metadata={toolName:o,url:a,transport:"http"}),l}getMetadata(e){return e&&typeof e=="object"&&"metadata"in e?e.metadata:{toolName:"unknown",transport:"unknown"}}};function Ar(t,e){let o=t.getStore(S)?.get(e);return o&&typeof o=="object"&&"transport"in o&&o.transport==="stdio"?"stdio":"http"}function we(t,e){if(typeof t=="object"&&t!==null&&("url"in t||"serverId"in t)){let n=t;if(typeof n.auth?.token=="string"&&n.auth.token.trim().length===0)throw new TypeError("mcp(): auth.token must be a non-empty string when provided");if(Array.isArray(n.auth?.token)){if(n.auth.token.length===0)throw new TypeError("mcp(): auth.token array must not be empty");for(let o=0;o<n.auth.token.length;o++){let r=n.auth.token[o];if(typeof r!="string"||r.trim().length===0)throw new TypeError(`mcp(): auth.token[${o}] must be a non-empty string`)}}return routecraft.tagAdapter(new ye(n),we,routecraft.factoryArgs(t,e))}if(typeof t=="string"&&t.includes(":")){let n=t.indexOf(":"),o=t.slice(0,n),r=t.slice(n+1),i={serverId:o,tool:r};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(i.args=e.args),routecraft.tagAdapter(new ye(i),we,routecraft.factoryArgs(t,e))}if(typeof t=="string")throw routecraft.rcError("RC5003",void 0,{message:`mcp(${JSON.stringify(t)}) is not a valid call: a bare string without ":" is neither a client shorthand nor source options`,suggestion:"Use .from(mcp()) for a source (tool name comes from .id()), .to(mcp({ url, tool })) for a remote server, .to(mcp('server:tool')) for a registered shorthand, or direct('name') for in-process dispatch."});return routecraft.tagAdapter(new Ze(t??{}),we,routecraft.factoryArgs(t,e))}function br(t){if(typeof t=="function")return async n=>t(n);let e=t;return async n=>n===e.client_id?e:void 0}function Er(t){if(!t)throw new TypeError("oauth: `verify` is required. Pass jwks(...), jwt(...), or a custom (token) => OAuthPrincipal function.");return typeof t=="function"?async e=>t(e):async e=>t.validator(e)}function Tn(t){if(!t.verify)throw new TypeError("oauth: `verify` is required. Pass jwks(...), jwt(...), or a custom (token) => OAuthPrincipal function.");let e=Er(t.verify),n=br(t.client),o=typeof t.verify=="function"?void 0:t.verify.issuer;return {provider:"oauth",endpoints:t.endpoints,verifyAccessToken:e,getClient:n,...o!==void 0&&{issuer:o},...t.baseUrl!==void 0&&{baseUrl:t.baseUrl},...t.requiredScopes!==void 0&&{requiredScopes:t.requiredScopes}}}var et;async function Ir(){if(et)return et;try{return et=(await import('yaml')).parse,et}catch(t){throw t instanceof Error&&(t.message.includes("ERR_MODULE_NOT_FOUND")||t.message.includes("Cannot find module")||t.message.includes("Cannot find package"))&&t.message.includes("yaml")?new Error('The markdown loaders (agents()/skills()) require the "yaml" package. Install it with: bun add yaml'):t}}async function kt(t,e){let n=path.basename(e,path.extname(e));if(!t.startsWith("---"))return {path:e,filename:n,frontmatter:{},body:t.trim()};let o=t.indexOf(`
14
- ---`,3);if(o===-1)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": opened a YAML front-matter block with "---" but never closed it. Add a closing "---" line before the body.`});let r=t.slice(3,o).trim(),i=t.slice(o+4).trim(),a;try{r?a=(await Ir())(r):a={};}catch(s){throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": YAML front matter failed to parse. ${s.message}`})}if(a!==null&&typeof a!="object")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": YAML front matter must parse to an object (got ${typeof a}).`});return {path:e,filename:n,frontmatter:a??{},body:i}}async function tt(t){let e=path.resolve(process.cwd(),t),n;try{n=fs.readFileSync(e,"utf-8");}catch(o){throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${t}" could not be read: ${o.message}`})}return kt(n,e)}async function nt(t,e={}){let n=path.resolve(process.cwd(),t),o;try{o=fs.statSync(n);}catch(i){throw routecraft.rcError("RC5003",void 0,{message:`Markdown directory "${t}" could not be opened: ${i.message}`})}if(!o.isDirectory())throw routecraft.rcError("RC5003",void 0,{message:`Markdown directory "${t}" is not a directory. Pass a path to a directory containing .md files.`});let r=[];for(let i of fs.readdirSync(n,{withFileTypes:true})){if(i.isFile()){if(path.extname(i.name).toLowerCase()!==".md")continue;r.push(await kt(fs.readFileSync(path.join(n,i.name),"utf-8"),path.join(n,i.name)));continue}if(i.isDirectory()&&e.sentinelFilename){let a=path.join(n,i.name,e.sentinelFilename),s;try{s=fs.statSync(a);}catch{continue}if(!s.isFile())continue;let l=await kt(fs.readFileSync(a,"utf-8"),a);l.filename=i.name,r.push(l);}}return r.sort((i,a)=>i.filename.localeCompare(a.filename)),r}function se(t,e,n){if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a non-empty string.`});return t}function Tt(t,e,n){if(t!==void 0){if(!Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be an array of strings.`});for(let o of t)if(typeof o!="string"||o.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must contain only non-empty strings.`});return t}}function On(t,e,n){if(t!==void 0){if(typeof t!="boolean")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a boolean (true or false).`});return t}}function bn(t,e,n){if(t!==void 0){if(typeof t!="number"||!Number.isFinite(t)||!Number.isInteger(t)||t<1)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a positive integer.`});return t}}var En=new Set(["name","description","model","maxTurns","tools","skills","principal"]);function Dr(t,e,n,o){for(let u of Object.keys(e))if(!En.has(u))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter field "${u}" is not yet supported. Currently supported fields: ${[...En].sort().join(", ")}.`});let r=se(e.name,"name",o);if(r!==t)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "name" ("${r}") must match the filename ("${t}"). Rename one or the other.`});let i=se(e.description,"description",o);if(n.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": agent body is empty. The body becomes the agent's system prompt; an empty system prompt is rejected at dispatch.`});let a=e.model;if(a!==void 0&&typeof a!="string")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "model" must be a string of the form "provider:model" (e.g. "anthropic:claude-sonnet-4-6").`});if(typeof a=="string"&&!a.includes(":"))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "model" ("${a}") must use the full "provider:model" form. Bare model aliases like "sonnet" / "opus" / "haiku" are not yet supported by the markdown loader.`});let s=bn(e.maxTurns,"maxTurns",o),l=Tt(e.tools,"tools",o),d=Tt(e.skills,"skills",o),c=On(e.principal,"principal",o),m={description:i,system:n};return a&&(m.model=a),s!==void 0&&(m.maxTurns=s),l!==void 0&&(m.tools=Ke(l)),d!==void 0&&(m.skills=d),c!==void 0&&(m.principal=c),{name:r,agent:m}}function _r(t,e){if(!e)return t;let n={...t};return e.description!==void 0&&(n.description=e.description),e.model!==void 0&&(n.model=e.model),e.maxTurns!==void 0&&(n.maxTurns=e.maxTurns),e.tools!==void 0&&(n.tools=e.tools),e.skills!==void 0&&(n.skills=e.skills),e.principal!==void 0&&(n.principal=e.principal),e.system!==void 0&&(n.system=e.system),n}async function xn(t,e={}){let n={},o=t.endsWith(".md")?[await tt(t)]:await nt(t);for(let r of o){let{name:i,agent:a}=Dr(r.filename,r.frontmatter,r.body,r.path);n[i]=_r(a,e[i]);}for(let r of Object.keys(e))if(!(r in n))throw routecraft.rcError("RC5003",void 0,{message:`agents("${t}"): override for "${r}" but no agent with that name was loaded from disk.`});return n}var ot=class extends Error{name="SuspendError";reason;resumeChannel;constructor(e){super(e?.reason?`Agent suspended: ${e.reason}`:"Agent suspended pending external resumption."),e?.reason!==void 0&&(this.reason=e.reason),e?.resumeChannel!==void 0&&(this.resumeChannel=e.resumeChannel);}};function Nr(t,e,n,o){let r=se(e.name,"name",o);if(r!==t)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "name" ("${r}") must match the filename ("${t}"). Rename one or the other.`});let i=se(e.description,"description",o);if(n.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": skill body is empty. The body becomes the injected skill content; an empty body would not change the agent's behaviour.`});return {name:r,description:i,content:n}}async function Ln(t,e={}){let n={},o=new Map,r=t.endsWith(".md")?[await tt(t)]:await nt(t,{sentinelFilename:"SKILL.md"});for(let i of r){let a=Nr(i.filename,i.frontmatter,i.body,i.path),s=o.get(a.name);if(s)throw routecraft.rcError("RC5003",void 0,{message:`skills("${t}"): duplicate skill name "${a.name}" loaded from both "${s}" and "${i.path}". Each skill name must be unique within a directory; rename or remove one source.`});o.set(a.name,i.path);let l=e[a.name];n[a.name]=l?{...a,...l}:a;}for(let i of Object.keys(e))if(!(i in n))throw routecraft.rcError("RC5003",void 0,{message:`skills("${t}"): override for "${i}" but no skill with that name was loaded from disk.`});return n}function Ur(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`Embedding adapter: model id must be "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function jr(t,e){if(!e)throw new Error(`Embedding adapter: model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so embedding providers can be read.`);let n=e.getStore(Ee);if(!n)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:o,modelName:r}=Ur(t),i=n.get(o);if(!i)throw new Error(`Embedding provider "${o}" not found. Register it with embeddingPlugin({ providers: { "${o}": {} } }).`);return {config:i,modelName:r}}function Fr(t){return e=>{let n=t(e);return Array.isArray(n)?n.filter(Boolean).join(" | "):n}}var le=class{constructor(e,n){this.modelId=e;this.options=n;}modelId;adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore(xe),...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:o,modelName:r}=jr(this.modelId,n),i=this.mergedOptions(n),s=Fr(i.using)(e);return {embedding:await Vt({config:o,modelName:r,text:s})}}};function In(t,e){return new le(t,e)}Object.defineProperty(exports,"jwks",{enumerable:true,get:function(){return routecraft.jwks}});Object.defineProperty(exports,"jwt",{enumerable:true,get:function(){return routecraft.jwt}});exports.AgentDestinationAdapter=F;exports.BRAND=Pn;exports.EmbeddingDestinationAdapter=le;exports.LlmDestinationAdapter=re;exports.MCP_LOCAL_TOOL_REGISTRY=O;exports.McpHeadersKeys=Ce;exports.McpServer=W;exports.McpToolRegistry=K;exports.SuspendError=ot;exports.agent=ze;exports.agentPlugin=Ye;exports.agents=xn;exports.currentTime=un;exports.defaultArgs=Qe;exports.directTool=he;exports.disposeEmbeddingPipelineCache=be;exports.embedding=In;exports.embeddingPlugin=Le;exports.isMcpAdapter=hr;exports.isOAuthAuth=Pe;exports.llm=kn;exports.llmPlugin=ve;exports.mcp=we;exports.mcpPlugin=Ae;exports.oauth=Tn;exports.randomUuid=pn;exports.skills=Ln;exports.tools=Ke;exports.validateWithSchema=$t;//# sourceMappingURL=index.cjs.map
13
+ `)}`}function Ge(t){return t.replace(/\s*[\r\n]+\s*/g," ").trim()}var D=Symbol.for("routecraft.adapter.fn.registry");async function qe(t,e,n,o){let i=t.getStore(fe)?.get(e);if(i)try{return await i.callTool(n,o)}catch(d){throw routecraft.isRoutecraftError(d)?d:routecraft.rcError("RC5003",d,{message:`mcp dispatch: stdio call to "${e}:${n}" failed.`})}let s=t.getStore(M)?.get(e);if(!s)throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" is not registered. Register it via defineConfig.mcp / mcpPlugin({ clients }).`});if(typeof s!="object"||s===null)throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" config is a string shorthand and cannot be called directly. Use a full HTTP config with a url.`});if("transport"in s&&s.transport==="stdio")throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: stdio server "${e}" is registered but its client is not running. Ensure mcpPlugin started successfully.`});let l=s;if(typeof l.url!="string"||l.url.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`mcp dispatch: server "${e}" has no url. Cannot dispatch over HTTP.`});return kt(l.url,n,o,l.auth)}async function kt(t,e,n,o){let r=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/index.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),i=await routecraft.loadOptionalPeer(()=>import('@modelcontextprotocol/sdk/client/streamableHttp.js'),{adapterName:"mcp (http client)",packageName:"@modelcontextprotocol/sdk"}),a,s;try{let l=new URL(t),d=await Ee(o),c=d?{requestInit:{headers:d}}:void 0;a=new i.StreamableHTTPClientTransport(l,c),s=new r.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}}),await s.connect(a);let p=await s.callTool.call(s,{name:e,arguments:n});return Me(p)}catch(l){throw routecraft.isRoutecraftError(l)?l:routecraft.rcError("RC5003",l,{message:`mcp dispatch: failed to call tool "${e}" at "${t}".`})}finally{if(s){let l=s,d=l.close??l.disconnect;if(typeof d=="function")try{await Promise.resolve(d.call(s));}catch{}}if(a){let l=a,d=l.close??l.destroy;if(typeof d=="function")try{await Promise.resolve(d.call(a));}catch{}}}}var he=Symbol.for("routecraft.ai.fn.deferred");function oe(t){return typeof t=="object"&&t!==null&&he in t&&t[he]===true}function Or(t){let e={...t};return t.audience&&(e.audience=[...t.audience]),t.scopes&&(e.scopes=[...t.scopes]),t.roles&&(e.roles=[...t.roles]),t.claims&&(e.claims=structuredClone(t.claims)),routecraft.isAuthentic(t)?routecraft.markAuthentic(e):e}var Pn={type:"object",properties:{},additionalProperties:false},An={"~standard":{version:1,vendor:"routecraft",validate(t){return t===null||typeof t!="object"||Array.isArray(t)||Object.keys(t).length>0?{issues:[{message:"Expected an empty object {}."}]}:{value:{}}},jsonSchema:{input:()=>Pn,output:()=>Pn}}};function We(t,e){if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"directTool: routeId must be a non-empty string."});return {[he]:true,kind:"direct",targetId:t,resolve(n,o){let r=Ar(n,t,o),i=e?.description??r.description;if(typeof i!="string"||i.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`directTool: route "${t}" has no .description() and no override was provided (referenced as fn "${o}").`});let a=e?.input??r.input?.body;if(!a)throw routecraft.rcError("RC5003",void 0,{message:`directTool: route "${t}" has no .input(...) schema and no override was provided (referenced as fn "${o}").`});let s=r.tags,l=((d,c)=>Tr(n,c,t,d));return {description:i,input:a,...s&&s.length>0?{tags:[...s]}:{},handler:l}}}}function Ar(t,e,n){let o=t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY),r=routecraft.sanitizeEndpoint(e),i=o?.get(r);if(!i){let a=o?[...o.keys()].sort():[];throw routecraft.rcError("RC5003",void 0,{message:`directTool: unknown direct route id "${e}" (referenced as fn "${n}"). `+(a.length>0?`Known route ids: ${a.join(", ")}.`:"No direct routes are registered in this context.")})}return i}async function Tr(t,e,n,o){let r=routecraft.sanitizeEndpoint(n),i={};e.correlationId&&(i[routecraft.HeadersKeys.CORRELATION_ID]=e.correlationId),e.principal&&(i[routecraft.HeadersKeys.AUTH_PRINCIPAL]=Or(e.principal));let a=new routecraft.DefaultExchange(t,{body:o,...Object.keys(i).length>0?{headers:i}:{}});return (await routecraft.getDirectChannel(t,r,{}).send(r,a)).body}function Tn(){return {description:"Returns the current UTC timestamp in ISO 8601 format.",input:An,handler:()=>new Date().toISOString(),tags:["read-only","idempotent"]}}function Mn(){return {description:"Generates a fresh random UUID v4.",input:An,handler:()=>crypto$1.randomUUID(),tags:["read-only"]}}var ze=Symbol.for("routecraft.ai.tools.selection");function re(t){return typeof t=="object"&&t!==null&&ze in t&&t[ze]===true}function Je(t){if(typeof t!="function"&&!Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:"tools(): argument must be an array of ToolsItem or a (catalog) => ToolsItem[] builder."});return {[ze]:true,resolve(e){let n=typeof t=="function"?Sr(t,Mr(e)):t,o=new Map;for(let r of n){if(typeof r=="string"){if(xn(r)&&!Sn(e,r)){for(let l of Ln(e,r,void 0))o.set(l.name,l);continue}let s=En(e,r,void 0);o.set(s.name,s);continue}if(r===null||typeof r!="object"||!("name"in r))throw routecraft.rcError("RC5003",void 0,{message:"tools(): each item must be a string or { name, guard?, description? }."});if(typeof r.name!="string"||r.name.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"tools(): { name } must be a non-empty string."});if(xn(r.name)&&!Sn(e,r.name)){if(r.description!==void 0)throw routecraft.rcError("RC5003",void 0,{message:`tools(): { name: "${r.name}", description } is not supported for MCP tools. The MCP server is the source of truth for description and schema; do not override.`});for(let s of Ln(e,r.name,r.guard))o.set(s.name,s);continue}if(r.description!==void 0&&(typeof r.description!="string"||r.description.trim()===""))throw routecraft.rcError("RC5003",void 0,{message:`tools(): { name: "${r.name}", description } must be a non-empty string when present.`});let i=En(e,r.name,r.guard),a=r.description!==void 0?{...i,description:r.description}:i;o.set(a.name,a);}return [...o.values()]}}}function Mr(t){let e=[],n=t.getStore(D);if(n)for(let[s,l]of n)if(oe(l))e.push(Object.freeze({name:s}));else {let d=l.tags&&l.tags.length>0?Object.freeze([...l.tags]):void 0;e.push(Object.freeze({name:s,description:l.description,...d!==void 0?{tags:d}:{}}));}let o=[],r=t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY);if(r)for(let[s,l]of r){let d=l.tags&&l.tags.length>0?Object.freeze([...l.tags]):void 0,c=decodeURIComponent(s);o.push(Object.freeze({id:c,...l.description?{description:l.description}:{},...d!==void 0?{tags:d}:{}}));}let i=[],a=t.getStore(J);if(a)for(let s of a.getTools()){let l=s.tags&&s.tags.length>0?Object.freeze([...s.tags]):void 0;i.push(Object.freeze({server:s.source,tool:s.name,...s.description?{description:s.description}:{},...l!==void 0?{tags:l}:{}}));}return Object.freeze({fns:Object.freeze(e),routes:Object.freeze(o),mcp:Object.freeze(i)})}function Sr(t,e){let n;try{n=t(e);}catch(o){throw routecraft.rcError("RC5003",o,{message:`tools(builder): builder threw: ${o?.message??String(o)}`})}if(!Array.isArray(n))throw routecraft.rcError("RC5003",void 0,{message:`tools(builder): builder must return an array of ToolsItem (got ${typeof n}).`});return n}function Sn(t,e){return t.getStore(D)?.has(e)??false}function En(t,e,n){if(typeof e!="string"||e.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"tools(): tool name must be a non-empty string."});let r=t.getStore(D)?.get(e);if(r)return Er(t,e,r,n);let i=/^Direct\((.*)\)$/.exec(e);if(i){let s=i[1].trim();if(s==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): "${e}" has an empty route id; use "Direct(<routeId>)".`});let l=`direct_${s}`,c=We(s).resolve(t,l);return Ct(l,c,n)}let a=Dr(t);throw routecraft.rcError("RC5003",void 0,{message:`tools(): unknown tool "${e}". `+(a.length>0?`Available: ${a.join(", ")}.`:"No fns or direct routes are registered in this context.")})}function Er(t,e,n,o){if(oe(n)){let r=n.resolve(t,e);return Ct(e,r,o)}return Ct(e,n,o)}function Ct(t,e,n){return {name:t,description:e.description,input:e.input,...e.tags&&e.tags.length>0?{tags:e.tags}:{},...n?{guard:n}:{},handler:e.handler}}function xn(t){return t.startsWith("mcp__")?true:t.startsWith("MCP(")&&t.endsWith(")")}function xr(t){if(t.startsWith("MCP(")&&t.endsWith(")")){let e=t.slice(4,-1).trim(),n=e.indexOf(":"),o=n===-1?e:e.slice(0,n).trim(),r=n===-1?"*":e.slice(n+1).trim();if(o===""||r==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "MCP(server:tool)" or "MCP(server)"; got an empty server or tool segment.`});return {clientName:o,toolName:r}}if(t.startsWith("mcp__")){let e=t.slice(5),n=e.indexOf("__"),o=n===-1?e:e.slice(0,n),r=n===-1?"*":e.slice(n+2);if(o.trim()===""||r.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "mcp__server__tool" or "mcp__server"; got an empty server or tool segment.`});return {clientName:o,toolName:r}}throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${t}" must use "MCP(server:tool)", "MCP(server)", or the raw "mcp__server__tool" form.`})}function Ln(t,e,n){let{clientName:o,toolName:r}=xr(e),i=t.getStore(J);if(!i)throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but no MCP_TOOL_REGISTRY is present. Install mcpPlugin (defineConfig.mcp) so external clients populate the registry.`});let a=i.getToolsByServer(o);if(a.length===0){let l=Ir(i);throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but client "${o}" has no registered tools. `+(l.length>0?`Known MCP clients: ${l.map(d=>`"${d}"`).join(", ")}.`:"No MCP clients are registered in this context.")})}if(r==="*")return a.map(l=>In(t,l,n));let s=a.find(l=>l.name===r);if(!s){let l=a.map(d=>d.name).sort();throw routecraft.rcError("RC5003",void 0,{message:`tools(): MCP reference "${e}" but tool "${r}" is not registered under client "${o}". Known tools on "${o}": ${l.map(d=>`"${d}"`).join(", ")}.`})}return [In(t,s,n)]}function In(t,e,n){let o=`mcp__${e.source}__${e.name}`,r=e.description&&e.description.trim()!==""?e.description:`MCP tool "${e.name}" on client "${e.source}".`,i=Lr(e.inputSchema),s={name:o,description:r,input:i,handler:async l=>{if(l==null||typeof l!="object"||Array.isArray(l))throw routecraft.rcError("RC5003",void 0,{message:`mcp tool "${o}" expects an object argument; received ${l===null?"null":Array.isArray(l)?"array":typeof l}.`});let d=l;return qe(t,e.source,e.name,d)}};return e.tags&&e.tags.length>0&&(s.tags=[...e.tags]),n&&(s.guard=n),s}function Lr(t){return {"~standard":{version:1,vendor:"routecraft",validate(e){return {value:e}},jsonSchema:{input:()=>t,output:()=>t}}}}function Ir(t){let e=new Set;for(let n of t.getTools())e.add(n.source);return [...e].sort()}function Dr(t){let e=[...(t.getStore(D)??new Map).keys()],n=[...(t.getStore(routecraft.ADAPTER_DIRECT_REGISTRY)??new Map).keys()].map(o=>`Direct(${o})`);return [...e,...n].sort()}function Pt(t){if(typeof t.system=="string"){if(t.system.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" is required and must be a non-empty string (or a function that returns one).'})}else if(typeof t.system!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "system" must be a string or a function (exchange) => string.'});if(t.user!==void 0&&typeof t.user!="string"&&typeof t.user!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "user" must be a string or a function (exchange) => string when present.'});if(t.model!==void 0){if(typeof t.model!="string"||t.model.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "model" must be a non-empty "providerId:modelName" string when present.'});try{B(t.model);}catch{throw routecraft.rcError("RC5003",void 0,{message:`Agent: "model" string must be in "providerId:modelName" form (e.g. ollama:llama3). Got: "${t.model}"`})}}if(t.tools!==void 0&&!re(t.tools))throw routecraft.rcError("RC5003",void 0,{message:'Agent: "tools" must be the result of tools([...]).'});if(t.principal!==void 0&&typeof t.principal!="boolean"&&typeof t.principal!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "principal" must be a boolean or a function (principal, exchange) => string when present.'});if(t.output!==void 0){if(t.output===null||typeof t.output!="object")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "output" must be a Standard Schema (Zod/Valibot/ArkType/etc.).'});if(typeof t.output["~standard"]?.validate!="function")throw routecraft.rcError("RC5003",void 0,{message:'Agent: "output" must be a Standard Schema (Zod/Valibot/ArkType/etc.).'})}t.blocks!==void 0&&Ot(t.blocks);}function Ot(t,e){let n={seenNames:new Set,seenObjects:new WeakSet,...e!==void 0?{defaultsLabel:e}:{}};Nn(t,"",n);}function Nn(t,e,n){if(t===null||typeof t!="object"||Array.isArray(t))throw routecraft.rcError("RC5027",void 0,{message:'Agent: "blocks" must be a Record<string, BlockBody | Blocks | false>.'});if(n.seenObjects.has(t))throw Rt(e);n.seenObjects.add(t);let o=0;for(let[r,i]of Object.entries(t)){let a=e?`${e}${ft}${r}`:r;if(r.trim()==="")throw routecraft.rcError("RC5026",void 0,{message:"Agent block: block name must be a non-empty string."});if(r.startsWith(U)||a.startsWith(U))throw routecraft.rcError("RC5026",void 0,{message:`Agent block "${a}": names starting with "${U}" are reserved for synthetic block tools. Rename the block or a parent group.`});if(i===false){if(n.defaultsLabel!==void 0)throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: "${n.defaultsLabel}.${a}" cannot be false. "false" is the per-agent removal sentinel; defaults cannot remove themselves. Drop the entry or replace it with a BlockBody.`});continue}if(i===null||typeof i!="object")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": value must be a BlockBody object (with mode and value), a nested Blocks group, or "false" to remove a default.`});if(gt(i)){let l=i.value;if(typeof l=="string"||typeof l=="function")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": "mode" must be "inject" or "progressive" (got ${JSON.stringify(i.mode)}).`});let d=Nn(i,a,n);if(d===0)throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": a nested group must contain at least one block (got an empty group or only "false" members).`});o+=d;continue}if(n.seenNames.has(a))throw wt(a);n.seenNames.add(a),o+=1;let s=i;if(s.mode!=="inject"&&s.mode!=="progressive")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": "mode" must be "inject" or "progressive" (got ${JSON.stringify(s.mode)}).`});if(s.mode==="progressive"&&(typeof s.description!="string"||s.description.trim()===""))throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": progressive-mode blocks require a non-empty "description" so the model can decide whether to load.`});if(s.mode==="progressive"){if(!ht.test(a))throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": a progressive block becomes the loader tool "${j}${a}", so its flattened name must match ${ht.source} (letters, digits, "_", "-"). Rename the block or its parent group.`});let l=`${j}${a}`;if(l.length>yt)throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": the loader tool name "${l}" is ${l.length} characters, over the provider limit of ${yt}. Shorten the block name or its parent group.`})}if(s.lifetime!==void 0&&s.lifetime!=="dispatch"&&s.lifetime!=="context")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": "lifetime" must be "dispatch" or "context" when present (got ${JSON.stringify(s.lifetime)}).`});if(typeof s.value!="string"&&typeof s.value!="function")throw routecraft.rcError("RC5027",void 0,{message:`Agent block "${a}": "value" must be a string or a function returning a string.`})}return n.seenObjects.delete(t),o}function Ye(t,e){if(typeof t=="string"){if(t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"Agent: name must be a non-empty string."});return routecraft.tagAdapter(new G({kind:"by-name",name:t,...e?{perCall:e}:{}}),Ye,e?routecraft.factoryArgs(t,e):routecraft.factoryArgs(t))}return Pt(t),routecraft.tagAdapter(new G({kind:"inline",options:t}),Ye,routecraft.factoryArgs(t))}function $n(t,e){if(e===null||typeof e!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" entry must be an object with description, input, and handler.`});if(typeof e.description!="string"||e.description.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" is missing a non-empty "description".`});if(e.input===null||typeof e.input!="object"||typeof e.input["~standard"]!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "input" is required and must be a Standard Schema value (Zod/Valibot/ArkType/etc.).`});if(typeof e.input["~standard"]?.validate!="function")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "input" must be a Standard Schema with a callable validate.`});if(typeof e.handler!="function")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "handler" is required and must be a function.`});if(e.tags!==void 0){if(!Array.isArray(e.tags))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "tags" must be an array of non-empty strings.`});for(let o=0;o<e.tags.length;o++){let r=e.tags[o];if(typeof r!="string"||r.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${t}" "tags" must contain only non-empty strings.`});e.tags[o]=r.trim();}}}function _r(t,e){if(typeof e.description!="string"||e.description.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${t}" is missing a non-empty "description". Registered agents carry their own description because they are not backed by a route.`});Pt(e);}function Xe(t={}){let e=t.agents??{},n=t.functions??{},o=Nr(t.defaultOptions);return {apply(r){let i=r.getStore(H),a=i??new Map;for(let[d,c]of Object.entries(e)){if(d.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"agentPlugin: agent id must be a non-empty string."});if(c===null||typeof c!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${d}" entry must be an object with description, model, and system.`});if(c.tools!==void 0&&!re(c.tools))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: agent "${d}" "tools" must be the result of tools([...]).`});if(_r(d,c),a.has(d))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: duplicate agent id "${d}". Each agent id must be unique within a context.`});a.set(d,c);}i||r.setStore(H,a);let s=r.getStore(D),l=s??new Map;for(let[d,c]of Object.entries(n)){if(d.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:"agentPlugin: fn id must be a non-empty string."});if(c===null||typeof c!="object")throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: fn "${d}" entry must be an object with description, input, and handler.`});if(oe(c)||$n(d,c),l.has(d))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: duplicate fn id "${d}". Each fn id must be unique within a context.`});l.set(d,c);}if(s||r.setStore(D,l),o!==void 0){let d=r.getStore(te),c=$r(d,o);r.setStore(te,c);}}}}function Nr(t){if(t!==void 0){if(t===null||typeof t!="object"||Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions" must be an object with optional "model" / "tools".'});if(t.model!==void 0){if(typeof t.model!="string"||t.model.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.model" must be a non-empty "providerId:modelName" string.'});try{B(t.model);}catch{throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: "defaultOptions.model" must be in "providerId:modelName" form (e.g. anthropic:claude-opus-4-7). Got: "${t.model}"`})}}if(t.tools!==void 0&&!re(t.tools))throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.tools" must be the result of tools([...]).'});return t.blocks!==void 0&&Ot(t.blocks,"defaultOptions.blocks"),t}}function $r(t,e){if(!t)return {...e};if(e.model!==void 0&&t.model!==void 0)throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.model" is already set on this context. A context can have only one default model.'});if(e.tools!==void 0&&t.tools!==void 0)throw routecraft.rcError("RC5003",void 0,{message:'agentPlugin: "defaultOptions.tools" is already set on this context. Combine selectors into a single tools([...]) call.'});let n;if((t.blocks!==void 0||e.blocks!==void 0)&&(n={...t.blocks??{}},e.blocks!==void 0))for(let[o,r]of Object.entries(e.blocks)){if(Object.prototype.hasOwnProperty.call(n,o))throw routecraft.rcError("RC5003",void 0,{message:`agentPlugin: "defaultOptions.blocks" already contains "${o}" from a previous install. Each block name may be defined once across all installs.`});n[o]=r;}return {...t,...e.model!==void 0?{model:e.model}:{},...e.tools!==void 0?{tools:e.tools}:{},...n!==void 0?{blocks:n}:{}}}routecraft.registerConfigApplier("llm",t=>Pe(t));routecraft.registerConfigApplier("mcp",t=>xe(t));routecraft.registerConfigApplier("embedding",t=>Ne(t));routecraft.registerConfigApplier("agent",t=>Xe(t));var jn={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function jr(t,e){return typeof t=="object"&&t!==null&&t[e]===true}function Ur(t){return jr(t,jn.McpAdapter)}async function Fr(t,e){let n;try{n=JSON.parse(t);}catch{return}let o=e["~standard"];if(!o?.validate)return;let r=o.validate(n);if(r instanceof Promise&&(r=await r),!(r&&typeof r=="object"&&"issues"in r&&r.issues))return r&&typeof r=="object"&&"value"in r?r.value:void 0}var Hr=0,Vr=1024,ie=class{constructor(e,n={}){this.modelId=e;this.options=n;}modelId;adapterId="routecraft.adapter.llm";options;mergedOptions(e){let n=e.getStore(pe);return {temperature:Hr,maxTokens:Vr,...n,...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:o,modelName:r}=$e(this.modelId,n),i=this.mergedOptions(n),a=F(i.system,e),s=F(i.user,e)||je(e),l={temperature:i.temperature,maxTokens:i.maxTokens};i.topP!==void 0&&(l.topP=i.topP),i.frequencyPenalty!==void 0&&(l.frequencyPenalty=i.frequencyPenalty),i.presencePenalty!==void 0&&(l.presencePenalty=i.presencePenalty);let d=i.output!==void 0?He(i.output):void 0,c=await Fe({config:o,modelId:r,options:l,system:a,user:s,output:d});if(c.output===void 0&&c.text&&i.output!==void 0){let u=await Fr(c.text,i.output);u!==void 0&&(c.output=u);}return c}getMetadata(e){let n=e,{providerId:o}=B(this.modelId),r={model:this.modelId,provider:o};return n.usage&&(n.usage.inputTokens!==void 0&&(r.inputTokens=n.usage.inputTokens),n.usage.outputTokens!==void 0&&(r.outputTokens=n.usage.outputTokens)),r}};function Un(t,e){return new ie(t,e)}var se=Symbol.for("routecraft.mcp.adapter");var Gr=/^[A-Za-z0-9_-]{1,64}$/;function qr(t){if(!Gr.test(t))throw routecraft.rcError("RC5003",void 0,{message:`Invalid MCP tool name "${t}"`,suggestion:"MCP tool names must match /^[A-Za-z0-9_-]{1,64}$/ for client interoperability (OpenAI, Anthropic, etc.). Use alphanumerics, underscore, or hyphen in the route's .id()."})}var et=class{adapterId="routecraft.adapter.mcp";options;constructor(e={}){this[se]=true,this.options=e;}async subscribe(e,n,o,r,i){if(!i?.routeId)throw routecraft.rcError("RC5003",void 0,{message:"McpSourceAdapter requires a route id from the engine (missing SourceMeta.routeId)",suggestion:"MCP source adapters take their tool name from the route id. Call .id('tool-name') on the route before .from(mcp(...))."});let a=i.routeId;qr(a);let s=i.discovery,l=s?.description;if(typeof l!="string"||l.length===0)throw routecraft.rcError("RC5003",void 0,{message:`MCP route "${a}" requires a description`,suggestion:"Set .description('...') on the route builder before .from(mcp()); the MCP protocol requires a non-empty description for each tool."});if(e.getStore(me)!==true)throw new Error("MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].");let c=e.getStore(E);if(c||(c=new Map,e.setStore(E,c)),c.has(a))throw routecraft.rcError("RC5003",void 0,{message:`Duplicate MCP tool endpoint "${a}": another .from(mcp(...)) route already registered this endpoint in the same context`,suggestion:"Each MCP tool endpoint must be unique within a context. Rename one of the mcp() routes to a different route id."});let p={endpoint:a,description:l,handler:async m=>n(m.body,m.headers,void 0,void 0,m.principal)};s?.title!==void 0&&(p.title=s.title),s?.input!==void 0&&(p.input=s.input),s?.output!==void 0&&(p.output=s.output),this.options.annotations!==void 0&&(p.annotations=this.options.annotations),this.options.icons!==void 0&&(p.icons=this.options.icons),o.signal.addEventListener("abort",()=>{e.getStore(E)?.delete(a);},{once:true}),!o.signal.aborted&&(c.set(a,p),r?.(),await new Promise(m=>{if(o.signal.aborted){m();return}o.signal.addEventListener("abort",()=>m(),{once:true});}));}};function Wr(t){let e=t.trim().toLowerCase();if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error(`MCP client: url must be HTTP or HTTPS. Stdio is not supported in routes; register stdio clients via mcpPlugin({ clients: { name: { command, args } } }). Got: "${t.slice(0,50)}${t.length>50?"...":""}"`)}function zr(t,e){if(!t.serverId||!e)return;let o=e.getStore(M)?.get(t.serverId);if(!(o&&typeof o=="object"&&"transport"in o))return o}function Jr(t,e){if(t.url)return Wr(t.url),{url:t.url,auth:t.auth};if(t.serverId&&!e)throw new Error(`MCP client: serverId "${t.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${String(M)}" can be read.`);if(t.serverId&&e){let n=zr(t,e);if(!n)throw new Error(`MCP client: serverId "${t.serverId}" not found in context store. Register it with context store key "${String(M)}".`);let o=typeof n=="string"?n:n.url,r=t.auth??(typeof n=="object"&&"auth"in n?n.auth:void 0);return {url:o,auth:r}}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var tt=t=>typeof t.body=="object"&&t.body!==null?t.body:{input:t.body},ye=class{constructor(e){this.options=e;if(this[se]=true,!e.url&&!e.serverId)throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.");if(e.url&&e.serverId)throw new Error("MCP client: cannot provide both url and serverId. Use either url for direct HTTP or serverId for registered servers.")}options;adapterId="routecraft.adapter.mcp";async send(e){let n=routecraft.getExchangeContext(e),o=this.options.tool??(typeof e.body=="object"&&e.body!==null&&"tool"in e.body&&typeof e.body.tool=="string"?e.body.tool:void 0);if(!o)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let i=(this.options.args??tt)(e);if(this.options.serverId){if(!n)throw new Error(`MCP client: serverId "${this.options.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route).`);let d=Yr(n,this.options.serverId),c=await qe(n,this.options.serverId,o,i);return c&&typeof c=="object"&&(c.metadata={toolName:o,transport:d,serverId:this.options.serverId}),c}let{url:a,auth:s}=Jr(this.options,n),l=await kt(a,o,i,s);return l&&typeof l=="object"&&(l.metadata={toolName:o,url:a,transport:"http"}),l}getMetadata(e){return e&&typeof e=="object"&&"metadata"in e?e.metadata:{toolName:"unknown",transport:"unknown"}}};function Yr(t,e){let o=t.getStore(M)?.get(e);return o&&typeof o=="object"&&"transport"in o&&o.transport==="stdio"?"stdio":"http"}function we(t,e){if(typeof t=="object"&&t!==null&&("url"in t||"serverId"in t)){let n=t;if(typeof n.auth?.token=="string"&&n.auth.token.trim().length===0)throw new TypeError("mcp(): auth.token must be a non-empty string when provided");if(Array.isArray(n.auth?.token)){if(n.auth.token.length===0)throw new TypeError("mcp(): auth.token array must not be empty");for(let o=0;o<n.auth.token.length;o++){let r=n.auth.token[o];if(typeof r!="string"||r.trim().length===0)throw new TypeError(`mcp(): auth.token[${o}] must be a non-empty string`)}}return routecraft.tagAdapter(new ye(n),we,routecraft.factoryArgs(t,e))}if(typeof t=="string"&&t.includes(":")){let n=t.indexOf(":"),o=t.slice(0,n),r=t.slice(n+1),i={serverId:o,tool:r};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(i.args=e.args),routecraft.tagAdapter(new ye(i),we,routecraft.factoryArgs(t,e))}if(typeof t=="string")throw routecraft.rcError("RC5003",void 0,{message:`mcp(${JSON.stringify(t)}) is not a valid call: a bare string without ":" is neither a client shorthand nor source options`,suggestion:"Use .from(mcp()) for a source (tool name comes from .id()), .to(mcp({ url, tool })) for a remote server, .to(mcp('server:tool')) for a registered shorthand, or direct('name') for in-process dispatch."});return routecraft.tagAdapter(new et(t??{}),we,routecraft.factoryArgs(t,e))}function Zr(t){if(typeof t=="function")return async n=>t(n);let e=t;return async n=>n===e.client_id?e:void 0}function Qr(t){if(!t)throw new TypeError("oauth: `verify` is required. Pass jwks(...), jwt(...), or a custom (token) => OAuthPrincipal function.");return typeof t=="function"?async e=>t(e):async e=>t.validator(e)}function Bn(t){if(!t.verify)throw new TypeError("oauth: `verify` is required. Pass jwks(...), jwt(...), or a custom (token) => OAuthPrincipal function.");let e=Qr(t.verify),n=Zr(t.client),o=typeof t.verify=="function"?void 0:t.verify.issuer;return {provider:"oauth",endpoints:t.endpoints,verifyAccessToken:e,getClient:n,...o!==void 0&&{issuer:o},...t.baseUrl!==void 0&&{baseUrl:t.baseUrl},...t.requiredScopes!==void 0&&{requiredScopes:t.requiredScopes}}}var nt;async function ni(){if(nt)return nt;try{return nt=(await import('yaml')).parse,nt}catch(t){throw t instanceof Error&&(t.message.includes("ERR_MODULE_NOT_FOUND")||t.message.includes("Cannot find module")||t.message.includes("Cannot find package"))&&t.message.includes("yaml")?new Error('The markdown loaders (agents() / skills()) require the "yaml" package. Install it with: bun add yaml'):t}}async function Et(t,e){let n=path.basename(e,path.extname(e));if(!t.startsWith("---"))return {path:e,filename:n,frontmatter:{},body:t.trim()};let o=t.indexOf(`
14
+ ---`,3);if(o===-1)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": opened a YAML front-matter block with "---" but never closed it. Add a closing "---" line before the body.`});let r=t.slice(3,o).trim(),i=t.slice(o+4).trim(),a;try{r?a=(await ni())(r):a={};}catch(s){throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": YAML front matter failed to parse. ${s.message}`})}if(a!==null&&typeof a!="object")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${e}": YAML front matter must parse to an object (got ${typeof a}).`});return {path:e,filename:n,frontmatter:a??{},body:i}}async function ot(t){let e=path.resolve(process.cwd(),t),n;try{n=fs.readFileSync(e,"utf-8");}catch(o){throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${t}" could not be read: ${o.message}`})}return Et(n,e)}async function rt(t,e={}){let n=path.resolve(process.cwd(),t),o;try{o=fs.statSync(n);}catch(i){throw routecraft.rcError("RC5003",void 0,{message:`Markdown directory "${t}" could not be opened: ${i.message}`})}if(!o.isDirectory())throw routecraft.rcError("RC5003",void 0,{message:`Markdown directory "${t}" is not a directory. Pass a path to a directory containing .md files.`});let r=[];for(let i of fs.readdirSync(n,{withFileTypes:true})){if(i.isFile()){if(path.extname(i.name).toLowerCase()!==".md")continue;r.push(await Et(fs.readFileSync(path.join(n,i.name),"utf-8"),path.join(n,i.name)));continue}if(i.isDirectory()&&e.sentinelFilename){let a=path.join(n,i.name,e.sentinelFilename),s;try{s=fs.statSync(a);}catch{continue}if(!s.isFile())continue;let l=await Et(fs.readFileSync(a,"utf-8"),a);l.filename=i.name,r.push(l);}}return r.sort((i,a)=>i.filename.localeCompare(a.filename)),r}function ae(t,e,n){if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a non-empty string.`});return t}function Gn(t,e,n){if(t!==void 0){if(!Array.isArray(t))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be an array of strings.`});for(let o of t)if(typeof o!="string"||o.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must contain only non-empty strings.`});return t}}function qn(t,e,n){if(t!==void 0){if(typeof t!="boolean")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a boolean (true or false).`});return t}}function Kn(t,e,n){if(t!==void 0){if(typeof t!="number"||!Number.isFinite(t)||!Number.isInteger(t)||t<1)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${n}": frontmatter field "${e}" must be a positive integer.`});return t}}var Wn=new Set(["name","description","model","maxTurns","tools","principal"]);function oi(t,e,n,o){for(let u of Object.keys(e))if(!Wn.has(u))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter field "${u}" is not yet supported. Currently supported fields: ${[...Wn].sort().join(", ")}.`});let r=ae(e.name,"name",o);if(r!==t)throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "name" ("${r}") must match the filename ("${t}"). Rename one or the other.`});let i=ae(e.description,"description",o);if(n.trim()==="")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": agent body is empty. The body becomes the agent's system prompt; an empty system prompt is rejected at dispatch.`});let a=e.model;if(a!==void 0&&typeof a!="string")throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "model" must be a string of the form "provider:model" (e.g. "anthropic:claude-sonnet-4-6").`});if(typeof a=="string"&&!a.includes(":"))throw routecraft.rcError("RC5003",void 0,{message:`Markdown file "${o}": frontmatter "model" ("${a}") must use the full "provider:model" form. Bare model aliases like "sonnet" / "opus" / "haiku" are not yet supported by the markdown loader.`});let s=Kn(e.maxTurns,"maxTurns",o),l=Gn(e.tools,"tools",o),d=qn(e.principal,"principal",o),c={description:i,system:n};return a&&(c.model=a),s!==void 0&&(c.maxTurns=s),l!==void 0&&(c.tools=Je(l)),d!==void 0&&(c.principal=d),{name:r,agent:c}}function ri(t,e){if(!e)return t;let n={...t};return e.description!==void 0&&(n.description=e.description),e.model!==void 0&&(n.model=e.model),e.maxTurns!==void 0&&(n.maxTurns=e.maxTurns),e.tools!==void 0&&(n.tools=e.tools),e.principal!==void 0&&(n.principal=e.principal),e.blocks!==void 0&&(n.blocks=e.blocks),e.system!==void 0&&(n.system=e.system),n}async function zn(t,e={}){let n=Object.create(null),o=t.endsWith(".md")?[await ot(t)]:await rt(t);for(let r of o){let{name:i,agent:a}=oi(r.filename,r.frontmatter,r.body,r.path);n[i]=ri(a,e[i]);}for(let r of Object.keys(e))if(!Object.prototype.hasOwnProperty.call(n,r))throw routecraft.rcError("RC5003",void 0,{message:`agents("${t}"): override for "${r}" but no agent with that name was loaded from disk.`});return n}var it=class extends Error{name="SuspendError";reason;resumeChannel;constructor(e){super(e?.reason?`Agent suspended: ${e.reason}`:"Agent suspended pending external resumption."),e?.reason!==void 0&&(this.reason=e.reason),e?.resumeChannel!==void 0&&(this.resumeChannel=e.resumeChannel);}};async function Jn(t){if(!t||typeof t!="object")throw routecraft.rcError("RC5027",void 0,{message:"skills: options must be an object with at least { source }."});let{source:e,mode:n="progressive",lifetime:o}=t;if(typeof e!="string"||e.trim()==="")throw routecraft.rcError("RC5027",void 0,{message:'skills: "source" must be a non-empty path to a markdown file or directory.'});if(n!=="inject"&&n!=="progressive")throw routecraft.rcError("RC5027",void 0,{message:`skills: "mode" must be "inject" or "progressive" (got ${JSON.stringify(n)}).`});if(o!==void 0&&o!=="dispatch"&&o!=="context")throw routecraft.rcError("RC5027",void 0,{message:`skills: "lifetime" must be "dispatch" or "context" when present (got ${JSON.stringify(o)}).`});let r=e.endsWith(".md")?[await ot(e)]:await rt(e,{sentinelFilename:"SKILL.md"}),i=new Map,a={};for(let s of r){let l=ae(s.frontmatter.name,"name",s.path);if(l!==s.filename)throw routecraft.rcError("RC5027",void 0,{message:`skills: markdown file "${s.path}": frontmatter "name" ("${l}") must match the filename ("${s.filename}"). Rename one or the other.`});let d=ae(s.frontmatter.description,"description",s.path);if(s.body.trim()==="")throw routecraft.rcError("RC5027",void 0,{message:`skills: markdown file "${s.path}": skill body is empty. The body becomes the block's content; an empty body would not change the agent's behaviour.`});let c=i.get(l);if(c)throw routecraft.rcError("RC5026",void 0,{message:`skills("${e}"): duplicate skill name "${l}" loaded from both "${c}" and "${s.path}". Each skill name must be unique within a source; rename or remove one.`});i.set(l,s.path);let u={description:d,mode:n,value:s.body};o!==void 0&&(u.lifetime=o),a[l]=u;}return a}function Yn(t){if(typeof t!="string"||t.trim()==="")throw routecraft.rcError("RC5027",void 0,{message:'fromFile: "path" must be a non-empty string.'});return async()=>{let e=path.resolve(process.cwd(),t);try{return await promises.readFile(e,"utf-8")}catch(n){throw routecraft.rcError("RC5025",n,{message:`fromFile("${t}"): could not read file: ${n?.message??String(n)}`})}}}function li(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`Embedding adapter: model id must be "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function di(t,e){if(!e)throw new Error(`Embedding adapter: model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so embedding providers can be read.`);let n=e.getStore(De);if(!n)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:o,modelName:r}=li(t),i=n.get(o);if(!i)throw new Error(`Embedding provider "${o}" not found. Register it with embeddingPlugin({ providers: { "${o}": {} } }).`);return {config:i,modelName:r}}function ci(t){return e=>{let n=t(e);return Array.isArray(n)?n.filter(Boolean).join(" | "):n}}var de=class{constructor(e,n){this.modelId=e;this.options=n;}modelId;adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore(_e),...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:o,modelName:r}=di(this.modelId,n),i=this.mergedOptions(n),s=ci(i.using)(e);return {embedding:await Xt({config:o,modelName:r,text:s})}}};function Xn(t,e){return new de(t,e)}Object.defineProperty(exports,"jwks",{enumerable:true,get:function(){return routecraft.jwks}});Object.defineProperty(exports,"jwt",{enumerable:true,get:function(){return routecraft.jwt}});exports.AgentDestinationAdapter=G;exports.BRAND=jn;exports.EmbeddingDestinationAdapter=de;exports.LlmDestinationAdapter=ie;exports.MCP_LOCAL_TOOL_REGISTRY=E;exports.McpHeadersKeys=Oe;exports.McpServer=Y;exports.McpToolRegistry=X;exports.SuspendError=it;exports.agent=Ye;exports.agentPlugin=Xe;exports.agents=zn;exports.currentTime=Tn;exports.defaultArgs=tt;exports.directTool=We;exports.disposeEmbeddingPipelineCache=Ie;exports.embedding=Xn;exports.embeddingPlugin=Ne;exports.fromFile=Yn;exports.isMcpAdapter=Ur;exports.isOAuthAuth=Ae;exports.llm=Un;exports.llmPlugin=Pe;exports.mcp=we;exports.mcpPlugin=xe;exports.oauth=Bn;exports.randomUuid=Mn;exports.skills=Jn;exports.tools=Je;exports.validateWithSchema=qt;//# sourceMappingURL=index.cjs.map
15
15
  //# sourceMappingURL=index.cjs.map