@chatpanel/events 0.332.0 → 0.354.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/answer-grounding.js +5 -0
- package/capability-endpoints.js +1 -1
- package/client-prefs.js +1 -1
- package/context-attachments.js +4 -4
- package/context-breakdown.js +5 -0
- package/context-window.js +1 -1
- package/engine-pacer.js +1 -0
- package/housekeeping.js +1 -1
- package/index.js +1 -1
- package/install-advice.js +3 -0
- package/local-engine.js +1 -0
- package/mcp-client.js +4 -4
- package/mcp-signin.js +1 -1
- package/model-candidates.js +1 -1
- package/notes-grounding.js +5 -5
- package/package.json +27 -1
- package/plan-executor.js +1 -1
- package/plan.js +1 -1
- package/prefetch.js +3 -3
- package/question-plan.js +11 -0
- package/retrieval-plan.js +2 -1
- package/search-api.js +1 -0
- package/search-engines.js +1 -1
- package/search-modes.js +7 -7
- package/search-outcome.js +1 -0
- package/settings-registry.js +1 -1
- package/team-trail.js +2 -2
- package/titles.js +4 -4
- package/tool-hub.js +10 -10
- package/tool-need.js +2 -2
- package/tool-round.js +1 -1
- package/turn-events.js +1 -0
- package/turn-flow.js +2 -0
- package/turn-gather.js +1 -0
- package/turn-intent.js +3 -3
- package/turn-loop.js +4 -4
- package/turn-pipeline.js +4 -0
- package/turn-replay.js +1 -0
- package/web-search-tool.js +8 -8
- package/web-search.js +1 -1
package/retrieval-plan.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{needsWhole as
|
|
1
|
+
import{needsWhole as R}from"./digest.js";import{lookupSubject as q,prefetchPlan as E,answeredByPrefetch as C}from"./prefetch.js";import{partChars as P,STITCH_LIMITS as _}from"./stitch.js";import{isSmallWindow as b,FIT_HEADROOM as D,SMALL_WINDOW_ROUNDS as M}from"./context-window.js";import{isPleasantry as W}from"./tool-need.js";const F=4,L={device:3,api:2,agent:8};function Z({question:n="",sources:t=[],fixedTokens:r=0,target:o={}}={}){const a=o.kind||"api",s=Number(o.window)||0,i=b(s),d=R(n),g=Math.max(1,Number(o.concurrency)||(a==="device"?1:4)),k=Number(o.secondsPerCall)||L[a]||2,h=(Array.isArray(t)?t:[]).filter(Boolean).map(e=>({...e,chars:Math.max(0,Number(e.chars)||0)})),m=s?Math.floor(s*D)-(Number(o.reserve)||1024)-(Number(r)||0):1/0,y=Number.isFinite(m)?Math.max(0,m)*F:1/0,f=new Map,c=new Set;let u=y;for(let e=!0;e;){e=!1;const l=h.filter(w=>!c.has(w.id)),p=l.length?u/l.length:0;for(const w of l)w.chars<=p&&(c.add(w.id),u-=w.chars,e=!0)}const v=h.filter(e=>!c.has(e.id)).reduce((e,l)=>e+l.chars,0);for(const e of h)f.set(e.id,c.has(e.id)?e.chars:Math.floor(u*e.chars/(v||1)));const x=P({windowTokens:s,reserveTokens:450})||0,S=h.map(e=>{const l={id:e.id,kind:e.kind,title:e.title||"",chars:e.chars,share:f.get(e.id)};if(a==="agent")return{...l,how:"agent",calls:0,why:"an agent reads it itself — handed the material, not a digest"};if(c.has(e.id)||!Number.isFinite(y))return{...l,how:"whole",calls:0,why:"it fits"};if(!i)return{...l,how:"merge",calls:Math.ceil(e.chars/Math.max(1,l.share)),why:"a large window: what does not fit is merged by the model"};if(d&&x>0){const p=Math.min(_.parts,Math.ceil(e.chars/x)),w=Math.ceil(e.chars/x),O=Math.max(1,Math.ceil(p/4));return{...l,how:"digest",parts:p,calls:p+O,unread:Math.max(0,w-p),why:"the question needs all of it, and it does not fit: read in parts, the notes combined"}}return{...l,how:"cut",calls:0,why:d?"no room to read it in parts: the parts that bear on the question":"a focused question: the parts that bear on it, by relevance"}}),T=h.some(e=>e.prefetched),I=a==="agent"?{offer:!0,rounds:null,why:"an agent keeps its own tools"}:i&&T?{offer:!1,rounds:1,why:"a small window, and a prefetch already answered"}:i?{offer:!0,rounds:M,why:"a small window: one tool round, then the answer"}:{offer:!0,rounds:null,why:""},A=S.reduce((e,l)=>e+(l.calls||0),0),$=Math.round((Math.ceil(A/g)+1)*k);return{sources:S,tools:I,estimate:{calls:A+1,seconds:$},say:j(S,$)}}function j(n,t){const r=n.filter(i=>i.how==="digest");if(!r.length)return"";const o=r.length===1?`"${r[0].title||"what is attached"}"`:`${r.length} attachments`,a=r.reduce((i,d)=>i+d.parts,0),s=r.reduce((i,d)=>i+(d.unread||0),0);return`Reading all of ${o} in ${a} part${a===1?"":"s"} first, so the answer covers the whole of it${s?` (the last ${s} part${s===1?"":"s"} will not fit)`:""} — about ${t} s.`}const H=/\b(?:schedule|reschedule|remind(?:er)?|calendar|invite|e-?mail|inbox|send|reply|draft|create|add|save|write (?:a |an )?note|open|click|fill|book|order|cancel|delete|rename|move|upload|download|run|execute|deploy|commit|share|post)\b/i,B=/\b(?:find out|find more|more (?:info|information|details)|look into|latest|current(?:ly)?|today|tonight|tomorrow|right now|this (?:week|weekend|month|year)|price|prices|cost|stock|shares?|score|news|headlines?|weather|forecast|hours|near me|nearby|directions|traffic|flight|look (?:it |that )?up|search|google|website|link|url|https?:\/\/|who(?:'s| is| was| are)|where(?:'s| is| are)|when(?:'s| is| does| do))\b/i,U=/\b(?:my|mine|our|ours|we|us|did i|have i|i (?:said|asked|wrote|told|had|was)|remember|last time|earlier today|meetings?|notes?|transcripts?|conversations?|chats?)\b/i;function K(n){const t=String(n||"").trim();return t?H.test(t)||B.test(t)||U.test(t)||!!q(t):!1}const N=new Set(["weather","stock","news","page","web","research"]);function ee(n=[]){return(Array.isArray(n)?n:[]).some(t=>t?.kind==="prefetch"&&N.has(t.prefetch)&&String(t.text||"").trim())}function te(n=[]){return(Array.isArray(n)?n:[]).some(t=>t&&N.has(t.kind))}function Y({window:n=0,question:t="",answered:r=!1,decided:o=""}={}){return b(n)?r?{offer:!1,rounds:1,why:"a small window, and a prefetch already answered"}:o==="none"?{offer:!1,rounds:1,why:"a small window, and the decider found nothing to look up"}:o?{offer:!0,rounds:M,why:`a small window, the decider asked for ${o}, and the fetch found nothing`}:K(t)?{offer:!0,rounds:M,why:"a small window: one tool round, then the answer"}:{offer:!1,rounds:1,why:"a small window, and a question that needs no tools"}:{offer:!0,rounds:null,why:""}}function re({window:n=0,question:t="",answered:r=!1}={}){if(!b(n))return{history:"all",memories:"all",before:!1,why:""};const o=W(t);return{history:r?"none":"all",memories:o?"none":r?"pinned":"matched",before:!0,why:[r?"a small window a fetch answered: the evidence, the question and pinned memories":"",o?"a greeting: no memories":r?"":"a small window: only the memories that bear on it"].filter(Boolean).join("; ")}}const z="From the user's own history. It may have nothing to do with the question: use it only if it answers it, otherwise answer from your own knowledge.";function ne(n=[],t={}){const r=(Array.isArray(n)?n:[]).filter(o=>o&&typeof o=="object");return t?.before?r.map(o=>({...o,before:!0,text:`${z}
|
|
2
|
+
${String(o.text||"")}`})):r}function oe(n=[],t=""){const r=String(t||"").trim();if(!r)return n;const o=/^(?:here|near me|nearby|my (?:location|area|city))$/i;return(Array.isArray(n)?n:[]).map(a=>{const s=a?.input?.args||a?.input;if(!s)return a;if(a.kind==="weather"&&o.test(String(s.location||"").trim())){const i={...s,location:r};return{...a,input:a.input.args?{...a.input,args:i}:i,label:`Weather — ${r}`}}if(typeof s.query=="string"&&/\bnear (?:me|here)\b|\bnearby\b|\baround here\b/i.test(s.query)){const i=s.query.replace(/\bnear (?:me|here)\b|\bnearby\b|\baround here\b/i,`near ${r}`);return{...a,input:a.input.args?{...a.input,args:{...s,query:i}}:{...s,query:i},subject:i,label:String(a.label||"").replace(/\bnear (?:me|here)\b|\bnearby\b|\baround here\b/i,`near ${r}`)}}return a})}function ae({question:n="",previousText:t="",previousAnswer:r="",previous:o=null,reach:a=new Set,window:s=0,personal:i=!1,lookup:d=null,now:g=Date.now()}={}){const k=b(s),h=E({text:n,previous:o,previousText:t,previousAnswer:r,reach:a,research:!0,page:!0,lookup:d??k,now:g}),m=h.some(u=>["weather","stock","news","page","web","research"].includes(u.kind)),y=!!i&&!m,f=[];h.length&&f.push(`fetch first: ${h.map(u=>u.label||u.kind).join(", ")}`),i&&m&&f.push("a world fact — the person's history is not searched for it");const c=Y({window:s,question:n,answered:h.length>0});return c.why&&f.push(c.why),{prefetch:h,world:m,research:y,tools:c,why:f}}export{z as HISTORY_NOTE,C as answeredByPrefetch,re as contextPlan,ne as historyContext,oe as placeItems,Z as planRetrieval,ae as planTurn,Y as toolPlan,K as toolsWanted,ee as worldAnswered,te as worldPlanned};
|
package/search-api.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class u extends Error{constructor(r,s){super(s),this.name="SearchApiError",this.code=r}}const y=Object.freeze({exa:{name:"Exa",needsKey:!0,vars:["key"],docs:"https://docs.exa.ai/reference/search",about:"Finds pages by what they mean and returns their text.",urls:{key:"https://dashboard.exa.ai/api-keys"},request:{method:"POST",url:"https://api.exa.ai/search",headers:{"x-api-key":"{key}"},body:{query:"{q}",numResults:"{limit}",contents:{text:{maxCharacters:4e3}}}},response:{results:"results",url:"url",title:"title",snippet:["summary","highlights"],text:"text",published:"publishedDate"}},brave:{name:"Brave Search API",needsKey:!0,vars:["key"],docs:"https://api-dashboard.search.brave.com/app/documentation/web-search",about:"Brave's own index of the web.",urls:{key:"https://api-dashboard.search.brave.com/app/keys"},request:{method:"GET",url:"https://api.search.brave.com/res/v1/web/search?q={q}&count={limit}",headers:{"X-Subscription-Token":"{key}"}},response:{results:"web.results",url:"url",title:"title",snippet:"description",published:"page_age"}},tavily:{name:"Tavily",needsKey:!0,vars:["key"],docs:"https://docs.tavily.com/documentation/api-reference/endpoint/search",about:"Web search made for AI assistants.",urls:{key:"https://app.tavily.com/home"},request:{method:"POST",url:"https://api.tavily.com/search",headers:{Authorization:"Bearer {key}"},body:{query:"{q}",max_results:"{limit}"}},response:{results:"results",url:"url",title:"title",snippet:"content",text:"raw_content",published:"published_date"}},serper:{name:"Serper (Google results)",needsKey:!0,vars:["key"],docs:"https://serper.dev",about:"Google's results, through serper.dev.",urls:{key:"https://serper.dev"},request:{method:"POST",url:"https://google.serper.dev/search",headers:{"X-API-KEY":"{key}"},body:{q:"{q}",num:"{limit}"}},response:{results:"organic",url:"link",title:"title",snippet:"snippet",published:"date"}},serpapi:{name:"SerpApi (Google results)",needsKey:!0,vars:["key"],docs:"https://serpapi.com/search-api",about:"Google's results, through serpapi.com.",urls:{key:"https://serpapi.com/manage-api-key"},request:{method:"GET",url:"https://serpapi.com/search.json?engine=google&q={q}&num={limit}&api_key={key}"},response:{results:"organic_results",url:"link",title:"title",snippet:"snippet",published:"date"}},google:{name:"Google Programmable Search",needsKey:!0,vars:["key","cx"],maxLimit:10,docs:"https://developers.google.com/custom-search/v1/overview",about:"Google's own search API. Needs a key and a search engine ID.",urls:{key:"https://developers.google.com/custom-search/v1/introduction",cx:"https://programmablesearchengine.google.com/controlpanel/all"},request:{method:"GET",url:"https://www.googleapis.com/customsearch/v1?key={key}&cx={cx}&q={q}&num={limit}"},response:{results:"items",url:"link",title:"title",snippet:"snippet"}},jina:{name:"Jina Search",needsKey:!0,vars:["key"],docs:"https://jina.ai/reader",about:"Web search that returns each page as clean text.",urls:{key:"https://jina.ai/api-dashboard/key-manager"},request:{method:"GET",url:"https://s.jina.ai/?q={q}",headers:{Authorization:"Bearer {key}","X-Respond-With":"no-content"}},response:{results:"data",url:"url",title:"title",snippet:"description",text:"content",published:"date"}},mwmbl:{name:"Mwmbl",needsKey:!1,vars:[],docs:"https://mwmbl.org",about:"A small non-profit index of the web. No key needed.",request:{method:"GET",url:"https://api.mwmbl.org/api/v1/search/?s={q}"},response:{results:"",url:"url",title:"title",snippet:"extract"}},wikipedia:{name:"Wikipedia",needsKey:!1,vars:[],docs:"https://api.wikimedia.org/wiki/Core_REST_API",about:"Encyclopedia articles: a person, a company, a thing. No key needed.",request:{method:"GET",url:"https://en.wikipedia.org/w/rest.php/v1/search/page?q={q}&limit={limit}"},response:{results:"pages",url:{template:"https://en.wikipedia.org/wiki/{key}"},title:"title",snippet:["description","excerpt"]}}}),P=["GET","POST"],m=/\{([a-z][a-z0-9_]*)\}/gi,q=20;function b(e={}){const r=e.preset?y[e.preset]:null;if(e.preset&&!r)throw new u("UNKNOWN_PRESET",`no search API preset '${e.preset}'`);const s={...r?.request||{},...e.request||{}},n={...r?.response||{},...e.response||{}},t=String(e.id||e.preset||"").trim();if(!/^[a-z0-9][a-z0-9_-]{0,39}$/i.test(t))throw new u("BAD_API","search API id: letters, digits, - and _");const a=String(s.method||"GET").toUpperCase();if(!P.includes(a))throw new u("BAD_API",`search API '${t}': method must be GET or POST`);const i=String(s.url||"").trim();if(!/^https:\/\//i.test(i))throw new u("BAD_API",`search API '${t}': url must start with https://`);const o=i.slice(0,i.indexOf("/",8)===-1?i.length:i.indexOf("/",8));if(/[{}]/.test(o)||/[?#@]/.test(o))throw new u("BAD_API",`search API '${t}': the host must be written out — no placeholder or credentials in it`);const l=JSON.stringify([i,s.headers||{},s.body??null]);if(!/\{q\}/.test(l))throw new u("BAD_API",`search API '${t}': the request must carry the query as {q}`);if(n.results==null||!n.url||!n.title)throw new u("BAD_API",`search API '${t}': say where the results are, and their url and title`);const p=[...new Set([...r?.vars||[],...[...l.matchAll(m)].map(c=>c[1]).filter(c=>c!=="q"&&c!=="limit")])];return Object.freeze({id:t,preset:e.preset||"",name:String(e.name||r?.name||t),needsKey:e.needsKey??r?.needsKey??p.includes("key"),vars:p,maxLimit:Math.min(q,Number(e.maxLimit||r?.maxLimit||q)),request:Object.freeze({method:a,url:i,headers:{...s.headers||{}},body:s.body??null}),response:Object.freeze(n),host:new URL(i.replace(m,"x")).hostname})}function O(){return Object.entries(y).map(([e,r])=>({id:e,name:r.name,about:r.about||"",needsKey:!!r.needsKey,vars:[...r.vars||[]],urls:{...r.urls||{}},docs:r.docs||"",host:new URL(r.request.url.replace(m,"x")).hostname})).sort((e,r)=>Number(r.needsKey)-Number(e.needsKey))}function j(e={},{name:r=""}={}){const s=String(r||"").trim()||"The API",n=Array.isArray(e?.results)?e.results.length:0;if(n)return{ok:!0,message:`It works: ${n} result${n===1?"":"s"} came back.`};if(e?.timedOut)return{ok:!1,message:`${s} did not answer in time. Try again.`};if(e?.network)return{ok:!1,message:`${s} could not be reached. Check the connection.`};const t=Number(e?.status)||0;return t===401||t===403?{ok:!1,message:"The key was not accepted. Check that all of it was copied."}:t===402?{ok:!1,message:`The key was accepted, but the account at ${s} has no credit left.`}:t===429?{ok:!1,message:`${s} is refusing requests right now: too many, or the plan's limit. The key may still be right.`}:t>=400?{ok:!1,message:`${s} refused the request (${t}). Check each value.`}:{ok:!1,message:`${s} answered, but no results could be read from the answer.`}}function g(e,r={}){return e.vars.filter(s=>!String(r[s]??"").trim())}function f(e,r,s){return String(e).replace(m,(n,t)=>t in r?s?encodeURIComponent(r[t]):String(r[t]):n)}function A(e,r){if(typeof e=="string"){const s=e.match(/^\{([a-z][a-z0-9_]*)\}$/i);return s&&s[1]==="limit"?Number(r.limit):f(e,r,!1)}return Array.isArray(e)?e.map(s=>A(s,r)):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([s,n])=>[s,A(n,r)])):e}function K(e,{q:r,limit:s=8,vars:n={},site:t=""}={},{assertFetchable:a}={}){const i=String(t?`site:${t} ${r}`:r||"").trim();if(!i)throw new u("NO_QUERY","nothing to search for");const o=g(e,n);if(o.length)throw new u("MISSING_VARS",`search API '${e.id}' needs ${o.join(", ")}`);const l={...Object.fromEntries(e.vars.map(d=>[d,String(n[d])])),q:i,limit:Math.max(1,Math.min(e.maxLimit,Number(s)||8))},p=f(e.request.url,l,!0);if(new URL(p).hostname!==e.host)throw new u("BAD_API",`search API '${e.id}': the request left its host`);typeof a=="function"&&a(p);const c={Accept:"application/json",...Object.fromEntries(Object.entries(e.request.headers).map(([d,I])=>[d,f(I,l,!1)]))};let k=null;return e.request.method==="POST"&&(k=JSON.stringify(A(e.request.body??{},l)),c["Content-Type"]=c["Content-Type"]||"application/json"),{url:p,method:e.request.method,headers:c,body:k}}function v(e,r){return r?String(r).split(".").reduce((s,n)=>s?.[n],e):e}function h(e,r){if(!r)return"";if(Array.isArray(r)){for(const n of r){const t=h(e,n);if(t)return t}return""}if(typeof r=="object"&&r.template)return f(r.template,new Proxy({},{has:(n,t)=>e?.[t]!=null,get:(n,t)=>e?.[t]}),!0);const s=v(e,r);return Array.isArray(s)?s.map(n=>n&&typeof n=="object"?n.value??n.text??"":n).join(s.every(n=>typeof n=="object")?"":" "):s==null||typeof s=="object"?"":String(s)}const x=e=>String(e||"").replace(/<[^>]*>/g,"").replace(/"/g,'"').replace(/�?39;/g,"'").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\s+/g," ").trim();function $(e,r,{limit:s=8}={}){const n=v(r,e.response.results);if(!Array.isArray(n))return[];const t=[],a=new Set;for(const i of n){const o=h(i,e.response.url).trim();if(!/^https?:\/\/[^\s]+$/i.test(o)||a.has(o))continue;a.add(o);const l={url:o,title:x(h(i,e.response.title))||o,snippet:x(h(i,e.response.snippet)).slice(0,500)},p=h(i,e.response.text);p&&(l.text=p.slice(0,2e4));const c=h(i,e.response.published);if(c&&(l.published=c),t.push(l),t.length>=s)break}return t}function S(e,{enabled:r=!0}={}){return Object.freeze({id:`api:${e.id}`,name:e.name,kind:"api",url:e.request.url,needsKey:e.needsKey,enabled:r,api:e})}const E=Object.freeze([S(b({preset:"mwmbl"}))]),_=["id","preset","name","request","response","needsKey","maxLimit"],w=e=>E.some(r=>r.api.id===e?.id)&&!e?.request&&!e?.preset;function T(e=[]){const r=Array.isArray(e)?e:[],s=new Set(r.filter(a=>a?.enabled===!1).map(a=>a.id)),n=[];for(const a of r){if(!a||w(a))continue;let i;try{i=b(a)}catch{continue}const o=a.vars||{};n.push({...S(i,{enabled:a.enabled!==!1&&!g(i,o).length}),vars:o})}return[...E.filter(a=>!s.has(a.api.id)&&!n.some(i=>i.api.id===a.api.id)),...n]}function N(e=[],r=[]){if(!Array.isArray(r))throw new u("BAD_API","search APIs: a list of edits");const s=(Array.isArray(e)?e:[]).map(n=>({...n,vars:{...n?.vars||{}}}));for(const n of r){if(!n||typeof n!="object"||!n.id)throw new u("BAD_API","each search API needs an id");const t=s.findIndex(o=>o.id===n.id);if(n.remove===!0){t>=0&&s.splice(t,1);continue}const a=t>=0?s[t]:{},i={...a,enabled:n.enabled??a.enabled??!0,vars:{...a.vars||{}}};for(const o of _)o in n&&(i[o]=n[o]);for(const[o,l]of Object.entries(n.vars||{}))l===null||l===""?delete i.vars[o]:typeof l=="string"&&(i.vars[o]=l.trim());w(i)||b(i),t>=0?s[t]=i:s.push(i)}return s}function G(e=[]){const r=Array.isArray(e)?e:[],s=T(r).map(t=>({id:t.api.id,preset:t.api.preset,name:t.api.name,host:t.api.host,needsKey:t.api.needsKey,enabled:t.enabled,custom:!t.api.preset,vars:Object.fromEntries(t.api.vars.map(a=>[a,!!String(t.vars?.[a]??"").trim()])),missing:g(t.api,t.vars||{}),request:{method:t.api.request.method,url:t.api.request.url,headers:Object.keys(t.api.request.headers)},response:t.api.response})),n=r.filter(t=>t?.enabled===!1&&w(t)).map(t=>({id:t.id,enabled:!1,preset:t.id,name:y[t.id]?.name||t.id,missing:[]}));return[...s,...n]}export{E as DEFAULT_API_ENGINES,y as SEARCH_API_PRESETS,u as SearchApiError,S as apiEngine,T as apiEnginesFrom,j as apiTrialVerdict,K as buildApiRequest,b as defineSearchApi,g as missingVars,N as patchApiEntries,$ as readApiResponse,O as searchApiChoices,G as viewApiEntries};
|
package/search-engines.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class c extends Error{constructor(t,n){super(n),this.name="SearchEngineError",this.code=t}}const E=Object.freeze(["serp","api","gateway"]);function p({id:r,name:t,url:n,kind:i="serp",enabled:o=!1,needsKey:a=!1,retired:e=!1}){if(!r)throw new c("BAD_ENGINE","engine.id required");if(!E.includes(i))throw new c("BAD_ENGINE",`engine '${r}': unknown kind '${i}'`);if(!e&&!n&&i!=="gateway")throw new c("BAD_ENGINE",`engine '${r}': url required`);return Object.freeze({id:r,name:t||r,url:i==="gateway"?"":n,kind:i,enabled:o,needsKey:a,retired:e})}const w=p({id:"gateway",name:"Your gateway (SearXNG when it runs there)",url:"",kind:"gateway"}),g=2,d=Object.freeze({bing:!1});function h(r,{rev:t=0}={}){return!Array.isArray(r)||Number(t)>=g?r:r.map(n=>{if(!n||n.chosen||!(n.id in d)||n.enabled!==d[n.id])return n;const{enabled:i,...o}=n;return o})}function y(r,t,{hasKey:n=!1}={}){const i=new Map(t.map(e=>[e.id,e])),o=new Set(t.filter(e=>e.retired).map(e=>e.id));let a=(Array.isArray(r)&&r.length?r:t).filter(e=>e?.id&&!o.has(e.id)).map(e=>({...i.get(e.id)||{},...e}));for(const e of t)e.retired||a.some(s=>s.id===e.id)||a.push({...e});if(a=a.map(e=>{if(!i.get(e.id)?.needsKey)return e;const u=Array.isArray(r)&&r.some(f=>f?.id===e.id)&&r.find(f=>f.id===e.id)?.enabled===!1;return{...e,enabled:n&&!u}}),!a.some(e=>e.enabled!==!1&&!e.needsKey))for(const e of t){if(e.retired||e.needsKey)continue;const s=a.find(l=>l.id===e.id);s&&(s.enabled=e.enabled!==!1)}return a}function b(r){const t=r.filter(n=>!n.retired);return[...t.filter(n=>n.enabled!==!1),...t.filter(n=>n.enabled===!1)]}export{g as ENGINES_REV,E as ENGINE_KINDS,w as GATEWAY_ENGINE,c as SearchEngineError,b as attemptOrder,p as defineSearchEngine,h as inheritEngineDefaults,y as reconcileEngines};
|
package/search-modes.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{fuseRRF as
|
|
2
|
-
`),
|
|
3
|
-
${s.snippet||""}`),rank:
|
|
4
|
-
${e.snippet||""}`,{midSentence:!0,exclude:
|
|
5
|
-
${
|
|
6
|
-
Most likely answered by: ${o.decision.title}`:"",
|
|
1
|
+
import{fuseRRF as Y}from"./rrf.js";import{lexicalPlan as H}from"./recall.js";import{recall as Q}from"./memory.js";const T=Object.freeze(["memory","keyword","graph","hybrid"]),W=new Set("i im ive id ill the a an this that these those what who whom whose which when where why how is are was were do does did can could will would should my your our their his her its we you he she they it me him them us yes no ok okay hi hello hey thanks please sure also and but or so if then monday tuesday wednesday thursday friday saturday sunday january february march april may june july august september october november december today tomorrow yesterday chap chapel chatpanel gemini nano claude codex speaker note notes chat chats meeting meetings summary topics action items met meet saw see called call asked ask told tell emailed email texted text messaged pinged ping spoke talked thanks thank dear cc per via with from here there now sorry great sure maybe perhaps still just only even also yet here's there's let please compare".split(/\s+/)),X=new Set("ceo cfo cto coo ai api usa us uk eu faq pdf ok".split(" ")),J=/(?<![\w'])((?:[A-Z][a-z][A-Za-z'-]+|[A-Z]{2,}[A-Za-z]*|[a-z][A-Z][A-Za-z]+)(?:\s+(?:[A-Z][A-Za-z'-]*|\d{1,4}[A-Za-z]{0,3})(?![\w'])){0,3})/g,K=/\b([A-Z][a-z][A-Za-z'-]+(?:\s+[A-Z][a-z][A-Za-z'-]+){0,2})\b/g;function M(o,{max:i=12,midSentence:n=!1,exclude:r=[],things:t=!1}={}){const h=new Map,y=new Set(r.map(u=>String(u).toLowerCase()).filter(Boolean)),c=new RegExp((t?J:K).source,"g"),m=String(o||"");for(const u of m.matchAll(c)){if(n&&/(^|[.!?:|\n"“]\s*)$/.test(m.slice(Math.max(0,u.index-3),u.index)))continue;const b=u[1].split(/\s+/).filter(d=>!W.has(d.toLowerCase().replace(/'s$/,"")));if(!b.length)continue;const S=b.join(" ").replace(/'s?$/,"");if(S.length<3||/^[\d\s]+$/.test(S)||t&&X.has(S.toLowerCase()))continue;const a=S.toLowerCase();if(![...y].some(d=>a===d||a.split(" ").some(E=>d.split(" ").includes(E)))&&(h.set(a,h.get(a)||{name:S,count:0}),h.get(a).count+=1,h.size>=i*2))break}return[...h.values()].sort((u,b)=>b.count-u.count).slice(0,i).map(u=>u.name)}function I(o,{plan:i=null}={}){const n=(i?.terms?.length?i.terms:H(o).terms).map(t=>String(t).toLowerCase().replace(/[^a-z0-9]/g,"")).filter(t=>t.length>=2),r=new Set(n);for(const t of n)t.length>=7&&r.add(t.slice(0,5));return[...r].slice(0,12)}const q=o=>o?.sinceAt||0;function B(o,i){const n=I(o).filter(t=>t.length>=4);if(n.length<2)return!1;const r=String(i||"").toLowerCase();return n.filter(t=>r.includes(t)).length/n.length>=.8}const U=500;function L(o){const i=(n,r)=>[...new Set((Array.isArray(n)?n:[]).map(r).filter(Boolean))].slice(-U);return{notNames:i(o?.notNames,n=>String(n||"").replace(/\s+/g," ").trim().toLowerCase().slice(0,80)),hidden:i(o?.hidden,n=>String(n||"").trim().slice(0,200))}}function re(o,{op:i,value:n}){const r=L(o),t=String(n||"").trim();return t?i==="not-a-name"?L({...r,notNames:[...r.notNames,t]}):i==="undo-name"?{...r,notNames:r.notNames.filter(h=>h!==t.toLowerCase())}:i==="hide"?L({...r,hidden:[...r.hidden,t]}):i==="unhide"?{...r,hidden:r.hidden.filter(h=>h!==t)}:r:r}async function V(o,i,{decide:n,k:r=6}={}){const t=i.slice(0,r);if(!n||!t.length)return{reranked:i,decision:{verdict:"unknown"}};const h=[`Question: ${String(o).slice(0,400)}`,"",...t.map((a,d)=>`[${d+1}] ${a.title}: ${String(a.snippet||"").replace(/\s+/g," ").slice(0,280)}`)].join(`
|
|
2
|
+
`),y={};t.forEach((a,d)=>{y[`p${d+1}`]={type:"score",instructions:`How well does passage [${d+1}] answer the question? A passage that only repeats the question, or says it cannot tell, does NOT answer it.`,options:["no","partly","answers it"]}}),y.answer={type:"choice",instructions:'Which passage gives the answer itself (a name, a fact, a date)? "none" if none does.',options:[...t.map((a,d)=>String(d+1)),"none"]};let c=null;try{c=await n({state:h,questions:y})}catch{c=null}const m=c?.answers;if(!m)return{reranked:i,decision:{verdict:"unknown"}};const u=t.map((a,d)=>({...a,relevance:Number(m[`p${d+1}`]?.value??0),at:d})).sort((a,d)=>d.relevance-a.relevance||a.at-d.at).map(({at:a,...d})=>d),b=String(m.answer?.value??"none"),S=b==="none"?null:t[Number(b)-1];return{reranked:[...u,...i.slice(r)],decision:S?{verdict:"found",id:S.id,title:S.title,by:c.model||"model",calibrated:c.calibrated!==!1}:{verdict:"not-found",by:c.model||"model",calibrated:c.calibrated!==!1}}}async function ie(o,{lexical:i,decide:n=null,corrections:r=null,memories:t=[],self:h="",excludeId:y=null,plan:c=null,limit:m=10,modes:u=T,hops:b=5,now:S=()=>Date.now()}={}){const a=new Set(u),d=L(r),E=new Set(d.hidden),Z=async s=>(await i(s)||[]).filter(p=>!E.has(p.id)),x=c||H(o),O={chats:"chat",notes:"note",meetings:"meeting"}[x.scope]||"",f={plan:{terms:I(o,{plan:x}),scope:x.scope,since:x.since||""},modes:{},ms:{},errors:{}},C=async(s,p)=>{const k=S();try{f.modes[s]=await p()}catch($){f.modes[s]=[],f.errors[s]=String($?.message||$)}return f.ms[s]=S()-k,f.modes[s]},F=await C("memory",async()=>a.has("memory")||a.has("graph")||a.has("hybrid")?Q(t,{text:o,includeAmbient:!1,limit:m}).map((s,p)=>({id:`memory:${s.id}`,kind:"memory",title:s.text,date:s.updatedAt||0,snippet:s.text,score:1/(1+p)})).filter(s=>!E.has(s.id)):[]),N=await C("keyword",async()=>a.has("keyword")||a.has("graph")||a.has("hybrid")?(await Z({terms:f.plan.terms,kind:O,sinceAt:q(x),limit:Math.max(m,20)+5})||[]).filter(s=>s.id!==y).map((s,p)=>({...s,echo:B(o,`${s.title}
|
|
3
|
+
${s.snippet||""}`),rank:p})).sort((s,p)=>Number(s.echo)-Number(p.echo)||s.rank-p.rank).map(({rank:s,...p},k)=>({...p,score:1/(1+k),...p.echo?{via:"an earlier time you asked"}:{}})):[]);await C("graph",async()=>{if(!a.has("graph")&&!a.has("hybrid"))return[];const s=new Map,p=(e,l,g)=>{const A=e.toLowerCase();if($.has(A))return;const D=s.get(A)||{name:e,weight:0,from:g};D.weight+=l,s.set(A,D)},k=h?[h]:[],$=new Set(d.notNames),w=new Set(N.filter(e=>e.echo).map(e=>e.id));for(const e of M(o,{exclude:k}))p(e,2,"question");F.slice(0,3).forEach((e,l)=>M(e.snippet,{exclude:k}).forEach(g=>p(g,1.5/(1+l),"memory"))),N.filter(e=>!e.echo).slice(0,8).forEach((e,l)=>[...e.names||[],...M(`${e.title}
|
|
4
|
+
${e.snippet||""}`,{midSentence:!0,exclude:k})].forEach(g=>p(g,1/(2+l),e.id)));const v=e=>N.slice(0,8).filter(l=>`${l.title} ${l.snippet||""} ${(l.names||[]).join(" ")}`.toLowerCase().includes(e)).length,R=Math.min(8,N.length),G=[...s.values()].some(e=>e.from==="question"||e.from==="memory"),j=[...s.entries()].filter(([e,l])=>l.from==="question"||l.from==="memory"||(!G||v(e)>=2)&&(R<4||v(e)<=R/2)).map(([e,l])=>({...l,key:e,weight:l.weight/(1+Math.max(0,v(e)-2))})).sort((e,l)=>l.weight-e.weight).slice(0,b),z=new Map,_=(e,l,g)=>{const A=z.get(e.id)||{...e,score:0,via:[]};A.score+=l,g&&!A.via.includes(g)&&A.via.push(g),z.set(e.id,A)};N.forEach((e,l)=>_(e,(e.echo?.05:.5)/(1+l),null));for(const e of j)(await Z({terms:[e.name.toLowerCase()],phrase:!0,kind:O,sinceAt:q(x),limit:10})||[]).filter(g=>g.id!==y&&!w.has(g.id)&&!B(o,`${g.title}
|
|
5
|
+
${g.snippet||""}`)).forEach((g,A)=>_(g,e.weight/(1+A),e.name));return f.plan.names=j.map(e=>e.name),[...z.values()].sort((e,l)=>l.score-e.score).slice(0,m).map(e=>({...e,via:e.via.length?e.via.join(", "):void 0}))}),await C("hybrid",async()=>{if(!a.has("hybrid"))return[];const s=(f.modes.graph||[]).filter(w=>w.via),p=[f.modes.memory,f.modes.keyword,s].map(w=>(w||[]).map(v=>v.id)),k=new Map;for(const w of["graph","keyword","memory"])for(const v of f.modes[w]||[])k.has(v.id)||k.set(v.id,{...v,from:[]});for(const w of["memory","keyword","graph"])for(const v of f.modes[w]||[])k.get(v.id)?.from.push(w);const $=Y(p,{limit:m+5}).map(({id:w,score:v})=>({...k.get(w),score:v,from:k.get(w).from.join("+")}));return[...$.filter(w=>!w.echo),...$.filter(w=>w.echo)].slice(0,m)}),n&&a.has("hybrid")&&await C("reranked",async()=>{const s=await V(o,f.modes.hybrid||[],{decide:n});return f.decision=s.decision,s.reranked});for(const s of T)a.has(s)||delete f.modes[s];return a.has("keyword")&&(f.modes.keyword=N.slice(0,m)),f}const ee=[/\bmy\s+(?:friends?|family|wife|husband|partner|mom|mum|dad|mother|father|son|daughter|kids?|brother|sister|boss|manager|colleagues?|team|doctor|neighbou?rs?|contacts?|notes?|meetings?|chats?|trip|plans?|tasks?|reminders?|flight|hotel|appointment|address|car|house|home)\b/i,/\b(?:did|have|had)\s+(?:i|we)\b/i,/\bwhat\s+did\s+(?:i|we|\w+)\s+(?:say|decide|agree|discuss|mention|write|note)\b/i,/\b(?:when|where|who|what)\s+(?:was|were|is|are)\s+(?:my|our)\b/i,/\b(?:i|we)\s+(?:told you|mentioned|said|talked about|discussed)\b/i,/\bremind me\b/i,/\b(?:was|were|am|are)\s+(?:i|we)\s+(?:planning|going|supposed|meant|thinking|hoping|booked|scheduled)\b/i,/\b(?:i|we)\s+(?:was|were)\s+(?:planning|going to|supposed to|meant to|thinking of|hoping to)\b/i,/\b(?:i|we)\s+(?:planned|booked|scheduled|decided)\b/i,/\bwho\s+(?:do|did|have)\s+i\s+(?:meet|met|talk|speak|spoke|chat)/i,/\b(?:meetings?|calls?|chats?|notes?)\s+(?:per|by|each|every|a)\s+(?:month|week|day)\b/i],te=/^\s*(?:(?:ok(?:ay)?|hey|so|and|also|please)[\s,]+)*(?:remember|note|keep in mind|don'?t forget|never forget|forget|call me|save (?:that|this))\b/i;function P(o,{names:i=[]}={}){const n=String(o||"");if(n.trim().length<6||te.test(n))return!1;if(ee.some(t=>t.test(n))||/\b(meetings?|calls?|chats?|notes?|conversations?)\b/i.test(n)&&M(n).length)return!0;const r=n.toLowerCase();return i.some(t=>t&&new RegExp(`\\b${String(t).toLowerCase().replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`).test(r))}function ae(o,{names:i=[],home:n="",world:r=!1}={}){if(P(o))return!0;if(r)return!1;const t=c=>String(c||"").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean),h=t(n),y=c=>{const m=t(c);return m.length>0&&h.length>0&&(m.every(u=>h.includes(u))||h.every((u,b)=>m[b]===u))};return P(o,{names:(Array.isArray(i)?i:[]).filter(c=>!y(c))})}function ce(o,{max:i=5,now:n=Date.now()}={}){const r=(o?.modes?.reranked||o?.modes?.hybrid||[]).filter(c=>!c.echo).slice(0,i);if(!r.length)return null;const t=r.map((c,m)=>{const u=c.kind==="memory"?"memory":`${c.id}${c.title?` “${String(c.title).slice(0,80)}”`:""}`,b=String(c.snippet||c.title||"").replace(/\s+/g," ").trim().slice(0,300);return`${m+1}. [${u}] ${b}`}),h=o?.decision?.verdict==="found"?`
|
|
6
|
+
Most likely answered by: ${o.decision.title}`:"",y=`(ALREADY SEARCHED the person's own chats, notes, meetings and memory for the question below. Answer from this; cite the [id] you used. If the answer is not here, say you could not find it in their data — never guess, and never say you have no access to their data.)
|
|
7
7
|
|
|
8
8
|
${t.join(`
|
|
9
|
-
`)}${
|
|
9
|
+
`)}${h}`;return{id:`prefetch_personal_${n}`,kind:"prefetch",prefetch:"personal",title:"Searched your own data",text:y,chars:y.length}}export{U as MAX_CORRECTIONS,T as SEARCH_MODES,re as correct,ce as evidenceAttachment,B as isEcho,I as keywordTerms,M as namesIn,L as normalizeCorrections,P as personalAsk,ae as personalFirst,V as rerankAndDecide,ie as runSearchModes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=Object.freeze({ANSWERED:"answered",NOTHING:"nothing",BLOCKED:"blocked",RESTING:"resting",OFFLINE:"offline",NO_ENGINES:"no-engines"});function g(e){return e?.kind==="gateway"?"gateway":e?.kind==="api"?"api":e?.kind==="tab"?"browser":"page"}function h(e,t){const i=g(t);return(Array.isArray(e)?e:[]).map(n=>({engine:t?.id||"",via:i,...n}))}function c({results:e=[],asked:t=[],resting:i=[]}={}){return e.length?r.ANSWERED:t.length?t.some(n=>n.outcome==="empty"||n.outcome==="answered")?r.NOTHING:t.every(n=>n.network)?r.OFFLINE:r.BLOCKED:i.length?r.RESTING:r.NO_ENGINES}const d={duckduckgo:"DuckDuckGo",startpage:"Startpage",bing:"Bing",google:"Google",gateway:"your gateway","api:mwmbl":"Mwmbl","api:exa":"Exa","api:brave":"Brave","api:tavily":"Tavily","api:serper":"Serper","api:serpapi":"SerpApi","api:google":"Google Programmable Search","api:jina":"Jina","api:wikipedia":"Wikipedia"};function o(e,t={}){return t[e]||d[e]||String(e||"").replace(/^api:/,"")}function f(e){return e.outcome==="answered"?`${e.found} result${e.found===1?"":"s"}`:e.outcome==="empty"?"found nothing":e.timedOut?"timed out":e.status===429?"refused (too many requests)":e.status===403?"refused (blocked)":e.status>=500?`failed (${e.status})`:e.status>0?`refused (${e.status})`:"could not be reached"}const p=e=>{const t=new Date(e);return`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`};function l(e={},{names:t={}}={}){const i=c(e),n=(e.asked||[]).map(a=>`${o(a.id,t)} ${f(a)}`),s=(e.resting||[]).map(a=>`${o(a.id,t)} until ${p(a.until)}`);s.length&&n.push(`resting: ${s.join(", ")}`);const u={[r.ANSWERED]:"",[r.NOTHING]:"The engines answered but had nothing for this wording — try a shorter, more general query (drop dates and qualifiers).",[r.BLOCKED]:"The engines refused the request — this says nothing about whether the information exists. Try again in a few minutes, or add a search API in Settings › Web search.",[r.RESTING]:"Every engine is resting after refusing earlier searches, so none was asked. Try again later, or add a search API in Settings › Web search.",[r.OFFLINE]:"No search engine could be reached — the device may be offline.",[r.NO_ENGINES]:"No search engine is switched on — turn one on in Settings › Web search."}[i];return{status:i,line:n.join(" · "),advice:u}}export{r as SEARCH_STATUS,o as engineLabel,c as searchStatus,l as summarizeSearch,h as tagResults,g as viaOf};
|