@chatpanel/events 0.332.0 → 0.353.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/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(/&quot;/g,'"').replace(/&#0?39;/g,"'").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/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 d extends Error{constructor(r,t){super(t),this.name="SearchEngineError",this.code=r}}const u=Object.freeze(["serp","api","gateway"]);function w({id:n,name:r,url:t,kind:a="serp",enabled:s=!1,needsKey:i=!1,retired:e=!1}){if(!n)throw new d("BAD_ENGINE","engine.id required");if(!u.includes(a))throw new d("BAD_ENGINE",`engine '${n}': unknown kind '${a}'`);if(!e&&!t&&a!=="gateway")throw new d("BAD_ENGINE",`engine '${n}': url required`);return Object.freeze({id:n,name:r||n,url:a==="gateway"?"":t,kind:a,enabled:s,needsKey:i,retired:e})}const E=w({id:"gateway",name:"Your gateway (SearXNG when it runs there)",url:"",kind:"gateway"});function p(n,r,{hasKey:t=!1}={}){const a=new Map(r.map(e=>[e.id,e])),s=new Set(r.filter(e=>e.retired).map(e=>e.id));let i=(Array.isArray(n)&&n.length?n:r).filter(e=>e?.id&&!s.has(e.id)).map(e=>({...a.get(e.id)||{},...e}));for(const e of r)e.retired||i.some(o=>o.id===e.id)||i.push({...e});if(i=i.map(e=>{if(!a.get(e.id)?.needsKey)return e;const c=Array.isArray(n)&&n.some(f=>f?.id===e.id)&&n.find(f=>f.id===e.id)?.enabled===!1;return{...e,enabled:t&&!c}}),!i.some(e=>e.enabled!==!1&&!e.needsKey))for(const e of r){if(e.retired||e.needsKey)continue;const o=i.find(l=>l.id===e.id);o&&(o.enabled=e.enabled!==!1)}return i}function g(n){const r=n.filter(t=>!t.retired);return[...r.filter(t=>t.enabled!==!1),...r.filter(t=>t.enabled===!1)]}export{u as ENGINE_KINDS,E as GATEWAY_ENGINE,d as SearchEngineError,g as attemptOrder,w as defineSearchEngine,p as reconcileEngines};
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 F}from"./rrf.js";import{lexicalPlan as T}from"./recall.js";import{recall as Q}from"./memory.js";const Z=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".split(/\s+/));function M(o,{max:a=12,midSentence:n=!1,exclude:r=[]}={}){const t=new Map,y=new Set(r.map(l=>String(l).toLowerCase()).filter(Boolean)),k=/\b([A-Z][a-z][A-Za-z'-]+(?:\s+[A-Z][a-z][A-Za-z'-]+){0,2})\b/g,d=String(o||"");for(const l of d.matchAll(k)){if(n&&/(^|[.!?:|\n"“]\s*)$/.test(d.slice(Math.max(0,l.index-3),l.index)))continue;const v=l[1].split(/\s+/).filter(i=>!W.has(i.toLowerCase().replace(/'s$/,"")));if(!v.length)continue;const S=v.join(" ").replace(/'s$/,"");if(S.length<3)continue;const b=S.toLowerCase();if(![...y].some(i=>b===i||b.split(" ").some(h=>i.split(" ").includes(h)))&&(t.set(b,t.get(b)||{name:S,count:0}),t.get(b).count+=1,t.size>=a*2))break}return[...t.values()].sort((l,v)=>v.count-l.count).slice(0,a).map(l=>l.name)}function I(o,{plan:a=null}={}){const n=(a?.terms?.length?a.terms:T(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,a){const n=I(o).filter(t=>t.length>=4);if(n.length<2)return!1;const r=String(a||"").toLowerCase();return n.filter(t=>r.includes(t)).length/n.length>=.8}const X=500;function N(o){const a=(n,r)=>[...new Set((Array.isArray(n)?n:[]).map(r).filter(Boolean))].slice(-X);return{notNames:a(o?.notNames,n=>String(n||"").replace(/\s+/g," ").trim().toLowerCase().slice(0,80)),hidden:a(o?.hidden,n=>String(n||"").trim().slice(0,200))}}function te(o,{op:a,value:n}){const r=N(o),t=String(n||"").trim();return t?a==="not-a-name"?N({...r,notNames:[...r.notNames,t]}):a==="undo-name"?{...r,notNames:r.notNames.filter(y=>y!==t.toLowerCase())}:a==="hide"?N({...r,hidden:[...r.hidden,t]}):a==="unhide"?{...r,hidden:r.hidden.filter(y=>y!==t)}:r:r}async function G(o,a,{decide:n,k:r=6}={}){const t=a.slice(0,r);if(!n||!t.length)return{reranked:a,decision:{verdict:"unknown"}};const y=[`Question: ${String(o).slice(0,400)}`,"",...t.map((i,h)=>`[${h+1}] ${i.title}: ${String(i.snippet||"").replace(/\s+/g," ").slice(0,280)}`)].join(`
2
- `),k={};t.forEach((i,h)=>{k[`p${h+1}`]={type:"score",instructions:`How well does passage [${h+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"]}}),k.answer={type:"choice",instructions:'Which passage gives the answer itself (a name, a fact, a date)? "none" if none does.',options:[...t.map((i,h)=>String(h+1)),"none"]};let d=null;try{d=await n({state:y,questions:k})}catch{d=null}const l=d?.answers;if(!l)return{reranked:a,decision:{verdict:"unknown"}};const v=t.map((i,h)=>({...i,relevance:Number(l[`p${h+1}`]?.value??0),at:h})).sort((i,h)=>h.relevance-i.relevance||i.at-h.at).map(({at:i,...h})=>h),S=String(l.answer?.value??"none"),b=S==="none"?null:t[Number(S)-1];return{reranked:[...v,...a.slice(r)],decision:b?{verdict:"found",id:b.id,title:b.title,by:d.model||"model",calibrated:d.calibrated!==!1}:{verdict:"not-found",by:d.model||"model",calibrated:d.calibrated!==!1}}}async function se(o,{lexical:a,decide:n=null,corrections:r=null,memories:t=[],self:y="",excludeId:k=null,plan:d=null,limit:l=10,modes:v=Z,hops:S=5,now:b=()=>Date.now()}={}){const i=new Set(v),h=N(r),j=new Set(h.hidden),R=async s=>(await a(s)||[]).filter(m=>!j.has(m.id)),A=d||T(o),O={chats:"chat",notes:"note",meetings:"meeting"}[A.scope]||"",u={plan:{terms:I(o,{plan:A}),scope:A.scope,since:A.since||""},modes:{},ms:{},errors:{}},E=async(s,m)=>{const w=b();try{u.modes[s]=await m()}catch(x){u.modes[s]=[],u.errors[s]=String(x?.message||x)}return u.ms[s]=b()-w,u.modes[s]},P=await E("memory",async()=>i.has("memory")||i.has("graph")||i.has("hybrid")?Q(t,{text:o,includeAmbient:!1,limit:l}).map((s,m)=>({id:`memory:${s.id}`,kind:"memory",title:s.text,date:s.updatedAt||0,snippet:s.text,score:1/(1+m)})).filter(s=>!j.has(s.id)):[]),C=await E("keyword",async()=>i.has("keyword")||i.has("graph")||i.has("hybrid")?(await R({terms:u.plan.terms,kind:O,sinceAt:q(A),limit:Math.max(l,20)+5})||[]).filter(s=>s.id!==k).map((s,m)=>({...s,echo:B(o,`${s.title}
3
- ${s.snippet||""}`),rank:m})).sort((s,m)=>Number(s.echo)-Number(m.echo)||s.rank-m.rank).map(({rank:s,...m},w)=>({...m,score:1/(1+w),...m.echo?{via:"an earlier time you asked"}:{}})):[]);await E("graph",async()=>{if(!i.has("graph")&&!i.has("hybrid"))return[];const s=new Map,m=(e,c,f)=>{const $=e.toLowerCase();if(x.has($))return;const H=s.get($)||{name:e,weight:0,from:f};H.weight+=c,s.set($,H)},w=y?[y]:[],x=new Set(h.notNames),p=new Set(C.filter(e=>e.echo).map(e=>e.id));for(const e of M(o,{exclude:w}))m(e,2,"question");P.slice(0,3).forEach((e,c)=>M(e.snippet,{exclude:w}).forEach(f=>m(f,1.5/(1+c),"memory"))),C.filter(e=>!e.echo).slice(0,8).forEach((e,c)=>[...e.names||[],...M(`${e.title}
4
- ${e.snippet||""}`,{midSentence:!0,exclude:w})].forEach(f=>m(f,1/(2+c),e.id)));const g=e=>C.slice(0,8).filter(c=>`${c.title} ${c.snippet||""} ${(c.names||[]).join(" ")}`.toLowerCase().includes(e)).length,z=Math.min(8,C.length),Y=[...s.values()].some(e=>e.from==="question"||e.from==="memory"),_=[...s.entries()].filter(([e,c])=>c.from==="question"||c.from==="memory"||(!Y||g(e)>=2)&&(z<4||g(e)<=z/2)).map(([e,c])=>({...c,key:e,weight:c.weight/(1+Math.max(0,g(e)-2))})).sort((e,c)=>c.weight-e.weight).slice(0,S),L=new Map,D=(e,c,f)=>{const $=L.get(e.id)||{...e,score:0,via:[]};$.score+=c,f&&!$.via.includes(f)&&$.via.push(f),L.set(e.id,$)};C.forEach((e,c)=>D(e,(e.echo?.05:.5)/(1+c),null));for(const e of _)(await R({terms:[e.name.toLowerCase()],phrase:!0,kind:O,sinceAt:q(A),limit:10})||[]).filter(f=>f.id!==k&&!p.has(f.id)&&!B(o,`${f.title}
5
- ${f.snippet||""}`)).forEach((f,$)=>D(f,e.weight/(1+$),e.name));return u.plan.names=_.map(e=>e.name),[...L.values()].sort((e,c)=>c.score-e.score).slice(0,l).map(e=>({...e,via:e.via.length?e.via.join(", "):void 0}))}),await E("hybrid",async()=>{if(!i.has("hybrid"))return[];const s=(u.modes.graph||[]).filter(p=>p.via),m=[u.modes.memory,u.modes.keyword,s].map(p=>(p||[]).map(g=>g.id)),w=new Map;for(const p of["graph","keyword","memory"])for(const g of u.modes[p]||[])w.has(g.id)||w.set(g.id,{...g,from:[]});for(const p of["memory","keyword","graph"])for(const g of u.modes[p]||[])w.get(g.id)?.from.push(p);const x=F(m,{limit:l+5}).map(({id:p,score:g})=>({...w.get(p),score:g,from:w.get(p).from.join("+")}));return[...x.filter(p=>!p.echo),...x.filter(p=>p.echo)].slice(0,l)}),n&&i.has("hybrid")&&await E("reranked",async()=>{const s=await G(o,u.modes.hybrid||[],{decide:n});return u.decision=s.decision,s.reranked});for(const s of Z)i.has(s)||delete u.modes[s];return i.has("keyword")&&(u.modes.keyword=C.slice(0,l)),u}const J=[/\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],K=/^\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 ne(o,{names:a=[]}={}){const n=String(o||"");if(n.trim().length<6||K.test(n))return!1;if(J.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 a.some(t=>t&&new RegExp(`\\b${String(t).toLowerCase().replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`).test(r))}function oe(o,{max:a=5,now:n=Date.now()}={}){const r=(o?.modes?.reranked||o?.modes?.hybrid||[]).filter(d=>!d.echo).slice(0,a);if(!r.length)return null;const t=r.map((d,l)=>{const v=d.kind==="memory"?"memory":`${d.id}${d.title?` “${String(d.title).slice(0,80)}”`:""}`,S=String(d.snippet||d.title||"").replace(/\s+/g," ").trim().slice(0,300);return`${l+1}. [${v}] ${S}`}),y=o?.decision?.verdict==="found"?`
6
- Most likely answered by: ${o.decision.title}`:"",k=`(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.)
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
- `)}${y}`;return{id:`prefetch_personal_${n}`,kind:"prefetch",prefetch:"personal",title:"Searched your own data",text:k,chars:k.length}}export{X as MAX_CORRECTIONS,Z as SEARCH_MODES,te as correct,oe as evidenceAttachment,B as isEcho,I as keywordTerms,M as namesIn,N as normalizeCorrections,ne as personalAsk,G as rerankAndDecide,se as runSearchModes};
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};