@chatpanel/events 0.193.0 → 0.200.1
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/agent-card-view.js +1 -0
- package/agent-card.js +1 -0
- package/agent-dialects.js +1 -0
- package/agent-portability.js +1 -0
- package/agent.js +2 -2
- package/client-prefs.js +1 -1
- package/dialect-a2a.js +3 -0
- package/dialect-chatpanel.js +2 -0
- package/dialect-claude.js +3 -0
- package/dialect-codex.js +1 -0
- package/frontmatter.js +14 -0
- package/index.js +1 -1
- package/mcp-args-env.js +3 -0
- package/mcp-config-import.js +2 -6
- package/model-candidates.js +1 -1
- package/model-picker.js +1 -1
- package/package.json +25 -1
- package/recruit.js +1 -1
- package/router.js +2 -2
- package/settings-registry.js +1 -1
- package/skill-package.js +3 -0
- package/source-gate.js +1 -1
- package/team-observe.js +1 -1
- package/toml-lite.js +12 -0
- package/voice-models.js +1 -1
package/mcp-config-import.js
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
`)}function A(n){try{return JSON.parse(n)}catch{return null}}function v(n){const t=n?.mcpServers||n?.mcp_servers||n?.servers||n;return!t||typeof t!="object"?[]:t.command||t.url?[f("Imported MCP server",t)]:Object.entries(t).map(([e,i])=>f(e,i)).filter(Boolean)}function b(n){const t=[],e=/^\s*\[(mcp_servers|mcpServers)\.([^\]]+)\]\s*$/gm;let i;for(;i=e.exec(n);)t.push({name:i[2].trim().replace(/^["']|["']$/g,""),start:e.lastIndex});return t.map((r,s)=>{const o=t[s+1]?.start?n.lastIndexOf(`
|
|
4
|
-
`,t[s+1].start-t[s+1].name.length-15):n.length,l=n.slice(r.start,o<r.start?n.length:o);return f(r.name,j(l))}).filter(Boolean)}function j(n){const t={};for(const o of["type","transport","command","url"]){const l=u(n,o);l!=null&&(t[o]=p(l))}const e=u(n,"args");e!=null&&(t.args=h(e));const i=u(n,"env")??u(n,"environment")??u(n,"environmentVariables");i!=null&&(t.env=m(i));const r=u(n,"headers");r!=null&&(t.headers=m(r));const s=u(n,"authorization")??u(n,"auth_header");return s!=null&&(t.headers={...t.headers||{},Authorization:p(s)}),t}function f(n,t={}){if(!t||typeof t!="object")return null;const e=d(t.command),i=d(t.url),r=String(t.type||t.transport||(e?"stdio":"http")).toLowerCase(),s={name:$(n),enabled:t.enabled!==!1};return e||r==="stdio"?e?{...s,transport:"stdio",command:e,args:S(t.args),env:g(t.env||t.environment||t.environmentVariables)}:null:i?{...s,transport:"http",url:i,headers:g(t.headers)}:null}function u(n,t){const e=String(n||"").split(/\r?\n/);for(let i=0;i<e.length;i++){const r=new RegExp(`^\\s*${C(t)}\\s*=\\s*(.*)$`).exec(e[i]);if(!r)continue;let s=x(r[1]).trim();if(!s)return"";const o=s[0],l=o==="["?"]":o==="{"?"}":"";if(!l)return s;let a=y(s,o,l);for(;a>0&&i+1<e.length;)i+=1,s+=`
|
|
5
|
-
${x(e[i])}`,a=y(s,o,l);return s.trim()}return null}function h(n){const t=String(n||"").trim(),e=t.startsWith("[")&&t.endsWith("]")?t.slice(1,-1):t,i=[];let r=0;for(;r<e.length;){for(;r<e.length&&/[\s,]/.test(e[r]);)r+=1;if(r>=e.length)break;const s=e[r];if(s==='"'||s==="'"){const a=c(e,r);i.push(a.value),r=a.next;continue}let o=r;for(;o<e.length&&e[o]!==",";)o+=1;const l=e.slice(r,o).trim();l&&i.push(l),r=o+1}return i}function m(n){const t=String(n||"").trim(),e=t.startsWith("{")&&t.endsWith("}")?t.slice(1,-1):t,i={};let r=0;for(;r<e.length;){for(;r<e.length&&/[\s,]/.test(e[r]);)r+=1;if(r>=e.length)break;let s="";if(e[r]==='"'||e[r]==="'"){const o=c(e,r);s=o.value,r=o.next}else{let o=r;for(;o<e.length&&e[o]!=="="&&e[o]!==":";)o+=1;s=e.slice(r,o).trim(),r=o}for(;r<e.length&&/[\s=:]/.test(e[r]);)r+=1;if(!s)break;if(e[r]==='"'||e[r]==="'"){const o=c(e,r);i[s]=o.value,r=o.next}else{let o=r;for(;o<e.length&&e[o]!==",";)o+=1;i[s]=p(e.slice(r,o).trim()),r=o+1}}return i}function w(n){const t=[];let e="",i="",r=!1;for(const s of String(n||"")){if(r){e+=s,r=!1;continue}if(s==="\\"){r=!0;continue}if(i){s===i?i="":e+=s;continue}if(s==='"'||s==="'"){i=s;continue}if(/\s/.test(s)){e&&(t.push(e),e="");continue}e+=s}return e&&t.push(e),t}function c(n,t){const e=n[t];let i="",r=!1;for(let s=t+1;s<n.length;s++){const o=n[s];if(r){i+=e==='"'?O(o):o,r=!1;continue}if(o==="\\"&&e==='"'){r=!0;continue}if(o===e)return{value:i,next:s+1};i+=o}return{value:i,next:n.length}}function p(n){const t=String(n||"").trim().replace(/,+$/,"");return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?c(t,0).value:t==="true"?!0:t==="false"?!1:t}function g(n){return!n||typeof n!="object"||Array.isArray(n)?{}:Object.fromEntries(Object.entries(n).map(([t,e])=>[t,String(e)]))}function $(n){return String(n||"MCP server").replace(/[_-]+/g," ").replace(/\b[a-z]/g,t=>t.toUpperCase())}function d(n){return typeof n=="string"?n.trim():""}function x(n){let t="";for(let e=0;e<n.length;e++){const i=n[e];if(t){i===t&&(t=""),i==="\\"&&t==='"'&&(e+=1);continue}if(i==='"'||i==="'")t=i;else if(i==="#")return n.slice(0,e)}return n}function y(n,t,e){let i=0,r="";for(let s=0;s<n.length;s++){const o=n[s];if(r){o==="\\"&&r==='"'?s+=1:o===r&&(r="");continue}o==='"'||o==="'"?r=o:o===t?i+=1:o===e&&(i-=1)}return i}function O(n){return{n:`
|
|
6
|
-
`,r:"\r",t:" ",'"':'"',"\\":"\\"}[n]??n}function C(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export{T as argsToText,z as envLikeArgs,I as envToText,S as parseArgsInput,_ as parseEnvInput,W as parseMcpConfig};
|
|
1
|
+
import{tomlValue as o,parseArray as d,parseInlineTable as l,parseScalar as m}from"./toml-lite.js";import{parseArgsInput as h}from"./mcp-args-env.js";import{parseArgsInput as b,argsToText as j,parseEnvInput as z,envToText as _,envLikeArgs as w}from"./mcp-args-env.js";function I(e){const r=String(e||"").trim();if(!r)return[];const n=v(r);return n?g(n):y(r)}function v(e){try{return JSON.parse(e)}catch{return null}}function g(e){const r=e?.mcpServers||e?.mcp_servers||e?.servers||e;return!r||typeof r!="object"?[]:r.command||r.url?[p("Imported MCP server",r)]:Object.entries(r).map(([n,t])=>p(n,t)).filter(Boolean)}function y(e){const r=[],n=/^\s*\[(mcp_servers|mcpServers)\.([^\]]+)\]\s*$/gm;let t;for(;t=n.exec(e);)r.push({name:t[2].trim().replace(/^["']|["']$/g,""),start:n.lastIndex});return r.map((a,s)=>{const i=r[s+1]?.start?e.lastIndexOf(`
|
|
2
|
+
`,r[s+1].start-r[s+1].name.length-15):e.length,u=e.slice(a.start,i<a.start?e.length:i);return p(a.name,S(u))}).filter(Boolean)}function S(e){const r={};for(const i of["type","transport","command","url"]){const u=o(e,i);u!=null&&(r[i]=m(u))}const n=o(e,"args");n!=null&&(r.args=d(n));const t=o(e,"env")??o(e,"environment")??o(e,"environmentVariables");t!=null&&(r.env=l(t));const a=o(e,"headers");a!=null&&(r.headers=l(a));const s=o(e,"authorization")??o(e,"auth_header");return s!=null&&(r.headers={...r.headers||{},Authorization:m(s)}),r}function p(e,r={}){if(!r||typeof r!="object")return null;const n=f(r.command),t=f(r.url),a=String(r.type||r.transport||(n?"stdio":"http")).toLowerCase(),s={name:x(e),enabled:r.enabled!==!1};return n||a==="stdio"?n?{...s,transport:"stdio",command:n,args:h(r.args),env:c(r.env||r.environment||r.environmentVariables)}:null:t?{...s,transport:"http",url:t,headers:c(r.headers)}:null}function c(e){return!e||typeof e!="object"||Array.isArray(e)?{}:Object.fromEntries(Object.entries(e).map(([r,n])=>[r,String(n)]))}function x(e){return String(e||"MCP server").replace(/[_-]+/g," ").replace(/\b[a-z]/g,r=>r.toUpperCase())}function f(e){return typeof e=="string"?e.trim():""}export{j as argsToText,w as envLikeArgs,_ as envToText,b as parseArgsInput,z as parseEnvInput,I as parseMcpConfig};
|
package/model-candidates.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{classifySource as
|
|
1
|
+
import{classifySource as R}from"./sources.js";import{defineModel as A}from"./router.js";import{windowOf as S}from"./context-window.js";const L=/^(ollama|lm-?studio|llama[-.]?cpp|llamafile|localai|local|vllm|mlx|mlx-lm|koboldcpp|jan|gpt4all|textgen|oobabooga)$/i;function b(e){const n=String(e||"").trim(),t=n.indexOf("/");return t>0&&L.test(n.slice(0,t))?"device":"any"}function g(e,n=null){if(e&&c.includes(e.reach))return e.reach;if(e?.provider_type==="agent"||e?.owned_by==="chatpanel-bridge"||n?.type==="agent"){const a=String(e?.id||""),o=e?.model||(a.includes("/")?a.slice(a.indexOf("/")+1):"");return b(o)}const i=String(n?.baseUrl||"");return i?p({baseUrl:i}):"any"}function p(e){if(e.kind==="bridge")return b(e.model);if(e.gatewayModel)return g(e);if(e.gateway){const i=String(e.model||""),a=(Array.isArray(e.modelOptions)?e.modelOptions:[]).find(o=>o&&typeof o=="object"&&o.id===i);return a?g(a):"any"}if(e.kind==="webllm")return"device";const n=String(e.baseUrl||e.url||"");if(/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0)(:|\/|$)/i.test(n))return"device";const t=R(n);return t.internal&&t.matched!=="unparseable"?"trusted":"any"}const I=Object.freeze([{id:"tools",label:"Tools",hint:"Can call functions — needed for page actions, search and MCP."},{id:"vision",label:"Vision",hint:"Can read images and screenshots."},{id:"reasoning",label:"Reasoning",hint:"Thinks before answering — worth the wait on hard tasks."},{id:"long-context",label:"Long context",hint:"Handles large documents and long meetings."},{id:"coding",label:"Coding",hint:"Strong at writing and refactoring code."},{id:"json",label:"Structured output",hint:"Reliably returns valid JSON."},{id:"speech-in",label:"Speech in",hint:"Takes audio as input — a meeting, a voice note."},{id:"speech-out",label:"Speech out",hint:"Speaks its answer."},{id:"audio",label:"Audio",hint:"Understands audio content beyond speech — music, sounds."},{id:"image-out",label:"Image out",hint:"Generates images."}]);function E(e){const n=String(e.model||"").toLowerCase(),t=new Set(["json"]);return(e.kind==="bridge"||e.model)&&t.add("tools"),/gpt-4|gpt-5|claude|gemini|vision|vl\b|llava|pixtral/.test(n)&&t.add("vision"),/o1|o3|r1|reason|think|opus|sonnet|deepseek-r/.test(n)&&t.add("reasoning"),/200k|1m\b|long|gemini|claude|gpt-4\.1|gpt-5/.test(n)&&t.add("long-context"),/code|coder|codex|deepseek|qwen|opus|sonnet/.test(n)&&t.add("coding"),e.kind==="bridge"&&(t.add("coding"),t.add("reasoning")),[...t]}const y=[/localhost|127\.0\.0\.1|ollama|lm.?studio/i,/anthropic|openai\.com|api\.deepseek|googleapis|x\.ai/i,/(^|\W)bridge(\W|$)/i,/openrouter|huggingface|together|groq|fireworks|nvidia|replicate/i],k=1e3;function _(e,n){const t=`${e.baseUrl||e.url||""} ${e.name||""} ${n||e.kind||""}`;for(let i=0;i<y.length;i++)if(y[i].test(t))return k+i*10;return k+50}function w(e){const n=`${e.model||""} ${e.name||""}`.toLowerCase(),t=Number(/(\d+(?:\.\d+)?)\s*b\b/.exec(n)?.[1]);return Number.isFinite(t)?t>=60?.85:t>=20?.6:.3:/instant|mini|nano|tiny|lite|-small\b|haiku/.test(n)?.3:/opus|gpt-5|o1|o3|\bpro\b|ultra|deepseek-r|thinking/.test(n)?.9:/sonnet|gpt-4|flash|gemini|deepseek|qwen|mistral|codestral/.test(n)?.6:e.kind==="bridge"?.8:.5}function x(e,n){const t=e==="device"?1500:700,i=Number.isFinite(n)?n:.5;return Math.round(t*(.6+i))}function M(e,n){if(n==="device")return 0;const t=String(e.model||"").toLowerCase();return/opus|gpt-4|pro\b/.test(t)?5:/sonnet|mini|flash|haiku/.test(t)?1:2}function m(e){if(e==null||e===""||typeof e=="boolean")return null;const n=Number(e);return Number.isFinite(n)?n:null}function f(e,n={},{declared:t=!1}={}){if(!n||typeof n!="object")return e;const i={...e},a=m(n.providerRank);a!==null&&(i.providerRank=a,i.orderPinned=!0),Array.isArray(n.capabilities)&&(i.capabilities=[...n.capabilities]);for(const o of["costPer1k","latencyMs","quality","window"]){const l=m(n[o]);l!==null&&(i[o]=l)}if(typeof n.available=="boolean"&&(i.available=n.available),n.reach&&c.includes(n.reach)){const o=c.includes(e.reachDetected)?e.reachDetected:e.reach;i.reachDetected=o,s[n.reach]>s[e.reach]?i.reach=n.reach:t&&s[n.reach]<s[o]&&s[n.reach]>=s[v]&&(i.reach=n.reach,i.reachDeclared=!0)}return i}import{DEFAULT_MIN_CALLS as O,cardOverride as D}from"./model-ledger.js";import{cardOverride as V}from"./model-ledger.js";function H(e,n,{minCalls:t=O,jobKind:i=null,userOverride:a=null}={}){const{override:o,observed:l}=D(n,{minCalls:t,jobKind:i});let r=f(e,o);const u=new Set(n?.capabilities?.withdrawn||[]);return u.size&&Array.isArray(r.capabilities)&&(r={...r,capabilities:r.capabilities.filter(d=>!u.has(d))},l.push("capabilities")),a&&(r=f(r,a,{declared:!0})),{...r,observed:l}}const s={device:0,trusted:1,any:2},c=["device","trusted","any"],v="trusted";function N(e){const n=c.indexOf(e);return n<0?[...c]:c.slice(Math.min(n,s[v]))}function P(e,n){return!c.includes(n)||!c.includes(e)?!1:s[n]<s[e]}const C=Object.freeze([{id:"device",label:"On this device",hint:"The words never leave this machine."},{id:"trusted",label:"My machine/network",hint:"Your own machine, your own network, or a service your organisation runs and controls."},{id:"any",label:"Third party",hint:"Someone else’s service."}]),h=e=>C.find(n=>n.id===e)?.label||String(e||""),q='How far a request travels to reach it. ChatPanel reads this from the address, so a coding agent that answers from its vendor comes out as a third party — and you can say otherwise: further out to hold it at arm’s length, or "My machine/network" when your organisation’s own controls put it inside yours. That is your word rather than a detection, and the row says so. "On this device" is never offered: it means the words never leave this machine, which is a fact we detect rather than a level of trust anyone can grant.';function U(e){return N(e).map(n=>{const t=P(e,n);return{value:n,label:n===e?`${h(n)} (detected)`:t?`${h(n)} (you declare)`:h(n),detected:n===e,declares:t,hint:t?`ChatPanel detects ${h(e)}. Choose this only if it really is inside your machine or network — content from an internal source will then be allowed to reach it.`:C.find(i=>i.id===n)?.hint||""}})}function z(e,n=null){const t=p(e||{});return!n||typeof n!="object"||!n.reach?t:f({reach:t,reachDetected:t},n,{declared:!0}).reach}function B(e,n,{override:t=null,health:i=null}={}){if(!e||!e.model&&n!=="bridge"&&e.kind!=="bridge")return null;const a=e.id||e.name||e.model;if(!a)return null;const o=[e.name,e.model&&e.model!==e.name?e.model:null].filter(Boolean).join(" · ")||[e.bridgeAgent,e.model].filter(Boolean).join(" · ")||n||e.kind||String(a),l=n||e.kind,r=p(l==="bridge"?{...e,kind:"bridge"}:e),u={id:a,label:o,model:e.model||"",reach:r,reachDetected:r,classUsed:r==="device"?"L":l==="bridge"?"A":"C",capabilities:E({...e,kind:l}),window:S({...e,kind:l,reach:r}),costPer1k:M(e,r),latencyMs:x(r,w({...e,kind:l})),quality:w({...e,kind:l}),providerRank:_(e,l),available:e.enabled!==!1},d=f(u,t||{},{declared:!0});return e.enabled===!1&&(d.available=!1),m(t?.latencyMs)===null&&(d.latencyMs=x(r,d.quality)),A({...d,available:d.available&&(i?i.available!==!1:!0),rateLimited:!!i?.rateLimited})}export{O as DEFAULT_MIN_CALLS,I as KNOWN_CAPABILITIES,C as REACH_CHOICES,v as REACH_DECLARABLE_FLOOR,q as REACH_HELP,s as REACH_RANK,b as agentModelReach,H as applyCard,f as applyOverride,E as capabilitiesOf,V as cardOverride,M as costOf,B as inferCandidate,x as latencyOf,m as numericOverride,_ as providerRankOf,w as qualityOf,N as reachChoicesFor,z as reachFor,P as reachIsDeclaration,h as reachLabel,p as reachOf,g as reachOfGatewayModel,U as reachOptionsFor};
|
package/model-picker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const c=e=>e?.providerType==="agent"||e?.viaBridge===!0,f=e=>String(e?.provider||e?.owner||"").trim(),d={openai:"OpenAI",openrouter:"OpenRouter",deepseek:"DeepSeek",xai:"xAI",vllm:"vLLM",lmstudio:"LM Studio",llamacpp:"llama.cpp","llama.cpp":"llama.cpp",huggingface:"Hugging Face",togetherai:"Together AI",together:"Together AI",githubcopilot:"GitHub Copilot",awsbedrock:"AWS Bedrock",bedrock:"AWS Bedrock",azureopenai:"Azure OpenAI",googleai:"Google AI",vertexai:"Vertex AI"};function p(e){const t=String(e||"").trim();if(!t)return"Other";const o=d[t.toLowerCase().replace(/[\s_-]/g,"")];return o||(/[A-Z ]/.test(t)?t:t.charAt(0).toUpperCase()+t.slice(1))}const u=(e,t)=>(e.available===!1)-(t.available===!1)||String(e.id).localeCompare(String(t.id));function b(e,{selectedId:t=""}={}){const o=Array.isArray(e)?e.filter(Boolean):[],r=[],n=new Map;for(const a of o){if(c(a)){r.push(a);continue}const s=f(a)||"other";n.has(s)||n.set(s,[]),n.get(s).push(a)}const l=a=>({...a,label:a.label||a.id,selected:!!t&&a.id===t}),i=[];r.length&&i.push({key:"agents",label:"
|
|
1
|
+
const c=e=>e?.providerType==="agent"||e?.viaBridge===!0,f=e=>String(e?.provider||e?.owner||"").trim(),d={openai:"OpenAI",openrouter:"OpenRouter",deepseek:"DeepSeek",xai:"xAI",vllm:"vLLM",lmstudio:"LM Studio",llamacpp:"llama.cpp","llama.cpp":"llama.cpp",huggingface:"Hugging Face",togetherai:"Together AI",together:"Together AI",githubcopilot:"GitHub Copilot",awsbedrock:"AWS Bedrock",bedrock:"AWS Bedrock",azureopenai:"Azure OpenAI",googleai:"Google AI",vertexai:"Vertex AI"};function p(e){const t=String(e||"").trim();if(!t)return"Other";const o=d[t.toLowerCase().replace(/[\s_-]/g,"")];return o||(/[A-Z ]/.test(t)?t:t.charAt(0).toUpperCase()+t.slice(1))}const u=(e,t)=>(e.available===!1)-(t.available===!1)||String(e.id).localeCompare(String(t.id));function b(e,{selectedId:t=""}={}){const o=Array.isArray(e)?e.filter(Boolean):[],r=[],n=new Map;for(const a of o){if(c(a)){r.push(a);continue}const s=f(a)||"other";n.has(s)||n.set(s,[]),n.get(s).push(a)}const l=a=>({...a,label:a.label||a.id,selected:!!t&&a.id===t}),i=[];r.length&&i.push({key:"agents",label:"Coding Agents",kind:"agent",items:r.slice().sort(u).map(l)});for(const a of[...n.keys()].sort((s,g)=>s.localeCompare(g)))i.push({key:`provider:${a}`,label:p(a),kind:"provider",items:n.get(a).slice().sort(u).map(l)});return i}function h(e,t){const o=String(t||"").trim().toLowerCase();if(!o)return e;const r=[];for(const n of e){if(n.label.toLowerCase().includes(o)){r.push(n);continue}const l=n.items.filter(i=>String(i.label).toLowerCase().includes(o)||String(i.id).toLowerCase().includes(o));l.length&&r.push({...n,items:l})}return r}function A(e){const t=Array.isArray(e)?e.filter(Boolean):[],o=t.filter(r=>r.available!==!1);return(o.find(c)||o[0]||t[0]||{}).id||""}function y(e,t){const o=Array.isArray(e)?e.filter(Boolean):[],r=o.find(n=>n.id===t);return r?{label:r.label||r.id,available:r.available!==!1,reason:r.reason||"",agent:c(r),provider:p(f(r))}:{label:t||"No model",available:o.length?!1:null,reason:o.length&&t?`${t} is not offered by the gateway any more`:"",agent:!1,provider:""}}function m(e,t=[],{max:o=5}={}){const r=new Map;for(const l of e||[])for(const i of l.items||[])r.has(i.id)||r.set(i.id,i);const n=[];for(const l of t||[]){const i=r.get(l);if(i&&!n.includes(i)&&n.push(i),n.length>=o)break}return n.length?[{key:"recent",label:"Recent",kind:"recent",items:n},...e||[]]:e||[]}function v(e=[],t,{max:o=5}={}){return t?[t,...(Array.isArray(e)?e:[]).filter(r=>r&&r!==t)].slice(0,o):Array.isArray(e)?e.slice(0,o):[]}export{A as defaultModelId,h as filterSections,b as groupModels,y as modelSummary,v as rememberRecent,m as withRecent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/events",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.200.1",
|
|
4
4
|
"description": "The canonical ChatPanel event-log and capability contracts — typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"./adapters.js": "./adapters.js",
|
|
10
10
|
"./adaptive-tool-policy.js": "./adaptive-tool-policy.js",
|
|
11
11
|
"./agent.js": "./agent.js",
|
|
12
|
+
"./agent-card.js": "./agent-card.js",
|
|
13
|
+
"./agent-card-view.js": "./agent-card-view.js",
|
|
14
|
+
"./agent-dialects.js": "./agent-dialects.js",
|
|
15
|
+
"./agent-portability.js": "./agent-portability.js",
|
|
12
16
|
"./attribution.js": "./attribution.js",
|
|
13
17
|
"./backup-envelope.js": "./backup-envelope.js",
|
|
14
18
|
"./blob-envelope.js": "./blob-envelope.js",
|
|
@@ -23,6 +27,7 @@
|
|
|
23
27
|
"./capability-providers.js": "./capability-providers.js",
|
|
24
28
|
"./capability-tools.js": "./capability-tools.js",
|
|
25
29
|
"./fast-path.js": "./fast-path.js",
|
|
30
|
+
"./frontmatter.js": "./frontmatter.js",
|
|
26
31
|
"./plan.js": "./plan.js",
|
|
27
32
|
"./plan-executor.js": "./plan-executor.js",
|
|
28
33
|
"./model-roles.js": "./model-roles.js",
|
|
@@ -46,6 +51,10 @@
|
|
|
46
51
|
"./default-mcp-servers.js": "./default-mcp-servers.js",
|
|
47
52
|
"./default-skills.js": "./default-skills.js",
|
|
48
53
|
"./distance.js": "./distance.js",
|
|
54
|
+
"./dialect-a2a.js": "./dialect-a2a.js",
|
|
55
|
+
"./dialect-chatpanel.js": "./dialect-chatpanel.js",
|
|
56
|
+
"./dialect-claude.js": "./dialect-claude.js",
|
|
57
|
+
"./dialect-codex.js": "./dialect-codex.js",
|
|
49
58
|
"./entitlement.js": "./entitlement.js",
|
|
50
59
|
"./engine.js": "./engine.js",
|
|
51
60
|
"./entity.js": "./entity.js",
|
|
@@ -69,6 +78,7 @@
|
|
|
69
78
|
"./sideline.js": "./sideline.js",
|
|
70
79
|
"./mcp-client.js": "./mcp-client.js",
|
|
71
80
|
"./mcp-config-import.js": "./mcp-config-import.js",
|
|
81
|
+
"./mcp-args-env.js": "./mcp-args-env.js",
|
|
72
82
|
"./mcp-dispatch.js": "./mcp-dispatch.js",
|
|
73
83
|
"./mcp-errors.js": "./mcp-errors.js",
|
|
74
84
|
"./mcp-manager.js": "./mcp-manager.js",
|
|
@@ -128,6 +138,7 @@
|
|
|
128
138
|
"./search-engines.js": "./search-engines.js",
|
|
129
139
|
"./search-gateway.js": "./search-gateway.js",
|
|
130
140
|
"./skill-manifest.js": "./skill-manifest.js",
|
|
141
|
+
"./skill-package.js": "./skill-package.js",
|
|
131
142
|
"./skill-runtime.js": "./skill-runtime.js",
|
|
132
143
|
"./skill-files.js": "./skill-files.js",
|
|
133
144
|
"./attached-paths.js": "./attached-paths.js",
|
|
@@ -182,6 +193,7 @@
|
|
|
182
193
|
"./tool-round.js": "./tool-round.js",
|
|
183
194
|
"./tool-schema.js": "./tool-schema.js",
|
|
184
195
|
"./tool-traits.js": "./tool-traits.js",
|
|
196
|
+
"./toml-lite.js": "./toml-lite.js",
|
|
185
197
|
"./toolset.js": "./toolset.js",
|
|
186
198
|
"./trajectory.js": "./trajectory.js",
|
|
187
199
|
"./turn-loop.js": "./turn-loop.js",
|
|
@@ -223,6 +235,10 @@
|
|
|
223
235
|
"adapters.js",
|
|
224
236
|
"adaptive-tool-policy.js",
|
|
225
237
|
"agent.js",
|
|
238
|
+
"agent-card.js",
|
|
239
|
+
"agent-card-view.js",
|
|
240
|
+
"agent-dialects.js",
|
|
241
|
+
"agent-portability.js",
|
|
226
242
|
"attribution.js",
|
|
227
243
|
"backup-envelope.js",
|
|
228
244
|
"blob-envelope.js",
|
|
@@ -237,6 +253,7 @@
|
|
|
237
253
|
"capability-providers.js",
|
|
238
254
|
"capability-tools.js",
|
|
239
255
|
"fast-path.js",
|
|
256
|
+
"frontmatter.js",
|
|
240
257
|
"plan.js",
|
|
241
258
|
"plan-executor.js",
|
|
242
259
|
"model-roles.js",
|
|
@@ -257,6 +274,10 @@
|
|
|
257
274
|
"default-mcp-servers.js",
|
|
258
275
|
"default-skills.js",
|
|
259
276
|
"distance.js",
|
|
277
|
+
"dialect-a2a.js",
|
|
278
|
+
"dialect-chatpanel.js",
|
|
279
|
+
"dialect-claude.js",
|
|
280
|
+
"dialect-codex.js",
|
|
260
281
|
"entitlement.js",
|
|
261
282
|
"engine.js",
|
|
262
283
|
"entity.js",
|
|
@@ -283,6 +304,7 @@
|
|
|
283
304
|
"sideline.js",
|
|
284
305
|
"mcp-client.js",
|
|
285
306
|
"mcp-config-import.js",
|
|
307
|
+
"mcp-args-env.js",
|
|
286
308
|
"mcp-dispatch.js",
|
|
287
309
|
"mcp-errors.js",
|
|
288
310
|
"mcp-manager.js",
|
|
@@ -349,6 +371,7 @@
|
|
|
349
371
|
"search-engines.js",
|
|
350
372
|
"search-gateway.js",
|
|
351
373
|
"skill-manifest.js",
|
|
374
|
+
"skill-package.js",
|
|
352
375
|
"skill-runtime.js",
|
|
353
376
|
"skill-files.js",
|
|
354
377
|
"attached-paths.js",
|
|
@@ -403,6 +426,7 @@
|
|
|
403
426
|
"tool-round.js",
|
|
404
427
|
"tool-schema.js",
|
|
405
428
|
"tool-traits.js",
|
|
429
|
+
"toml-lite.js",
|
|
406
430
|
"toolset.js",
|
|
407
431
|
"trajectory.js",
|
|
408
432
|
"turn-loop.js",
|
package/recruit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{fit as K,engineKey as j,normalizeEngine as E}from"./scorecard.js";import{cardOverride as G,DEFAULT_MIN_CALLS as J}from"./model-ledger.js";import{engineKeyOf as Y,describeEngine as T}from"./engine.js";import{engineOf as H,agentFromForm as X,normalizeAgent as Q,resolveTeam as V}from"./agent.js";import{applyAll as Z,jobToRole as ee}from"./job.js";import{WORK_GRANTS as A}from"./team.js";import{defineSchema as ne,describeSchema as te,coerce as se}from"./structured.js";import{normalizeBudget as ie}from"./budget.js";const q=.5,oe=5,ae=1500,I={device:0,trusted:1,any:2},B=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),F=e=>e==null||!Number.isFinite(e)?null:Math.round(e*1e3)/1e3,k=(e,l)=>String(e||"").trim().slice(0,l),M=e=>(Array.isArray(e)?e:[]).map(l=>String(l).toLowerCase()),S=ne({name:"recruit",purpose:"which applicant gets the job, or that none fits and what agent should exist",fields:{pick:{type:"string",max:64,describe:'the id of the ONE applicant to recruit, or "" when none fits'},why:{type:"string",required:!0,max:400,describe:"one or two sentences a person reads on the job page"},confidence:{type:"number",describe:"0 to 1"},proposalName:{type:"string",max:60,describe:"when none fits: the agent that should exist"},proposalPurpose:{type:"string",max:300},proposalSkills:{type:"string[]",maxItems:12},proposalGrants:{type:"string[]",maxItems:8,describe:"from: data, web, history, mcp, shell, fs:write, scm:read, scm:push, scm:pr"}},nothing:{pick:"",why:"none fits"}});function _(e,{card:l=null,minCalls:t=J,jobKind:s=null}={}){if(!B(e))return null;const o=e;let a=o.engine?E(o.engine):null;if(!a){const d=String(o.model||o.id||"");if(!d)return null;if(o.kind==="bridge"||o.kind==="harness"||o.classUsed==="A"){const f=d.indexOf("/");a=f>0?{kind:"harness",id:d.slice(0,f),model:d.slice(f+1)}:{kind:"harness",id:d}}else a={kind:"model",id:d}}const r=j(a);if(!r)return null;const{override:i,observed:c}=G(l,{minCalls:t,jobKind:s}),n=new Set(l?.capabilities?.withdrawn||[]),h=[...new Set(M(o.capabilities))].filter(d=>!n.has(d));n.size&&M(o.capabilities).some(d=>n.has(d))&&c.push("capabilities");const p=d=>d==null||d===""||!Number.isFinite(Number(d))?null:Number(d),u=I[o.reach]!=null?o.reach:"any",g=i.costPer1k??p(o.costPer1k)??(u==="device"&&a.kind==="model"?0:null);return{key:r,engine:a,label:k(o.label||o.name||le(a),120),reach:u,capabilities:h,quality:i.quality??p(o.quality),latencyMs:i.latencyMs??p(o.latencyMs),costPer1k:g,costPerTask:p(l?.cost?.perTask),availability:p(l?.availability?.rate),available:i.available??(o.available!==!1&&o.usable!==!1),observed:c}}const le=e=>`${e.id}${e.model&&e.model!==e.id?`/${e.model}`:""}`;function Ne(e,{cards:l={},minCalls:t,jobKind:s}={}){const o=[],a=new Set;for(const r of Array.isArray(e)?e:[]){const i=_(r,{card:null,minCalls:t,jobKind:s});!i||a.has(i.key)||(a.add(i.key),o.push(l[i.key]?_(r,{card:l[i.key],minCalls:t,jobKind:s}):i))}return o}function L(e,{reach:l="any"}={}){const t=M(e?.needs?.grants).filter(c=>c!=="none"),s=M(e?.needs?.tools),o=t.some(c=>A.includes(c)),a=[],r=[];(s.length||t.length)&&(a.push("tools"),r.push("the job uses tools")),o&&r.push(`a work grant (${t.filter(c=>A.includes(c)).join(", ")}) needs
|
|
1
|
+
import{fit as K,engineKey as j,normalizeEngine as E}from"./scorecard.js";import{cardOverride as G,DEFAULT_MIN_CALLS as J}from"./model-ledger.js";import{engineKeyOf as Y,describeEngine as T}from"./engine.js";import{engineOf as H,agentFromForm as X,normalizeAgent as Q,resolveTeam as V}from"./agent.js";import{applyAll as Z,jobToRole as ee}from"./job.js";import{WORK_GRANTS as A}from"./team.js";import{defineSchema as ne,describeSchema as te,coerce as se}from"./structured.js";import{normalizeBudget as ie}from"./budget.js";const q=.5,oe=5,ae=1500,I={device:0,trusted:1,any:2},B=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),F=e=>e==null||!Number.isFinite(e)?null:Math.round(e*1e3)/1e3,k=(e,l)=>String(e||"").trim().slice(0,l),M=e=>(Array.isArray(e)?e:[]).map(l=>String(l).toLowerCase()),S=ne({name:"recruit",purpose:"which applicant gets the job, or that none fits and what agent should exist",fields:{pick:{type:"string",max:64,describe:'the id of the ONE applicant to recruit, or "" when none fits'},why:{type:"string",required:!0,max:400,describe:"one or two sentences a person reads on the job page"},confidence:{type:"number",describe:"0 to 1"},proposalName:{type:"string",max:60,describe:"when none fits: the agent that should exist"},proposalPurpose:{type:"string",max:300},proposalSkills:{type:"string[]",maxItems:12},proposalGrants:{type:"string[]",maxItems:8,describe:"from: data, web, history, mcp, shell, fs:write, scm:read, scm:push, scm:pr"}},nothing:{pick:"",why:"none fits"}});function _(e,{card:l=null,minCalls:t=J,jobKind:s=null}={}){if(!B(e))return null;const o=e;let a=o.engine?E(o.engine):null;if(!a){const d=String(o.model||o.id||"");if(!d)return null;if(o.kind==="bridge"||o.kind==="harness"||o.classUsed==="A"){const f=d.indexOf("/");a=f>0?{kind:"harness",id:d.slice(0,f),model:d.slice(f+1)}:{kind:"harness",id:d}}else a={kind:"model",id:d}}const r=j(a);if(!r)return null;const{override:i,observed:c}=G(l,{minCalls:t,jobKind:s}),n=new Set(l?.capabilities?.withdrawn||[]),h=[...new Set(M(o.capabilities))].filter(d=>!n.has(d));n.size&&M(o.capabilities).some(d=>n.has(d))&&c.push("capabilities");const p=d=>d==null||d===""||!Number.isFinite(Number(d))?null:Number(d),u=I[o.reach]!=null?o.reach:"any",g=i.costPer1k??p(o.costPer1k)??(u==="device"&&a.kind==="model"?0:null);return{key:r,engine:a,label:k(o.label||o.name||le(a),120),reach:u,capabilities:h,quality:i.quality??p(o.quality),latencyMs:i.latencyMs??p(o.latencyMs),costPer1k:g,costPerTask:p(l?.cost?.perTask),availability:p(l?.availability?.rate),available:i.available??(o.available!==!1&&o.usable!==!1),observed:c}}const le=e=>`${e.id}${e.model&&e.model!==e.id?`/${e.model}`:""}`;function Ne(e,{cards:l={},minCalls:t,jobKind:s}={}){const o=[],a=new Set;for(const r of Array.isArray(e)?e:[]){const i=_(r,{card:null,minCalls:t,jobKind:s});!i||a.has(i.key)||(a.add(i.key),o.push(l[i.key]?_(r,{card:l[i.key],minCalls:t,jobKind:s}):i))}return o}function L(e,{reach:l="any"}={}){const t=M(e?.needs?.grants).filter(c=>c!=="none"),s=M(e?.needs?.tools),o=t.some(c=>A.includes(c)),a=[],r=[];(s.length||t.length)&&(a.push("tools"),r.push("the job uses tools")),o&&r.push(`a work grant (${t.filter(c=>A.includes(c)).join(", ")}) needs a coding agent`);const i=I[l]!=null?l:"any";return i!=="any"&&r.push(`reach ≤ ${i} (the project's privacy setting)`),{capabilities:a,harness:o,reach:i,why:r}}const C=(e,l,t)=>{const s=[];e.available===!1&&s.push("unavailable right now"),I[e.reach]>I[l.reach]&&s.push(`reach ${e.reach} exceeds ${l.reach}`),l.harness&&e.engine.kind!=="harness"&&s.push("not a coding agent");const o=l.capabilities.filter(a=>!e.capabilities.includes(a));if(o.length&&!(e.engine.kind==="harness"&&o.every(a=>a==="tools"))&&s.push(`lacks ${o.join(", ")}`),t){const a=r=>(r||[]).some(i=>i===e.key||i===`${e.engine.kind}:${e.engine.id}`||i===e.engine.id||i===e.engine.model);t.allow?.length&&!a(t.allow)&&s.push("not on the policy's allow list"),t.deny?.length&&a(t.deny)&&s.push("on the policy's deny list"),t.floor?.quality!=null&&e.quality!=null&&e.quality<t.floor.quality&&s.push(`quality ${e.quality} under the floor ${t.floor.quality}`),t.floor?.availability!=null&&e.availability!=null&&e.availability<t.floor.availability&&s.push(`availability ${e.availability} under the floor ${t.floor.availability}`),t.ceiling?.costPerTask!=null&&e.costPerTask!=null&&e.costPerTask>t.ceiling.costPerTask&&s.push(`$${e.costPerTask}/task over the ceiling`),t.ceiling?.latencyMs!=null&&e.latencyMs!=null&&e.latencyMs>t.ceiling.latencyMs&&s.push(`${e.latencyMs} ms over the ceiling`)}return s},D=(e,l)=>(e?.byEngine||[]).find(t=>t.key===l)?.rating?.avg??null;function re(e,l,t){const s=n=>n.quality??.5,o=n=>n.costPerTask??n.costPer1k??null,a=Math.max(...e.map(n=>o(n)??0),0)||1,r=Math.max(...e.map(n=>n.latencyMs??0),0)||1,i=n=>{const h=D(t,n.key);return h==null?0:h-.5},c=n=>{switch(l){case"cheapest-that-clears":return-((o(n)??a)/a)+s(n)*.01;case"best-quality":return s(n)-(o(n)??a)/a*.01;case"fastest":return-((n.latencyMs??r)/r)+s(n)*.01;default:return s(n)*.5+(1-(o(n)??a)/a)*.25+(1-(n.latencyMs??r)/r)*.25}};return e.map(n=>({row:n,score:c(n)+i(n)*.05})).sort((n,h)=>h.score-n.score).map(n=>n.row)}const z=e=>`${e.key} (quality ${e.quality??"?"}${e.observed.includes("quality")?" observed":""}${e.costPerTask!=null?`, $${e.costPerTask}/task`:e.costPer1k!=null?`, $${e.costPer1k}/1k`:""}${e.latencyMs!=null?`, ${e.latencyMs} ms`:""})`;function ce(e,l,{rows:t=[],summary:s=null,need:o=null,reach:a="any",chatModel:r=null,explore:i=!1}={}){const c=o||L(l,{reach:a}),n=H(e,{chatModel:r}),h=Array.isArray(t)?t.filter(Boolean):[],p=m=>({engine:null,key:null,reasons:m,alternatives:[],exploration:!1,clears:!1});if(n.kind!=="auto"){const m=Y(n),v=h.find(x=>x.key===m)||h.find(x=>x.engine.kind===n.kind&&(n.kind==="harness"?x.engine.id===n.harnessId&&!n.model:(x.engine.id===n.model||x.engine.model===n.model)&&!n.providerId));if(!h.length)return{engine:E({kind:n.kind,id:n.kind==="harness"?n.harnessId:n.providerId||n.model,model:n.model}),key:m,reasons:[`${T(n)} — pinned by the agent; no roster to check it against`],alternatives:[],exploration:!1,clears:!0};if(!v)return p([`${T(n)} is pinned by the agent but is not installed or configured here`]);const R=C(v,c,null);return R.length?p([`${T(n)} is pinned by the agent but ${R.join("; ")}`]):{engine:v.engine,key:v.key,reasons:["pinned by the agent",...c.why],alternatives:[],exploration:!1,clears:!0}}if(!h.length)return p(["engine is auto and the roster is empty"]);const u=n.policy||{},g=[],d=h.filter(m=>{const $=C(m,c,u);return $.length&&g.push(`${m.key}: ${$.join("; ")}`),!$.length});if(!d.length)return p([`no engine clears ${c.harness?"a coding agent with ":""}${c.capabilities.join(", ")||"the requirements"}${c.reach!=="any"?` within reach ${c.reach}`:""}${u.prefer?` under ${u.prefer}`:""}`,...g.slice(0,4)]);const f=re(d,u.prefer||"balanced",s);let y=f[0],b=!1;const w=m=>m.costPerTask??m.costPer1k??null;if(i&&y.engine.kind!=="harness"){const m=f.filter($=>$.engine.kind!=="harness"&&w($)!=null&&w(y)!=null&&w($)<w(y)).sort(($,v)=>w(v)-w($));m.length&&(b=!0,y=m[0])}const N=[b?`exploration: one tier cheaper than the policy's pick (${f[0].key})`:`${(u.prefer||"balanced").replace(/-/g," ")}: ${z(y)}`,...c.why],O=D(s,y.key);return O!=null&&N.push(`this agent rated ${Math.round(O*100)}% on it before`),(u.floor?.quality!=null||u.ceiling?.costPerTask!=null||u.ceiling?.latencyMs!=null)&&N.push(`${d.length} of ${h.length} engines clear the policy`),{engine:y.engine,key:y.key,reasons:N,alternatives:f.filter(m=>m!==y).slice(0,4).map(m=>m.engine),exploration:b,clears:!0}}function ue(e){const l=new Map((e||[]).filter(Boolean).map(t=>[t.key,t]));return{qualityOf:t=>l.get(t)?.quality??null,costOf:t=>{const s=l.get(t);return s?s.costPerTask??null:null}}}function pe(e,l,{summaries:t={},rows:s=[],reach:o="any",chatModel:a=null,adjust:r=!0,now:i=Date.now()}={}){const c=L(e,{reach:o}),n=ue(s),h=(u,g,d)=>{const f=K(u,{...g,tools:g.tools||g.grants},d,r?n:{adjust:!1}),y=ce(g,u,{rows:s,summary:d,need:c,chatModel:a});return{score:f.score,reasons:[...f.reasons,...y.reasons].slice(0,8),...y.clears?{engine:y.engine}:{},covers:U(u,g)}},p=new Map((l||[]).map(u=>[u?.id,U(e,u)]));return Z(e,l,h,{cards:t,now:i}).map(u=>({...u,covers:p.get(u.agentId)!==!1}))}function U(e,l){const t=M(e?.needs?.skills);if(!t.length)return!0;const s=new Set(M(l?.skills));return t.some(o=>s.has(o))}function de(e,l,t=[],{rows:s=[],top:o=oe}={}){const a=new Map((t||[]).map(n=>[n.id,n])),r=new Map((s||[]).filter(Boolean).map(n=>[n.key,n])),i=e?.needs||{},c=(l||[]).slice(0,o).map(n=>{const h=a.get(n.agentId)||{},p=n.engine?j(n.engine):null,u=p?r.get(p):null;return`- ${n.agentId} (fit ${Math.round((n.fit||0)*100)}%)${h.purpose?` — ${k(h.purpose,160)}`:""}; skills: ${(h.skills||[]).join(", ")||"none"}; grants: ${(h.grants||[]).join(", ")||"none"}; ${p?`engine: ${u?z(u):p}`:"NOT RECRUITABLE NOW — no engine clears"}; ${(n.reasons||[]).slice(0,4).join("; ")}`});return[`You are the evaluator for the job "${k(e?.title,200)}" on project ${e?.projectId||"?"}. Recruit ONE applicant, or say none fits.`,`Brief: ${k(e?.brief,ae)}`,`Needs — skills: ${(i.skills||[]).join(", ")||"none named"}; tools: ${(i.tools||[]).join(", ")||"none named"}; grants: ${(i.grants||[]).join(", ")||"none"}${e?.budget?`; budget: ${Object.entries(e.budget).map(([n,h])=>`${h} ${n}`).join(", ")}`:""}.`,"","Applicants, best computed fit first (fit = the skills, tools and grants the job names, then the attested record, then size):",...c.length?c:["- (no one applied)"],"",'Rules: prefer the applicant that has what the job names and a record of clearing work like it on the engine shown; never pick one marked NOT RECRUITABLE NOW; a lower fit is right only when its reasons show the higher one lacks something the brief needs. When no applicant has the skills the job names, answer pick "" and propose the agent that should exist.',"",te(S)].join(`
|
|
2
2
|
`)}function W(e,l=[]){const t=se(e,S);if(!t)return null;const s=t.value,o=String(s.pick||"").trim(),a=o?(l||[]).find(i=>i.agentId===o):null,r=s.proposalName?{name:k(s.proposalName,60),purpose:k(s.proposalPurpose,300),skills:(s.proposalSkills||[]).map(i=>k(i,80)).filter(Boolean),grants:(s.proposalGrants||[]).map(i=>k(i,64)).filter(Boolean)}:null;return a&&a.engine?{pick:o,why:k(s.why,400)||"the evaluator's pick",confidence:F(s.confidence),proposal:null}:{pick:"",why:a?`the evaluator picked ${o}, which no engine can run right now`:k(s.why,400)||"none fits",confidence:F(s.confidence),proposal:r}}function he(e,l,{evaluation:t=null,minFit:s=q}={}){const o=l||[];if(t&&t.pick){const i=o.find(c=>c.agentId===t.pick&&c.engine);if(i)return{kind:"recruit",agentId:i.agentId,engine:i.engine,fit:i.fit,why:t.why,by:"evaluator"}}if(t&&!t.pick)return{kind:"none",why:t.why,proposal:t.proposal||P(e),by:"evaluator"};const a=o.find(i=>i.engine&&i.covers!==!1);return a&&a.fit>=s?{kind:"recruit",agentId:a.agentId,engine:a.engine,fit:a.fit,why:`best fit (${Math.round(a.fit*100)}%): ${(a.reasons||[])[0]||"meets the needs"}`,by:"fit"}:{kind:"none",why:o.length?a?`the best fit is ${Math.round(a.fit*100)}%, under the ${Math.round(s*100)}% floor: ${(a.reasons||[]).find(i=>/missing/.test(i))||a.reasons?.[0]||""}`:o.some(i=>i.engine)?`no applicant has a skill the job names (${(e?.needs?.skills||[]).join(", ")})`:`${o.length} applied but no engine clears for any of them: ${(o[0].reasons||[]).find(i=>/engine|pinned|roster/.test(i))||"see the applications"}`:"no one in the pool applies to jobs",proposal:P(e),by:"fit"}}function P(e){const l=e?.needs||{};return{name:k(e?.title,60)||"New agent",purpose:`Does jobs like "${k(e?.title,80)}".`,skills:[...l.skills||[]],grants:(l.grants||[]).length?[...l.grants]:["none"]}}function ge(e,l,{by:t="evaluator"}={}){const s=e||P(l),o=new Set(["data","web","history","mcp",...A]),a=(s.grants||[]).map(r=>String(r).toLowerCase()).filter(r=>o.has(r)||/^mcp:/.test(r));return X({name:s.name,purpose:s.purpose,skills:s.skills,prompt:`You are ${s.name}. ${s.purpose||""}
|
|
3
3
|
|
|
4
4
|
You were proposed for the job "${k(l?.title,200)}" because no agent in the pool fit it. Do work like it well; ask on the thread when the brief is unclear.`,grants:a.length?a:["none"],engine:{kind:"auto",prefer:"balanced"},appliesTo:["jobs"],createdBy:t,origin:{kind:"proposal",projectId:l?.projectId,jobId:l?.id}})}function fe(e,l=null){if(e?.budget&&Object.keys(e.budget).length)return ie(e.budget);const t=l?.page?.budget||{},s=l?.spend||{},o=Math.max(1,(l?.jobs||[]).filter(r=>["open","evaluating"].includes(r.status)).length),a={};for(const r of["tokens","calls","ms","usd"]){if(!(Number(t[r])>0))continue;const i=Math.max(0,Number(t[r])-(Number(s[r])||0));i>0&&(a[r]=r==="usd"?Math.round(i/o*100)/100:Math.max(1,Math.floor(i/o)))}return Object.keys(a).length?a:null}function ye(e,l,t,{by:s="evaluator",at:o=Date.now(),record:a=null}={}){const r=[{type:"job.updated",at:o,job:{id:e.id,status:"evaluating",applications:(l||[]).map(({covers:i,...c})=>c)},by:s}];if(t?.kind==="recruit"){const i=fe(e,a);r.push({type:"job.updated",at:o,job:{id:e.id,status:"recruited",recruited:{agentId:t.agentId,engine:t.engine,...i?{budget:i}:{},by:t.by||s,at:o,why:k(t.why,600)}},by:s})}else{r.push({type:"job.updated",at:o,job:{id:e.id,status:"open"},by:s});const i=t?.proposal;r.push({type:"project.decision",at:o,by:s,kind:"proposal",text:`No one in the pool fits "${k(e.title,120)}": ${k(t?.why,400)}${i?` Proposed: ${i.name}${i.skills?.length?` — skills ${i.skills.join(", ")}`:""}${i.grants?.length?`; grants ${i.grants.join(", ")}`:""}.`:""}`,refs:[`job:${e.id}`]})}return r}async function me(e,l,{summaries:t={},rows:s=[],reach:o="any",chatModel:a=null,record:r=null,ask:i=null,minFit:c=q,by:n="evaluator",now:h=Date.now()}={}){const p=pe(e,l,{summaries:t,rows:s,reach:o,chatModel:a,now:h}),u=de(e,p,l,{rows:s});let g=null;if(i&&p.some(f=>f.engine))try{const f=await i(u,S);g=f==null?null:W(typeof f=="string"?f:JSON.stringify(f),p)}catch{g=null}const d=he(e,p,{evaluation:g,minFit:c});return{applications:p,evaluation:g,decision:d,events:ye(e,p,d,{by:d.by==="evaluator"?n:"fit",at:h,record:r}),prompt:u}}async function Te(e,l,{rows:t=[],summaries:s={},reach:o="any",chatModel:a=null,targetFor:r=null,ask:i=null,minFit:c=q,create:n=null,now:h=Date.now()}={}){const p=(Array.isArray(l)?l:[]).filter(b=>b&&b.id);if(n&&B(n))try{const b=Q(n);p.some(w=>w.id===b.id)||p.push(b)}catch{}const u=await me(e,p,{summaries:s,rows:t,reach:o,chatModel:a,ask:i,minFit:c,now:h}),g=u.decision;if(g.kind!=="recruit"){const b=ge(g.proposal||P(e),e,{by:g.by||"evaluator"});return{why:g.why,proposal:b?.ok===!1?null:b?.agent||b,applications:u.applications,events:u.events}}const d=p.find(b=>b.id===g.agentId);let y={...ee(e,d),agent:d.id,engine:g.engine};try{y=V({name:"recruit",roles:[y],budget:{tokens:1}},p,{chatModel:a,targetFor:r}).roles[0]}catch{}return{role:y,agentId:g.agentId,engine:g.engine,why:g.why,fit:g.fit??null,applications:u.applications,events:u.events}}export{ae as MAX_BRIEF_IN_PROMPT,q as MIN_FIT,S as RECRUIT_SCHEMA,oe as TOP_APPLICANTS,pe as applications,fe as carveBudget,he as decide,_ as engineRow,Ne as engineRows,ue as engineWorth,de as evaluatorPrompt,L as needForJob,W as parseEvaluation,P as proposalFromNeeds,ge as proposalToAgent,ye as recruitEvents,Te as recruitForRun,me as recruitJob,ce as routeFor};
|
package/router.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
class q extends Error{constructor(s,l){super(l),this.name="RouterError",this.code=s}}import{REACH as re,reachRank as ie}from"./reach.js";import{REACH as
|
|
2
|
-
`)||""),l=s.length,h=!!(t.images?.length||t.attachments?.some?.(a=>/^image\//.test(a?.type||""))),d=!!t.audio||!!t.attachments?.some?.(a=>/^audio\//.test(a?.type||"")),c=(t.attachments||[...t.messages||[]].reverse().find(a=>a?.role==="user")?.attachments||[]).filter(a=>a?.kind==="path");return{reference:c.length?{count:c.length,folders:c.filter(a=>a.isDir).length,documents:c.filter(a=>a.isDir===!1).length,pages:c.reduce((a,r)=>a+(Number(r.pages)||0),0)}:null,approxTokens:Math.round(l/4),modality:d?"audio":h?"vision":"text",code:/```/.test(s)||l>80&&/\bfunction\b|\bclass\b|=>|;\s*$|\bdef\b|\bimport\b|\bconst\b/m.test(s)||/\b(write|fix|debug|refactor|implement|optimi[sz]e|review|explain|generate|add|update|port|migrate)\b[^.?!]{0,60}\b(code|function|method|class|script|bug|regex|query|unit ?test|module|component|endpoint|snippet|compiler?|stack ?trace|typescript|javascript|python|rust|golang|sql|css|html)\b/i.test(s),complexity:l>4e3||/```/.test(s)||l>=200&&/\bstep by step\b|\bplan\b|\brefactor\b|\bmigrate\b|\banalyse|\banalyze/i.test(s)?"high":l<200?"low":"medium",nonLatin:/[^\u0000-\u024F\u2000-\u206F]/.test(s),smalltalk:l<100&&!/```/.test(s)&&!/\b(draw|click|open|fill|read|find|search|edit|write|create|update|delete|run|fix|change|add|remove|select|scroll|extract|summar\w*|analy\w*|check|review|list|show|go to|navigate|my|mine|this page|here|it|that)\b/i.test(s)&&!/\b(explain|describe|define|compare|translate|why|debug|refactor|implement|improve|optimi[sz]e|convert|calculate|generate|draft|rename|build|test|install|deploy|design|plan)\b/i.test(s)&&!/\bhow\s+(do|does|did|can|could|should|would|to|much|many|long)\b/i.test(s),chars:l}}const D=(t,s)=>`${t} ${s}${t===1?"":"s"}`;function B(t){const s=[];return t.folders&&s.push(D(t.folders,"folder")),t.documents&&s.push(`${D(t.documents,"document")}${t.pages?` (${t.pages} pages)`:""}`),s.length?s.join(" and "):D(t.count,"path")}function V(t={},{structured:s=!1,hasTools:l=!1,pageTools:h=!1,background:d=!1,minWindow:y=0}={}){const c=new Set;let a=0;const r=[];y>0&&r.push(`the turn needs a ${Math.round(y/1e3)}k window`),l&&(c.add("tools"),r.push("the turn carries tools")),t.modality==="vision"&&(c.add("vision"),r.push("the request includes an image")),h&&!t.smalltalk&&(c.add("tools"),a=Math.max(a,.55),r.push("driving a page needs exact actions")),t.approxTokens>2e4&&(c.add("long-context"),r.push("the request is large")),t.reference?.count&&(c.add("tools"),a=Math.max(a,.55),r.push(`${B(t.reference)} attached by reference — read through the files tool`)),t.code&&(c.add("coding"),r.push("the request contains code")),s&&!t.smalltalk&&(c.add("tools"),a=Math.max(a,.55),r.push("it must produce an exact structured payload")),t.complexity==="high"&&!d&&(a=Math.max(a,.55),r.push("the task is complex"));const i=t.smalltalk&&!t.reference?.count?[...c]:[...c].filter(n=>n!=="tools");return{required:[...c],negotiable:i,minQuality:a,why:r,minWindow:Math.max(0,Number(y)||0)}}function K(t={},{structured:s=!1,minQuality:l=0,hasTools:h=!1,background:d=!1}={}){return d?{prefer:"cost",why:"background work — spend as little as possible"}:s||t.complexity==="high"||t.code||t.modality==="vision"?{prefer:"quality",why:"the task is exact or hard — get it right rather than quick"}:t.smalltalk&&!h&&!l?{prefer:"latency",why:"nothing to look up — answer fast"}:{prefer:"balanced",why:"no reason to favour speed or quality"}}function O(t){return t?.orderPinned&&Number.isFinite(t.providerRank)?t.providerRank:1/0}const Q=.25,U=.25;function J(t=[],s={}){const l=i=>!!s.model&&S(i)===S(s);let h=[...t];if(s.reason==="gone"){const i=h.filter(n=>!l(n));i.length&&(h=i)}const d=i=>Number.isFinite(i.quality)?i.quality:.5,y=i=>(s.capabilities||[]).every(n=>(i.capabilities||[]).includes(n)),c=i=>!s.classUsed||i.classUsed===s.classUsed,a=Number.isFinite(s.quality)?s.quality:null;if(a!==null){const i=n=>l(n)?-1:Math.abs(d(n)-a)+(c(n)?0:Q)+(y(n)?0:U);return h.map(n=>({m:n,d:i(n)})).sort((n,p)=>n.d-p.d||O(n.m)-O(p.m)||n.m.costPer1k-p.m.costPer1k||String(n.m.id).localeCompare(String(p.m.id))).map(n=>n.m)}const r=i=>l(i)?0:c(i)&&y(i)?1:c(i)?2:y(i)?3:4;return h.sort((i,n)=>r(i)-r(n)||d(n)-d(i)||O(i)-O(n)||i.costPer1k-n.costPer1k)}function X(t,s={}){const l=String(t||""),h=String(s?.action||""),d=`${l}.${h}`;return/screenshot|marked_screenshot|read_canvas|sense_canvas/.test(d)?{required:["vision"],why:"this step reads an image"}:/structured_insert|sheet_write|fill_form|input_sequence|draw_path/.test(d)?{required:["tools"],minQuality:.55,why:"this step writes an exact payload"}:{required:l?["tools"]:[],why:""}}function Z({models:t=[],middleware:s=[],strategies:l=[],admit:h=null}={}){const d=[...t],y=[...s],c=[...l],a=r=>y.filter(i=>i.stage===r&&(!h||h(i))).sort((i,n)=>r==="request"?i.priority-n.priority:n.priority-i.priority);return{addModel(r){return d.push(r),()=>{const i=d.indexOf(r);i>=0&&d.splice(i,1)}},use(r){return y.push(r),()=>{const i=y.indexOf(r);i>=0&&y.splice(i,1)}},addStrategy(r){return c.push(r),()=>{const i=c.indexOf(r);i>=0&&c.splice(i,1)}},models:()=>[...d],middleware:()=>[...y],strategies:()=>[...c],route(r={}){const i=C.includes(r.reach)?r.reach:"any",n=r.capabilities||[],p=r.requirementReasons||[],u=[],g=d.filter(e=>{if(!e.available)return u.push({id:e.id,why:"unavailable"}),!1;if(h&&!h(e))return u.push({id:e.id,why:"disabled"}),!1;if(F(e.reach)>F(i))return u.push({id:e.id,why:`reach '${e.reach}' exceeds '${i}'`}),!1;const o=n.filter(k=>!e.capabilities.includes(k));if(o.length)return u.push({id:e.id,why:`missing ${o.join(", ")}`}),!1;if(r.minWindow>0&&Number(e.window)>0&&Number(e.window)<r.minWindow)return u.push({id:e.id,why:`a ${Math.round(e.window/1e3)}k window under the ${Math.round(r.minWindow/1e3)}k this turn needs`}),!1;if(r.maxLatencyMs>0&&e.latencyMs>r.maxLatencyMs)return u.push({id:e.id,why:`${e.latencyMs}ms exceeds the ${r.maxLatencyMs}ms deadline`}),!1;if(r.maxCostPer1k>=0&&r.maxCostPer1k!==void 0&&e.costPer1k>r.maxCostPer1k)return u.push({id:e.id,why:`costs ${e.costPer1k} over the ${r.maxCostPer1k} budget`}),!1;if(e.rateLimited)return u.push({id:e.id,why:"rate limited"}),!1;if(r.exclude?.includes?.(e.id))return u.push({id:e.id,why:"already failed this request"}),!1;if(r.minQuality>0){const k=Number.isFinite(e.quality)?e.quality:.5;if(k<r.minQuality)return u.push({id:e.id,why:`below the quality this task needs (${k} < ${r.minQuality})`}),!1}return!0});if(!g.length){if(r.minQuality>0){const e=this.route({...r,minQuality:0});if(e.model)return{...e,relaxed:!0,reasons:[...e.reasons,`no model met the quality this task needs (${r.minQuality}) — used the best available instead`]}}if(r.negotiable?.length){const e=(r.capabilities||[]).filter(k=>!r.negotiable.includes(k)),o=this.route({...r,minQuality:0,capabilities:e,negotiable:[]});if(o.model)return{...o,relaxed:!0,reasons:[...o.reasons,`no model offers ${r.negotiable.join(", ")} — used one without it, which may do this task poorly`]}}return{model:null,reasons:["no candidate satisfies the constraints"],constraints:p,rejected:u}}const b=r.prefer||"balanced",v=e=>{const o=1+Math.max(0,Math.min(1,e.load)),k=Number.isFinite(e.quality)?Math.max(.1,e.quality):.5,P=j[e.reach]??1;return b==="latency"?e.latencyMs*o*P:b==="cost"?e.costPer1k*o*P:b==="quality"?o/k:(e.latencyMs/1e3+e.costPer1k*L)*o*P/k},x=new Map,w=e=>(x.has(e)||x.set(e,v(e)),x.get(e)),$=(e,o)=>e.providerRank-o.providerRank||w(e)-w(o)||e.id.localeCompare(o.id),f=(e,o)=>w(e)-w(o)||e.providerRank-o.providerRank||e.id.localeCompare(o.id),m=new Map;for(const e of g){const o=S(e)||`#${e.id}`;m.has(o)||m.set(o,[]),m.get(o).push(e)}const _=[...m.values()].map(e=>[...e].sort(e.some(o=>o.orderPinned)?$:f));_.sort((e,o)=>w(e[0])-w(o[0])||$(e[0],o[0]));const R=[];for(const e of _){const o=R[R.length-1],k=o?w(o[0][0]):0;o&&w(e[0])<=k*(1+T)?o.push(e):R.push([e])}for(const e of R)e.sort((o,k)=>$(o[0],k[0]));let M=R.flat(2);const A=b==="balanced"?M.filter(e=>e.orderPinned).sort((e,o)=>e.providerRank-o.providerRank)[0]:null;A&&(M=[A,...M.filter(e=>e!==A)]);const N=R[0]?.length>1,I=(R[0]||[]).flat().length,E=M[0];return{model:E,eligible:M,constraints:p,strategy:A?"declared-default":"default-score",reasons:[`reach '${E.reach}' within '${i}'`,n.length?`has ${n.join(", ")}`:"no special capability needed",A?`your default (Order ${E.providerRank}) — nothing about this turn asked for anything else`:N?`order ${E.providerRank} broke a ${I}-way tie by ${b}, of ${M.length} eligible`:`best by ${b} (${M.length} eligible)`],rejected:u,runnersUp:M.slice(1).map(e=>e.id)}},async routeWith(r={},i={}){const n=this.route(r);if(!n.model||!c.length)return n;const p=new Map(n.eligible.map(u=>[u.id,u]));for(const u of c){if(h&&!h(u))continue;let g=null;try{const m=u.decide([...n.eligible],r,i);g=u.timeoutMs>0?await Promise.race([m,new Promise(_=>setTimeout(()=>_(null),u.timeoutMs))]):await m}catch{g=null}if(!g)continue;const b=Array.isArray(g)?g:Array.isArray(g?.ranked)?g.ranked:[g],v=Array.isArray(g?.why)?g.why.filter(m=>typeof m=="string"&&m):[],x=b.map(m=>typeof m=="string"?p.get(m):p.get(m?.id)).filter(Boolean),w=b.length-x.length;if(!x.length)continue;const $=n.eligible.filter(m=>!x.includes(m)),f=[...x,...$];return{...n,model:x[0],eligible:f,strategy:u.id,runnersUp:f.slice(1).map(m=>m.id),reasons:[...n.reasons.slice(0,-1),`chosen by '${u.id}' (class ${u.classUsed}) from ${n.eligible.length} eligible`,...v,...w>0?[`${w} suggestion(s) ignored — not eligible under the constraints`]:[]]}}return n},async run(r,{dispatch:i,need:n={}}={}){if(typeof i!="function")throw new q("NO_DISPATCH","run needs a dispatch function");const p=await this.routeWith(n,{request:r});if(!p.model)throw new q("NO_ROUTE",p.reasons[0]);const u=f=>!f.requiredFor||f.requiredFor(p.model,n),g=y.filter(f=>f.requiredFor&&f.requiredFor(p.model,n)),b=new Set(a("request").concat(a("response")).map(f=>f.id)),v=g.filter(f=>!b.has(f.id));if(v.length)throw new q("MISSING_REQUIRED",`route to '${p.model.id}' requires ${v.map(f=>f.id).join(", ")}, which is not active`);const x={model:p.model,need:n,decision:p};let w=r;for(const f of a("request"))u(f)&&(w=await f.run(w,x)??w);let $=await i(w,x);for(const f of a("response"))u(f)&&($=await f.run($,x)??$);return{answer:$,decision:p}}}}export{U as FAILOVER_CAPABILITY_GAP,Q as FAILOVER_CLASS_GAP,re as REACH,q as RouterError,Z as createModelRouter,G as defineMiddleware,W as defineModel,H as defineRouteStrategy,J as failoverOrder,O as pinnedOrderOf,K as preferenceFor,ie as reachRank,V as requirementsFor,X as requirementsForStep,S as sameModelKey,Y as signalsFrom};
|
|
1
|
+
class q extends Error{constructor(s,l){super(l),this.name="RouterError",this.code=s}}import{REACH as re,reachRank as ie}from"./reach.js";import{REACH as P,reachRank as F}from"./reach.js";function W({id:t,label:s,reach:l="any",classUsed:h="C",capabilities:d=[],costPer1k:y=1,latencyMs:c=1e3,load:a=0,available:r=!0,rateLimited:i=!1,observedLatencyMs:n=null,quality:p=null,model:u="",providerRank:w=50,orderPinned:x=!1,window:v=0,reachDetected:b="",reachDeclared:m=!1}){if(!t)throw new q("BAD_MODEL","model.id required");if(!P.includes(l))throw new q("BAD_MODEL",`model '${t}': unknown reach '${l}'`);return Object.freeze({id:t,label:s||t,reach:l,classUsed:h,reachDetected:P.includes(b)?b:l,reachDeclared:!!m,model:u,capabilities:[...d],costPer1k:y,latencyMs:Number.isFinite(n)?n:c,declaredLatencyMs:c,observedLatencyMs:n,load:a,available:r,rateLimited:i,providerRank:w,orderPinned:!!x,quality:p,window:Number(v)>0?Number(v):0})}function G({id:t,label:s,stage:l,priority:h=100,run:d,requiredFor:y=null}){if(!t)throw new q("BAD_MIDDLEWARE","middleware.id required");if(!["request","response"].includes(l))throw new q("BAD_MIDDLEWARE",`middleware '${t}': stage must be request or response`);if(typeof d!="function")throw new q("BAD_MIDDLEWARE",`middleware '${t}': run required`);return Object.freeze({id:t,label:s||t,stage:l,priority:h,run:d,requiredFor:y})}function H({id:t,label:s,classUsed:l="R",timeoutMs:h=0,decide:d}){if(!t)throw new q("BAD_STRATEGY","strategy.id required");if(typeof d!="function")throw new q("BAD_STRATEGY",`strategy '${t}': decide required`);return Object.freeze({id:t,label:s||t,classUsed:l,timeoutMs:h,decide:d})}const T=.1,L=5,j=Object.freeze({device:.7,trusted:.85,any:1});function S(t){return String(t?.model||t?.label||"").toLowerCase().replace(/^[^/]+\//,"").replace(/[:@].*$/,"").replace(/[^a-z0-9.]+/g,"")}function Y(t={}){const s=String(t.text||(t.messages||[]).map(a=>a?.content||"").join(`
|
|
2
|
+
`)||""),l=s.length,h=!!(t.images?.length||t.attachments?.some?.(a=>/^image\//.test(a?.type||""))),d=!!t.audio||!!t.attachments?.some?.(a=>/^audio\//.test(a?.type||"")),c=(t.attachments||[...t.messages||[]].reverse().find(a=>a?.role==="user")?.attachments||[]).filter(a=>a?.kind==="path");return{reference:c.length?{count:c.length,folders:c.filter(a=>a.isDir).length,documents:c.filter(a=>a.isDir===!1).length,pages:c.reduce((a,r)=>a+(Number(r.pages)||0),0)}:null,approxTokens:Math.round(l/4),modality:d?"audio":h?"vision":"text",code:/```/.test(s)||l>80&&/\bfunction\b|\bclass\b|=>|;\s*$|\bdef\b|\bimport\b|\bconst\b/m.test(s)||/\b(write|fix|debug|refactor|implement|optimi[sz]e|review|explain|generate|add|update|port|migrate)\b[^.?!]{0,60}\b(code|function|method|class|script|bug|regex|query|unit ?test|module|component|endpoint|snippet|compiler?|stack ?trace|typescript|javascript|python|rust|golang|sql|css|html)\b/i.test(s),complexity:l>4e3||/```/.test(s)||l>=200&&/\bstep by step\b|\bplan\b|\brefactor\b|\bmigrate\b|\banalyse|\banalyze/i.test(s)?"high":l<200?"low":"medium",nonLatin:/[^\u0000-\u024F\u2000-\u206F]/.test(s),smalltalk:l<100&&!/```/.test(s)&&!/\b(draw|click|open|fill|read|find|search|edit|write|create|update|delete|run|fix|change|add|remove|select|scroll|extract|summar\w*|analy\w*|check|review|list|show|go to|navigate|my|mine|this page|here|it|that)\b/i.test(s)&&!/\b(explain|describe|define|compare|translate|why|debug|refactor|implement|improve|optimi[sz]e|convert|calculate|generate|draft|rename|build|test|install|deploy|design|plan)\b/i.test(s)&&!/\bhow\s+(do|does|did|can|could|should|would|to|much|many|long)\b/i.test(s),chars:l}}const C=(t,s)=>`${t} ${s}${t===1?"":"s"}`;function B(t){const s=[];return t.folders&&s.push(C(t.folders,"folder")),t.documents&&s.push(`${C(t.documents,"document")}${t.pages?` (${t.pages} pages)`:""}`),s.length?s.join(" and "):C(t.count,"path")}function V(t={},{structured:s=!1,hasTools:l=!1,pageTools:h=!1,background:d=!1,minWindow:y=0}={}){const c=new Set;let a=0;const r=[];y>0&&r.push(`the turn needs a ${Math.round(y/1e3)}k window`),l&&(c.add("tools"),r.push("the turn carries tools")),t.modality==="vision"&&(c.add("vision"),r.push("the request includes an image")),h&&!t.smalltalk&&(c.add("tools"),a=Math.max(a,.55),r.push("driving a page needs exact actions")),t.approxTokens>2e4&&(c.add("long-context"),r.push("the request is large")),t.reference?.count&&(c.add("tools"),a=Math.max(a,.55),r.push(`${B(t.reference)} attached by reference — read through the files tool`)),t.code&&(c.add("coding"),r.push("the request contains code")),s&&!t.smalltalk&&(c.add("tools"),a=Math.max(a,.55),r.push("it must produce an exact structured payload")),t.complexity==="high"&&!d&&(a=Math.max(a,.55),r.push("the task is complex"));const i=t.smalltalk&&!t.reference?.count?[...c]:[...c].filter(n=>n!=="tools");return{required:[...c],negotiable:i,minQuality:a,why:r,minWindow:Math.max(0,Number(y)||0)}}function K(t={},{structured:s=!1,minQuality:l=0,hasTools:h=!1,background:d=!1}={}){return d?{prefer:"cost",why:"background work — spend as little as possible"}:s||t.complexity==="high"||t.code||t.modality==="vision"?{prefer:"quality",why:"the task is exact or hard — get it right rather than quick"}:t.smalltalk&&!h&&!l?{prefer:"latency",why:"nothing to look up — answer fast"}:{prefer:"balanced",why:"no reason to favour speed or quality"}}function D(t){return t?.orderPinned&&Number.isFinite(t.providerRank)?t.providerRank:1/0}const Q=.25,U=.25;function J(t=[],s={}){const l=i=>!!s.model&&S(i)===S(s);let h=[...t];if(s.reason==="gone"){const i=h.filter(n=>!l(n));i.length&&(h=i)}const d=i=>Number.isFinite(i.quality)?i.quality:.5,y=i=>(s.capabilities||[]).every(n=>(i.capabilities||[]).includes(n)),c=i=>!s.classUsed||i.classUsed===s.classUsed,a=Number.isFinite(s.quality)?s.quality:null;if(a!==null){const i=n=>l(n)?-1:Math.abs(d(n)-a)+(c(n)?0:Q)+(y(n)?0:U);return h.map(n=>({m:n,d:i(n)})).sort((n,p)=>n.d-p.d||D(n.m)-D(p.m)||n.m.costPer1k-p.m.costPer1k||String(n.m.id).localeCompare(String(p.m.id))).map(n=>n.m)}const r=i=>l(i)?0:c(i)&&y(i)?1:c(i)?2:y(i)?3:4;return h.sort((i,n)=>r(i)-r(n)||d(n)-d(i)||D(i)-D(n)||i.costPer1k-n.costPer1k)}function X(t,s={}){const l=String(t||""),h=String(s?.action||""),d=`${l}.${h}`;return/screenshot|marked_screenshot|read_canvas|sense_canvas/.test(d)?{required:["vision"],why:"this step reads an image"}:/structured_insert|sheet_write|fill_form|input_sequence|draw_path/.test(d)?{required:["tools"],minQuality:.55,why:"this step writes an exact payload"}:{required:l?["tools"]:[],why:""}}function Z({models:t=[],middleware:s=[],strategies:l=[],admit:h=null}={}){const d=[...t],y=[...s],c=[...l],a=r=>y.filter(i=>i.stage===r&&(!h||h(i))).sort((i,n)=>r==="request"?i.priority-n.priority:n.priority-i.priority);return{addModel(r){return d.push(r),()=>{const i=d.indexOf(r);i>=0&&d.splice(i,1)}},use(r){return y.push(r),()=>{const i=y.indexOf(r);i>=0&&y.splice(i,1)}},addStrategy(r){return c.push(r),()=>{const i=c.indexOf(r);i>=0&&c.splice(i,1)}},models:()=>[...d],middleware:()=>[...y],strategies:()=>[...c],route(r={}){const i=P.includes(r.reach)?r.reach:"any",n=r.capabilities||[],p=r.requirementReasons||[],u=[],w=d.filter(e=>{if(!e.available)return u.push({id:e.id,why:"unavailable"}),!1;if(h&&!h(e))return u.push({id:e.id,why:"disabled"}),!1;if(F(e.reach)>F(i))return u.push({id:e.id,why:`reach '${e.reach}' exceeds '${i}'`}),!1;const o=n.filter(k=>!e.capabilities.includes(k));if(o.length)return u.push({id:e.id,why:`missing ${o.join(", ")}`}),!1;if(r.minWindow>0&&Number(e.window)>0&&Number(e.window)<r.minWindow)return u.push({id:e.id,why:`a ${Math.round(e.window/1e3)}k window under the ${Math.round(r.minWindow/1e3)}k this turn needs`}),!1;if(r.maxLatencyMs>0&&e.latencyMs>r.maxLatencyMs)return u.push({id:e.id,why:`${e.latencyMs}ms exceeds the ${r.maxLatencyMs}ms deadline`}),!1;if(r.maxCostPer1k>=0&&r.maxCostPer1k!==void 0&&e.costPer1k>r.maxCostPer1k)return u.push({id:e.id,why:`costs ${e.costPer1k} over the ${r.maxCostPer1k} budget`}),!1;if(e.rateLimited)return u.push({id:e.id,why:"rate limited"}),!1;if(r.exclude?.includes?.(e.id))return u.push({id:e.id,why:"already failed this request"}),!1;if(r.minQuality>0){const k=Number.isFinite(e.quality)?e.quality:.5;if(k<r.minQuality)return u.push({id:e.id,why:`below the quality this task needs (${k} < ${r.minQuality})`}),!1}return!0});if(!w.length){if(r.minQuality>0){const e=this.route({...r,minQuality:0});if(e.model)return{...e,relaxed:!0,reasons:[...e.reasons,`no model met the quality this task needs (${r.minQuality}) — used the best available instead`]}}if(r.negotiable?.length){const e=(r.capabilities||[]).filter(k=>!r.negotiable.includes(k)),o=this.route({...r,minQuality:0,capabilities:e,negotiable:[]});if(o.model)return{...o,relaxed:!0,reasons:[...o.reasons,`no model offers ${r.negotiable.join(", ")} — used one without it, which may do this task poorly`]}}return{model:null,reasons:["no candidate satisfies the constraints"],constraints:p,rejected:u}}const x=r.prefer||"balanced",v=e=>{const o=1+Math.max(0,Math.min(1,e.load)),k=Number.isFinite(e.quality)?Math.max(.1,e.quality):.5,O=j[e.reach]??1;return x==="latency"?e.latencyMs*o*O:x==="cost"?e.costPer1k*o*O:x==="quality"?o/k:(e.latencyMs/1e3+e.costPer1k*L)*o*O/k},b=new Map,m=e=>(b.has(e)||b.set(e,v(e)),b.get(e)),$=(e,o)=>e.providerRank-o.providerRank||m(e)-m(o)||e.id.localeCompare(o.id),f=(e,o)=>m(e)-m(o)||e.providerRank-o.providerRank||e.id.localeCompare(o.id),g=new Map;for(const e of w){const o=S(e)||`#${e.id}`;g.has(o)||g.set(o,[]),g.get(o).push(e)}const _=[...g.values()].map(e=>[...e].sort(e.some(o=>o.orderPinned)?$:f));_.sort((e,o)=>m(e[0])-m(o[0])||$(e[0],o[0]));const R=[];for(const e of _){const o=R[R.length-1],k=o?m(o[0][0]):0;o&&m(e[0])<=k*(1+T)?o.push(e):R.push([e])}for(const e of R)e.sort((o,k)=>$(o[0],k[0]));let M=R.flat(2);const A=x==="balanced"?M.filter(e=>e.orderPinned).sort((e,o)=>e.providerRank-o.providerRank)[0]:null;A&&(M=[A,...M.filter(e=>e!==A)]);const N=R[0]?.length>1,I=(R[0]||[]).flat().length,E=M[0];return{model:E,eligible:M,constraints:p,strategy:A?"declared-default":"default-score",reasons:[`reach '${E.reach}' within '${i}'`,n.length?`has ${n.join(", ")}`:"no special capability needed",A?`your default (Order ${E.providerRank}) — nothing about this turn asked for anything else`:N?`order ${E.providerRank} broke a ${I}-way tie by ${x}, of ${M.length} eligible`:`best by ${x} (${M.length} eligible)`],rejected:u,runnersUp:M.slice(1).map(e=>e.id)}},async routeWith(r={},i={}){const n=this.route(r);if(!n.model||!c.length)return n;const p=new Map(n.eligible.map(u=>[u.id,u]));for(const u of c){if(h&&!h(u))continue;let w=null;try{const g=u.decide([...n.eligible],r,i);w=u.timeoutMs>0?await Promise.race([g,new Promise(_=>setTimeout(()=>_(null),u.timeoutMs))]):await g}catch{w=null}if(!w)continue;const x=Array.isArray(w)?w:Array.isArray(w?.ranked)?w.ranked:[w],v=Array.isArray(w?.why)?w.why.filter(g=>typeof g=="string"&&g):[],b=x.map(g=>typeof g=="string"?p.get(g):p.get(g?.id)).filter(Boolean),m=x.length-b.length;if(!b.length)continue;const $=n.eligible.filter(g=>!b.includes(g)),f=[...b,...$];return{...n,model:b[0],eligible:f,strategy:u.id,runnersUp:f.slice(1).map(g=>g.id),reasons:[...n.reasons.slice(0,-1),`chosen by '${u.id}' (class ${u.classUsed}) from ${n.eligible.length} eligible`,...v,...m>0?[`${m} suggestion(s) ignored — not eligible under the constraints`]:[]]}}return n},async run(r,{dispatch:i,need:n={}}={}){if(typeof i!="function")throw new q("NO_DISPATCH","run needs a dispatch function");const p=await this.routeWith(n,{request:r});if(!p.model)throw new q("NO_ROUTE",p.reasons[0]);const u=f=>!f.requiredFor||f.requiredFor(p.model,n),w=y.filter(f=>f.requiredFor&&f.requiredFor(p.model,n)),x=new Set(a("request").concat(a("response")).map(f=>f.id)),v=w.filter(f=>!x.has(f.id));if(v.length)throw new q("MISSING_REQUIRED",`route to '${p.model.id}' requires ${v.map(f=>f.id).join(", ")}, which is not active`);const b={model:p.model,need:n,decision:p};let m=r;for(const f of a("request"))u(f)&&(m=await f.run(m,b)??m);let $=await i(m,b);for(const f of a("response"))u(f)&&($=await f.run($,b)??$);return{answer:$,decision:p}}}}export{U as FAILOVER_CAPABILITY_GAP,Q as FAILOVER_CLASS_GAP,re as REACH,q as RouterError,Z as createModelRouter,G as defineMiddleware,W as defineModel,H as defineRouteStrategy,J as failoverOrder,D as pinnedOrderOf,K as preferenceFor,ie as reachRank,V as requirementsFor,X as requirementsForStep,S as sameModelKey,Y as signalsFrom};
|