@spendgraph/tools 0.6.0 → 0.8.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.
Files changed (211) hide show
  1. package/README.md +3 -2
  2. package/dist/builtin/calculate/calculate.d.ts +2 -0
  3. package/dist/builtin/calculate/calculate.js +1 -1
  4. package/dist/builtin/calculate/index.d.ts +2 -1
  5. package/dist/builtin/calculate/index.js +1 -1
  6. package/dist/builtin/code-exec/code-exec.d.ts +14 -0
  7. package/dist/builtin/date/date.d.ts +1 -1
  8. package/dist/builtin/date/date.js +1 -1
  9. package/dist/builtin/date/index.d.ts +1 -0
  10. package/dist/builtin/date/index.js +1 -1
  11. package/dist/builtin/date/shift.d.ts +1 -1
  12. package/dist/builtin/date/shift.js +1 -1
  13. package/dist/builtin/date/words.d.ts +13 -0
  14. package/dist/builtin/date/words.js +1 -0
  15. package/dist/builtin/decline/decline.d.ts +49 -0
  16. package/dist/builtin/decline/decline.js +1 -0
  17. package/dist/builtin/decline/index.d.ts +1 -0
  18. package/dist/builtin/decline/index.js +1 -0
  19. package/dist/builtin/escalate/escalate.d.ts +19 -0
  20. package/dist/builtin/escalate/escalate.js +1 -1
  21. package/dist/builtin/escalate/index.d.ts +1 -1
  22. package/dist/builtin/escalate/index.js +1 -1
  23. package/dist/builtin/extract/extract.d.ts +25 -0
  24. package/dist/builtin/extract/extract.js +1 -0
  25. package/dist/builtin/extract/index.d.ts +3 -0
  26. package/dist/builtin/extract/index.js +1 -0
  27. package/dist/builtin/extract/kinds.d.ts +6 -0
  28. package/dist/builtin/extract/kinds.js +1 -0
  29. package/dist/builtin/extract/readers.d.ts +7 -0
  30. package/dist/builtin/extract/readers.js +9 -0
  31. package/dist/builtin/finish/finish.d.ts +4 -0
  32. package/dist/builtin/finish/finish.js +1 -1
  33. package/dist/builtin/http/allow.d.ts +2 -0
  34. package/dist/builtin/http/allow.js +1 -1
  35. package/dist/builtin/http/hops.d.ts +10 -0
  36. package/dist/builtin/http/hops.js +1 -0
  37. package/dist/builtin/http/http.d.ts +9 -1
  38. package/dist/builtin/http/http.js +1 -1
  39. package/dist/builtin/http/index.d.ts +2 -0
  40. package/dist/builtin/http/index.js +1 -1
  41. package/dist/builtin/http/retry-after.d.ts +14 -0
  42. package/dist/builtin/http/retry-after.js +1 -0
  43. package/dist/builtin/index.d.ts +11 -6
  44. package/dist/builtin/index.js +1 -1
  45. package/dist/builtin/json/index.d.ts +1 -0
  46. package/dist/builtin/json/index.js +1 -1
  47. package/dist/builtin/json/json.js +1 -1
  48. package/dist/builtin/json/parse.d.ts +15 -0
  49. package/dist/builtin/json/parse.js +1 -0
  50. package/dist/builtin/notes/index.d.ts +1 -0
  51. package/dist/builtin/notes/index.js +1 -0
  52. package/dist/builtin/notes/notes.d.ts +45 -0
  53. package/dist/builtin/notes/notes.js +2 -0
  54. package/dist/builtin/openalex/api.d.ts +26 -0
  55. package/dist/builtin/openalex/api.js +1 -0
  56. package/dist/builtin/openalex/index.d.ts +3 -0
  57. package/dist/builtin/openalex/index.js +1 -0
  58. package/dist/builtin/openalex/openalex.d.ts +36 -0
  59. package/dist/builtin/openalex/openalex.js +1 -0
  60. package/dist/builtin/paper-search/index.d.ts +1 -0
  61. package/dist/builtin/paper-search/index.js +1 -0
  62. package/dist/builtin/paper-search/paper-search.d.ts +38 -0
  63. package/dist/builtin/paper-search/paper-search.js +3 -0
  64. package/dist/builtin/polite.d.ts +15 -0
  65. package/dist/builtin/polite.js +1 -0
  66. package/dist/builtin/publish/index.d.ts +1 -0
  67. package/dist/builtin/publish/index.js +1 -0
  68. package/dist/builtin/publish/publish.d.ts +46 -0
  69. package/dist/builtin/publish/publish.js +1 -0
  70. package/dist/builtin/web-search/web-search.d.ts +10 -35
  71. package/dist/builtin/web-search/web-search.js +1 -1
  72. package/dist/builtin/wikidata/api.d.ts +40 -0
  73. package/dist/builtin/wikidata/api.js +1 -0
  74. package/dist/builtin/wikidata/index.d.ts +5 -0
  75. package/dist/builtin/wikidata/index.js +1 -0
  76. package/dist/builtin/wikidata/render.d.ts +13 -0
  77. package/dist/builtin/wikidata/render.js +2 -0
  78. package/dist/builtin/wikidata/wikidata.d.ts +23 -0
  79. package/dist/builtin/wikidata/wikidata.js +1 -0
  80. package/dist/bus/bus.d.ts +23 -1
  81. package/dist/bus/bus.js +1 -1
  82. package/dist/bus/group.d.ts +14 -1
  83. package/dist/bus/group.js +1 -1
  84. package/dist/bus/index.d.ts +4 -1
  85. package/dist/bus/index.js +1 -1
  86. package/dist/bus/invoke.d.ts +1 -1
  87. package/dist/bus/invoke.js +3 -1
  88. package/dist/bus/namespace.d.ts +21 -0
  89. package/dist/bus/namespace.js +1 -0
  90. package/dist/bus/once.d.ts +32 -0
  91. package/dist/bus/once.js +1 -0
  92. package/dist/bus/only.d.ts +29 -0
  93. package/dist/bus/options.d.ts +31 -0
  94. package/dist/bus/output.d.ts +0 -17
  95. package/dist/bus/output.js +2 -2
  96. package/dist/bus/select.d.ts +0 -16
  97. package/dist/bus/select.js +1 -1
  98. package/dist/bus/spent.d.ts +16 -0
  99. package/dist/bus/spent.js +1 -0
  100. package/dist/bus/trace.d.ts +14 -1
  101. package/dist/bus/trace.js +1 -1
  102. package/dist/index.d.ts +6 -4
  103. package/dist/index.js +1 -1
  104. package/dist/internals.d.ts +8 -1
  105. package/dist/internals.js +1 -1
  106. package/dist/memories/client.d.ts +91 -0
  107. package/dist/memories/client.js +0 -0
  108. package/dist/memories/context.d.ts +5 -0
  109. package/dist/memories/context.js +1 -0
  110. package/dist/{builtin → memories}/deep-recall/deep-recall.d.ts +3 -0
  111. package/dist/memories/deep-recall/deep-recall.js +1 -0
  112. package/dist/memories/explore/explore.d.ts +18 -0
  113. package/dist/memories/explore/explore.js +7 -0
  114. package/dist/memories/explore/subgraph.d.ts +12 -0
  115. package/dist/memories/explore/subgraph.js +3 -0
  116. package/dist/memories/index.d.ts +30 -0
  117. package/dist/memories/index.js +1 -0
  118. package/dist/memories/observe/observe.d.ts +7 -0
  119. package/dist/memories/observe/observe.js +1 -0
  120. package/dist/memories/recall/recall.d.ts +42 -0
  121. package/dist/memories/recall/recall.js +1 -0
  122. package/dist/memories/recall/render.d.ts +5 -0
  123. package/dist/memories/recall/render.js +3 -0
  124. package/dist/memories/remember/remember.d.ts +49 -0
  125. package/dist/memories/remember/remember.js +1 -0
  126. package/dist/papers/consensus/consensus.d.ts +12 -0
  127. package/dist/papers/consensus/consensus.js +1 -0
  128. package/dist/papers/consensus/index.d.ts +4 -0
  129. package/dist/papers/consensus/index.js +1 -0
  130. package/dist/papers/consensus/reply.d.ts +24 -0
  131. package/dist/papers/consensus/reply.js +1 -0
  132. package/dist/papers/every.d.ts +6 -0
  133. package/dist/papers/every.js +1 -0
  134. package/dist/papers/index.d.ts +11 -0
  135. package/dist/papers/index.js +1 -0
  136. package/dist/papers/openalex/index.d.ts +2 -0
  137. package/dist/papers/openalex/index.js +1 -0
  138. package/dist/papers/openalex/openalex.d.ts +8 -0
  139. package/dist/papers/openalex/openalex.js +1 -0
  140. package/dist/papers/papers.d.ts +54 -0
  141. package/dist/papers/papers.js +1 -0
  142. package/dist/papers/render.d.ts +2 -0
  143. package/dist/papers/render.js +4 -0
  144. package/dist/papers/semantic-scholar/index.d.ts +4 -0
  145. package/dist/papers/semantic-scholar/index.js +1 -0
  146. package/dist/papers/semantic-scholar/reply.d.ts +29 -0
  147. package/dist/papers/semantic-scholar/reply.js +1 -0
  148. package/dist/papers/semantic-scholar/semantic-scholar.d.ts +8 -0
  149. package/dist/papers/semantic-scholar/semantic-scholar.js +1 -0
  150. package/dist/search/every.d.ts +6 -0
  151. package/dist/search/every.js +4 -0
  152. package/dist/search/index.d.ts +7 -0
  153. package/dist/search/index.js +1 -0
  154. package/dist/search/retry.d.ts +6 -0
  155. package/dist/search/retry.js +1 -0
  156. package/dist/search/search.d.ts +69 -0
  157. package/dist/search/search.js +1 -0
  158. package/dist/search/searxng/index.d.ts +2 -0
  159. package/dist/search/searxng/index.js +1 -0
  160. package/dist/search/searxng/searxng.d.ts +14 -0
  161. package/dist/search/searxng/searxng.js +5 -0
  162. package/dist/search/searxng/text.d.ts +2 -0
  163. package/dist/search/searxng/text.js +3 -0
  164. package/dist/search/sonar/index.d.ts +2 -0
  165. package/dist/search/sonar/index.js +1 -0
  166. package/dist/search/sonar/sonar.d.ts +13 -0
  167. package/dist/search/sonar/sonar.js +1 -0
  168. package/dist/search/tavily/index.d.ts +4 -0
  169. package/dist/search/tavily/index.js +1 -0
  170. package/dist/search/tavily/reply.d.ts +22 -0
  171. package/dist/search/tavily/reply.js +5 -0
  172. package/dist/search/tavily/tavily.d.ts +14 -0
  173. package/dist/search/tavily/tavily.js +1 -0
  174. package/dist/testing/index.d.ts +2 -0
  175. package/dist/testing/index.js +1 -0
  176. package/dist/testing/replay.d.ts +55 -0
  177. package/dist/testing/replay.js +1 -0
  178. package/dist/testing/store.d.ts +35 -0
  179. package/dist/testing/store.js +4 -0
  180. package/dist/tool/tool.d.ts +8 -1
  181. package/dist/tool/tool.js +1 -1
  182. package/dist/types/index.d.ts +1 -1
  183. package/dist/types/result.d.ts +38 -0
  184. package/dist/types/tool.d.ts +74 -0
  185. package/docs/bogus.mdx +85 -0
  186. package/docs/builtins.mdx +217 -0
  187. package/docs/bus.mdx +90 -0
  188. package/docs/declaring.mdx +85 -0
  189. package/docs/effects.mdx +49 -0
  190. package/docs/overview.mdx +76 -0
  191. package/docs/selecting.mdx +79 -0
  192. package/docs/stored.mdx +83 -0
  193. package/docs/turns.mdx +57 -0
  194. package/package.json +28 -9
  195. package/dist/builtin/deep-recall/deep-recall.js +0 -1
  196. package/dist/builtin/write-document/commission.d.ts +0 -56
  197. package/dist/builtin/write-document/commission.js +0 -13
  198. package/dist/builtin/write-document/design.d.ts +0 -49
  199. package/dist/builtin/write-document/design.js +0 -5
  200. package/dist/builtin/write-document/http.d.ts +0 -34
  201. package/dist/builtin/write-document/http.js +0 -1
  202. package/dist/builtin/write-document/index.d.ts +0 -6
  203. package/dist/builtin/write-document/index.js +0 -1
  204. package/dist/builtin/write-document/types.d.ts +0 -33
  205. package/dist/builtin/write-document/write-document.d.ts +0 -69
  206. package/dist/builtin/write-document/write-document.js +0 -1
  207. /package/dist/{builtin/write-document/types.js → bus/only.js} +0 -0
  208. /package/dist/{builtin → memories}/deep-recall/index.d.ts +0 -0
  209. /package/dist/{builtin → memories}/deep-recall/index.js +0 -0
  210. /package/dist/{builtin/web-search → search/sonar}/reply.d.ts +0 -0
  211. /package/dist/{builtin/web-search → search/sonar}/reply.js +0 -0
@@ -0,0 +1 @@
1
+ import{tool as h}from"../../tool/index.js";import{contextId as y,contextIds as a,fixedRoot as x,rootOf as w}from"../context.js";const g=["fact","decision","constraint","feedback","observation"],b="assistant",C=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),q=(e,t)=>{if(t==null)return{data:e};if(!C(t))throw new Error("details must be a JSON object of named fields.");return{...t,data:e}},d=(e,t)=>t?.length?{[e]:t}:{};function v(e){const t=e.defaultContextType,u=e.rootContextId===void 0?"":` under ${x(e.rootContextId)??"your root"}`,m=(c,l)=>{const s=w(e.rootContextId),i=a(l,t)??[],r=s?[s,...i]:i;return[...new Set(r)].filter(n=>n!==c)};return h({name:"remember",effect:"idempotent",description:`Records one thing in your own memory${u} so a later run can recall it. File it under a context named type:name, like person:alice or decision:pricing_tier, reusing a name from explore_memory when one fits. One fact per call; the text should stand on its own without the conversation around it.`,args:[{name:"context",type:"string",required:!0,maxLength:200,description:"Where it is filed, as type:name."},{name:"text",type:"string",required:!0,maxLength:4e3,description:"The thing to remember, as a complete sentence or two."},{name:"kind",type:"enum",required:!1,options:g,description:"What sort of thing it is. Default fact."},{name:"details",type:"json",required:!1,description:"Structured fields worth keeping with the note, as a JSON object: amounts, dates, ids, URLs. The text is still what a recall shows; these ride along with it."},{name:"extends",type:"list",required:!1,description:"Contexts this is a more specific detail of, as type:name."},{name:"related_to",type:"list",required:!1,description:"Contexts this sits alongside, as type:name."},{name:"reinforces",type:"list",required:!1,description:"Contexts this backs up with more evidence, as type:name."},{name:"contradicts",type:"list",required:!1,description:"Contexts this supersedes, as type:name."}],run:async({context:c,text:l,kind:s="fact",details:i,...r})=>{const n=y(c,t),f={event_kind:s,context_id:n,source:e.source??b,payload:q(l,i),...d("extends",m(n,r.extends)),...d("related_to",a(r.related_to,t)),...d("reinforces",a(r.reinforces,t)),...d("contradicts",a(r.contradicts,t))},p=await e.client.storeEventsBatch([f],e.graphId),[o]=p.results;if(!o||o.status==="error")throw new Error(o?.error??"The memory was not stored, and the server did not say why.");return o.status==="filtered"?{stored:!1,context:n,reason:o.error??"judged not worth keeping"}:{stored:!0,context:n,relevance:o.relevance}}})}export{v as remember};
@@ -0,0 +1,12 @@
1
+ import { type PaperSearcher, type PaperSearcherOptions } from "../papers.js";
2
+ export interface ConsensusOptions extends PaperSearcherOptions {
3
+ apiKey: string;
4
+ studyTypes?: string[];
5
+ humanOnly?: boolean;
6
+ controlledOnly?: boolean;
7
+ excludePreprints?: boolean;
8
+ minSampleSize?: number;
9
+ domain?: string;
10
+ journalName?: string;
11
+ }
12
+ export declare function consensus(opts: ConsensusOptions): PaperSearcher;
@@ -0,0 +1 @@
1
+ import{retrying as l}from"../../search/retry.js";import{DEFAULT_MAX_PAPERS as m,paperLimit as d}from"../papers.js";import{readReply as y}from"./reply.js";const f="https://api.consensus.app/v1/search",_="consensus",S=6e4,h=500;function g(e){return{...e.studyTypes?.length?{study_types:e.studyTypes.join(",")}:{},...e.humanOnly?{human:"true"}:{},...e.controlledOnly?{controlled:"true"}:{},...e.excludePreprints?{exclude_preprints:"true"}:{},...e.minSampleSize?{sample_size_min:String(e.minSampleSize)}:{},...e.domain?{domain:e.domain}:{},...e.journalName?{journal_name:e.journalName}:{}}}function p(e){return{...e.yearMin===void 0?{}:{year_min:String(e.yearMin)},...e.yearMax===void 0?{}:{year_max:String(e.yearMax)},...e.minCitations===void 0?{}:{citation_min:String(e.minCitations)},...e.openAccess?{open_access:"true"}:{}}}function w(e){if(!e.apiKey?.trim())throw new Error("consensus needs an apiKey; it will not read one from the process.");const s=e.fetch??globalThis.fetch.bind(globalThis),o=e.maxResults??m,c=l(e);return Object.assign(async i=>{const t=d(i,o),a=new URL(f);for(const[r,u]of Object.entries({query:i.query,page_size:String(t),...p(i),...g(e)}))a.searchParams.set(r,u);const n=await c(()=>s(a,{signal:AbortSignal.timeout(e.timeoutMs??S),headers:{"x-api-key":e.apiKey,accept:"application/json"}}));if(!n.ok){const r=(await n.text()).slice(0,h);throw new Error(`Consensus returned ${n.status}: ${r}`)}return y(await n.json(),t,e.costMicroUsd)},{source:_})}export{w as consensus};
@@ -0,0 +1,4 @@
1
+ export type { ConsensusOptions } from "./consensus.js";
2
+ export { consensus } from "./consensus.js";
3
+ export type { ConsensusReply } from "./reply.js";
4
+ export { readReply } from "./reply.js";
@@ -0,0 +1 @@
1
+ import{consensus as r}from"./consensus.js";import{readReply as s}from"./reply.js";export{r as consensus,s as readReply};
@@ -0,0 +1,24 @@
1
+ import type { PapersFound } from "../papers.js";
2
+ interface RawResult {
3
+ title?: string | null;
4
+ authors?: string[] | null;
5
+ publish_year?: number | null;
6
+ doi?: string | null;
7
+ journal_name?: string | null;
8
+ citation_count?: number | null;
9
+ study_type?: string | null;
10
+ sample_size?: number | null;
11
+ takeaway?: string | null;
12
+ abstract?: string | null;
13
+ url?: string | null;
14
+ pdf_url?: string | null;
15
+ }
16
+ export interface ConsensusReply {
17
+ results?: RawResult[];
18
+ page?: number;
19
+ page_size?: number;
20
+ is_end?: boolean;
21
+ next_page?: number | null;
22
+ }
23
+ export declare function readReply(raw: ConsensusReply, maxResults: number, costMicroUsd?: number): PapersFound;
24
+ export {};
@@ -0,0 +1 @@
1
+ const s="consensus";function i(t){return{title:t.title?.trim()||"(untitled)",year:t.publish_year??null,authors:t.authors??[],venue:t.journal_name||null,citations:t.citation_count??0,doi:t.doi?.replace(/^https?:\/\/doi\.org\//,"")||null,pdf:t.pdf_url||null,url:t.url||null,studyType:t.study_type||null,sampleSize:t.sample_size??null,takeaway:t.takeaway||null,abstract:t.abstract??""}}function o(t,u,e){const l=t.results??[],n=l.slice(0,u);return{papers:n.map(i),source:s,total:null,truncated:l.length>n.length||t.is_end===!1,...e===void 0?{}:{costMicroUsd:e}}}export{o as readReply};
@@ -0,0 +1,6 @@
1
+ import { type PaperSearcher } from "./papers.js";
2
+ export interface EveryPaperOptions {
3
+ maxResults?: number;
4
+ }
5
+ export declare function listed(names: string[]): string;
6
+ export declare function everyPaper(searchers: PaperSearcher[], opts?: EveryPaperOptions): PaperSearcher;
@@ -0,0 +1 @@
1
+ import{DEFAULT_MAX_PAPERS as h,paperLimit as m}from"./papers.js";function g(e){const t=[];for(let n=0;;n++){let o=!1;for(const a of e){const u=a[n];u!==void 0&&(o=!0,t.push(u))}if(!o)return t}}function w(e){return e.doi?`doi:${e.doi.toLowerCase()}`:`title:${e.title.toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}`}function v(e,t){return{title:e.title,year:e.year??t.year,authors:e.authors.length>0?e.authors:t.authors,venue:e.venue??t.venue,citations:Math.max(e.citations,t.citations),doi:e.doi??t.doi,pdf:e.pdf??t.pdf,url:e.url??t.url,studyType:e.studyType??t.studyType,sampleSize:e.sampleSize??t.sampleSize,takeaway:e.takeaway??t.takeaway,abstract:e.abstract||t.abstract}}function M(e){const t=new Map;for(const n of e){const o=w(n),a=t.get(o);t.set(o,a?v(a,n):n)}return[...t.values()]}const $=e=>e instanceof Error?e.message:String(e);function b(e){return e.map((t,n)=>t.source??`source ${n+1}`)}function f(e){return e.length<2?e[0]??"":`${e.slice(0,-1).join(", ")} and ${e.at(-1)}`}function x(e){const t=e.flatMap(n=>n.costMicroUsd===void 0?[]:[n.costMicroUsd]);return t.length>0?t.reduce((n,o)=>n+o,0):void 0}function E(e){const t=e.flatMap(n=>n.total===null?[]:[n.total]);return t.length>0?t.reduce((n,o)=>n+o,0):null}function L(e,t={}){if(e.length===0)throw new Error("everyPaper needs at least one paper searcher to run.");const n=b(e),o=t.maxResults??h;return Object.assign(async a=>{const u=m(a,o),p=await Promise.allSettled(e.map(r=>r({...a,limit:u}))),s=[],i=[];if(p.forEach((r,y)=>{r.status==="fulfilled"?s.push(r.value):i.push({source:n[y],reason:$(r.reason)})}),s.length===0)throw new Error(`No paper searcher answered. ${i.map(r=>`${r.source}: ${r.reason}`).join(" ")}`);const c=M(g(s.map(r=>r.papers))),l=c.slice(0,u),d=x(s);return{papers:l,source:f(s.map(r=>r.source)),total:E(s),truncated:c.length>l.length||s.some(r=>r.truncated),...d===void 0?{}:{costMicroUsd:d},...i.length>0?{failed:i}:{}}},{source:f(n)})}export{L as everyPaper,f as listed};
@@ -0,0 +1,11 @@
1
+ export type { ConsensusOptions, ConsensusReply } from "./consensus/index.js";
2
+ export { consensus } from "./consensus/index.js";
3
+ export type { EveryPaperOptions } from "./every.js";
4
+ export { everyPaper } from "./every.js";
5
+ export type { OpenAlexPapersOptions } from "./openalex/index.js";
6
+ export { openAlexPapers } from "./openalex/index.js";
7
+ export type { Paper, PaperAsk, PaperFailure, PaperFilters, PaperSearcher, PaperSearcherOptions, PapersFound, } from "./papers.js";
8
+ export { DEFAULT_ABSTRACT_CHARS, DEFAULT_MAX_PAPERS } from "./papers.js";
9
+ export { rendered } from "./render.js";
10
+ export type { SemanticScholarOptions, SemanticScholarReply } from "./semantic-scholar/index.js";
11
+ export { semanticScholar } from "./semantic-scholar/index.js";
@@ -0,0 +1 @@
1
+ import{consensus as o}from"./consensus/index.js";import{everyPaper as A}from"./every.js";import{openAlexPapers as t}from"./openalex/index.js";import{DEFAULT_ABSTRACT_CHARS as f,DEFAULT_MAX_PAPERS as n}from"./papers.js";import{rendered as a}from"./render.js";import{semanticScholar as S}from"./semantic-scholar/index.js";export{f as DEFAULT_ABSTRACT_CHARS,n as DEFAULT_MAX_PAPERS,o as consensus,A as everyPaper,t as openAlexPapers,a as rendered,S as semanticScholar};
@@ -0,0 +1,2 @@
1
+ export type { OpenAlexPapersOptions } from "./openalex.js";
2
+ export { openAlexPapers } from "./openalex.js";
@@ -0,0 +1 @@
1
+ import{openAlexPapers as p}from"./openalex.js";export{p as openAlexPapers};
@@ -0,0 +1,8 @@
1
+ import { type ApiOptions, type OpenAlexApi } from "../../builtin/openalex/api.js";
2
+ import { type PaperSearcher } from "../papers.js";
3
+ export interface OpenAlexPapersOptions extends ApiOptions {
4
+ maxResults?: number;
5
+ costMicroUsd?: number;
6
+ api?: OpenAlexApi;
7
+ }
8
+ export declare function openAlexPapers(opts?: OpenAlexPapersOptions): PaperSearcher;
@@ -0,0 +1 @@
1
+ import{openAlexApi as u}from"../../builtin/openalex/api.js";import{DEFAULT_MAX_PAPERS as c,paperLimit as l}from"../papers.js";const s="openalex";function p(e){return{...e,url:null,studyType:null,sampleSize:null,takeaway:null}}function d(e={}){const o=e.api??u(e),a=e.maxResults??c;return Object.assign(async n=>{const r=await o.search(n.query,l(n,a),n),t=r.costMicroUsd??e.costMicroUsd;return{papers:r.papers.map(p),source:s,total:null,truncated:!1,...t===void 0?{}:{costMicroUsd:t}}},{source:s})}export{d as openAlexPapers};
@@ -0,0 +1,54 @@
1
+ export interface Paper {
2
+ title: string;
3
+ year: number | null;
4
+ authors: string[];
5
+ venue: string | null;
6
+ citations: number;
7
+ doi: string | null;
8
+ pdf: string | null;
9
+ url: string | null;
10
+ studyType: string | null;
11
+ sampleSize: number | null;
12
+ takeaway: string | null;
13
+ abstract: string;
14
+ }
15
+ export interface PaperFilters {
16
+ yearMin?: number;
17
+ yearMax?: number;
18
+ openAccess?: boolean;
19
+ minCitations?: number;
20
+ }
21
+ export interface PaperAsk extends PaperFilters {
22
+ query: string;
23
+ limit?: number;
24
+ }
25
+ export interface PaperFailure {
26
+ source: string;
27
+ reason: string;
28
+ }
29
+ export interface PapersFound {
30
+ papers: Paper[];
31
+ source: string;
32
+ total: number | null;
33
+ truncated: boolean;
34
+ costMicroUsd?: number;
35
+ failed?: PaperFailure[];
36
+ }
37
+ export type PaperSearcher = ((ask: PaperAsk) => Promise<PapersFound>) & {
38
+ source?: string;
39
+ };
40
+ export interface PaperSearcherOptions {
41
+ maxResults?: number;
42
+ costMicroUsd?: number;
43
+ timeoutMs?: number;
44
+ attempts?: number;
45
+ fetch?: typeof fetch;
46
+ sleep?: (ms: number) => Promise<void>;
47
+ random?: () => number;
48
+ }
49
+ export declare const DEFAULT_MAX_PAPERS = 10;
50
+ export declare const DEFAULT_ABSTRACT_CHARS = 400;
51
+ export declare function paperLimit(ask: PaperAsk, ceiling: number): number;
52
+ export declare function yearRange({ yearMin, yearMax }: PaperFilters): string | null;
53
+ export declare const MICRO_USD = 1000000;
54
+ export declare const microUsd: (usd: number | undefined) => number | undefined;
@@ -0,0 +1 @@
1
+ const r=10,o=400;function i(t,n){return Math.max(1,Math.min(t.limit??n,n))}function u({yearMin:t,yearMax:n}){return t===void 0&&n===void 0?null:`${t??""}-${n??""}`}const e=1e6,p=t=>typeof t=="number"&&Number.isFinite(t)?Math.round(t*e):void 0;export{o as DEFAULT_ABSTRACT_CHARS,r as DEFAULT_MAX_PAPERS,e as MICRO_USD,p as microUsd,i as paperLimit,u as yearRange};
@@ -0,0 +1,2 @@
1
+ import type { Paper } from "./papers.js";
2
+ export declare function rendered(query: string, papers: readonly Paper[], maxChars: number): string;
@@ -0,0 +1,4 @@
1
+ const m=3;function c(n){const t=n.authors.slice(0,3).map(e=>e.split(" ").at(-1)??e),o=n.authors.length-t.length;return t.length===0?"unknown authors":o>0?`${t.join(", ")} +${o}`:t.join(", ")}function d(n){const t=n.year===null?"":` (${n.year})`,o=n.venue?` \xB7 ${n.venue}`:"";return`${n.title}${t} \u2014 ${c(n)}${o} \xB7 ${n.citations} citations`}function $(n){const o=[n.studyType,n.sampleSize===null?null:`n=${n.sampleSize}`].filter(e=>!!e);return o.length>0?o.join(" \xB7 "):null}function f(n){const o=[n.doi?`doi:${n.doi}`:null,n.pdf?`pdf: ${n.pdf}`:null].filter(e=>e!==null);return o.length>0?o.join(" \xB7 "):n.url}function h(n){const t=n.takeaway?.trim();return t?`Found: ${t}`:null}function a(n,t){const o=n.abstract.trim();return o?o.length>t?`${o.slice(0,t).trimEnd()}\u2026`:o:null}function g(n,t,o){const e=`${t.length} paper${t.length===1?"":"s"} on "${n}", most relevant first`,i=t.map((u,s)=>[`${s+1}. ${d(u)}`,$(u),f(u),h(u),a(u,o)].filter(l=>l!==null).map((l,r)=>r===0?l:` ${l}`).join(`
2
+ `));return[e,...i].join(`
3
+
4
+ `)}export{g as rendered};
@@ -0,0 +1,4 @@
1
+ export type { SemanticScholarReply } from "./reply.js";
2
+ export { readReply } from "./reply.js";
3
+ export type { SemanticScholarOptions } from "./semantic-scholar.js";
4
+ export { semanticScholar } from "./semantic-scholar.js";
@@ -0,0 +1 @@
1
+ import{readReply as o}from"./reply.js";import{semanticScholar as m}from"./semantic-scholar.js";export{o as readReply,m as semanticScholar};
@@ -0,0 +1,29 @@
1
+ import type { PapersFound } from "../papers.js";
2
+ interface RawPaper {
3
+ title?: string | null;
4
+ abstract?: string | null;
5
+ year?: number | null;
6
+ venue?: string | null;
7
+ citationCount?: number | null;
8
+ authors?: {
9
+ name?: string | null;
10
+ }[] | null;
11
+ externalIds?: {
12
+ DOI?: string | null;
13
+ } | null;
14
+ openAccessPdf?: {
15
+ url?: string | null;
16
+ } | null;
17
+ url?: string | null;
18
+ tldr?: {
19
+ text?: string | null;
20
+ } | null;
21
+ }
22
+ export interface SemanticScholarReply {
23
+ total?: number;
24
+ offset?: number;
25
+ next?: number;
26
+ data?: RawPaper[];
27
+ }
28
+ export declare function readReply(raw: SemanticScholarReply, maxResults: number, costMicroUsd?: number): PapersFound;
29
+ export {};
@@ -0,0 +1 @@
1
+ const s="semantic-scholar";function o(t){return{title:t.title?.trim()||"(untitled)",year:t.year??null,authors:(t.authors??[]).flatMap(l=>l.name?[l.name]:[]),venue:t.venue?.trim()||null,citations:t.citationCount??0,doi:t.externalIds?.DOI||null,pdf:t.openAccessPdf?.url||null,url:t.url||null,studyType:null,sampleSize:null,takeaway:t.tldr?.text||null,abstract:t.abstract??""}}function c(t,l,u){const a=t.data??[],n=a.slice(0,l),e=t.total??null;return{papers:n.map(o),source:s,total:e,truncated:a.length>n.length||e!==null&&e>n.length,...u===void 0?{}:{costMicroUsd:u}}}export{c as readReply};
@@ -0,0 +1,8 @@
1
+ import { type PaperSearcher, type PaperSearcherOptions } from "../papers.js";
2
+ export interface SemanticScholarOptions extends PaperSearcherOptions {
3
+ apiKey?: string;
4
+ fieldsOfStudy?: string[];
5
+ publicationTypes?: string[];
6
+ venues?: string[];
7
+ }
8
+ export declare function semanticScholar(opts?: SemanticScholarOptions): PaperSearcher;
@@ -0,0 +1 @@
1
+ import{retrying as u}from"../../search/retry.js";import{DEFAULT_MAX_PAPERS as f,paperLimit as d,yearRange as m}from"../papers.js";import{readReply as h}from"./reply.js";const y="https://api.semanticscholar.org/graph/v1/paper/search",p="semantic-scholar",S=6e4,g=500,A=["title","abstract","year","venue","citationCount","authors","externalIds","openAccessPdf","url","tldr"].join(",");function T(e){return{...e.fieldsOfStudy?.length?{fieldsOfStudy:e.fieldsOfStudy.join(",")}:{},...e.publicationTypes?.length?{publicationTypes:e.publicationTypes.join(",")}:{},...e.venues?.length?{venue:e.venues.join(",")}:{}}}function R(e){const t=m(e);return{...t===null?{}:{year:t},...e.minCitations===void 0?{}:{minCitationCount:String(e.minCitations)},...e.openAccess?{openAccessPdf:""}:{}}}function j(e={}){const t=e.fetch??globalThis.fetch.bind(globalThis),s=e.maxResults??f,c=u(e);return Object.assign(async i=>{const a=d(i,s),o=new URL(y);for(const[r,l]of Object.entries({query:i.query,limit:String(a),fields:A,...R(i),...T(e)}))o.searchParams.set(r,l);const n=await c(()=>t(o,{signal:AbortSignal.timeout(e.timeoutMs??S),headers:{accept:"application/json",...e.apiKey?{"x-api-key":e.apiKey}:{}}}));if(!n.ok){const r=(await n.text()).slice(0,g);throw new Error(`Semantic Scholar returned ${n.status}: ${r}`)}return h(await n.json(),a,e.costMicroUsd)},{source:p})}export{j as semanticScholar};
@@ -0,0 +1,6 @@
1
+ import { type Searcher } from "./search.js";
2
+ export interface EverySearchOptions {
3
+ maxChars?: number;
4
+ maxResults?: number;
5
+ }
6
+ export declare function everySearch(searchers: Searcher[], opts?: EverySearchOptions): Searcher;
@@ -0,0 +1,4 @@
1
+ import{DEFAULT_MAX_CHARS as T,DEFAULT_MAX_RESULTS as k}from"./search.js";const g={inputTokens:0,outputTokens:0,citationTokens:0,reasoningTokens:0,searchQueries:0};function w(e){return e.reduce((n,t)=>({inputTokens:n.inputTokens+t.inputTokens,outputTokens:n.outputTokens+t.outputTokens,citationTokens:n.citationTokens+t.citationTokens,reasoningTokens:n.reasoningTokens+t.reasoningTokens,searchQueries:n.searchQueries+t.searchQueries}),g)}function S(e){const n=[];for(let t=0;;t++){let r=!1;for(const u of e){const a=u[t];a!==void 0&&(r=!0,n.push(a))}if(!r)return n}}const d=e=>e.url.replace(/\/+$/,"").toLowerCase();function E(e){const n=new Set;return e.filter(t=>!n.has(d(t))&&n.add(d(t)))}const $=e=>e instanceof Error?e.message:String(e);function v(e){return e.map((n,t)=>n.source??`searcher ${t+1}`)}function x(e){if(e.every(n=>n.domains?.length))return[...new Set(e.flatMap(n=>[...n.domains??[]]))]}const j=e=>`# via ${e.model}
2
+ ${e.answer}`;function A(e,n={}){if(e.length===0)throw new Error("everySearch needs at least one searcher to run.");const t=v(e),r=n.maxChars??T,u=n.maxResults??k,a=x(e);return Object.assign(async m=>{const p=await Promise.allSettled(e.map(s=>s(m))),o=[],i=[];if(p.forEach((s,h)=>{s.status==="fulfilled"?o.push(s.value):i.push({source:t[h],reason:$(s.reason)})}),o.length===0)throw new Error(`No searcher answered. ${i.map(s=>`${s.source}: ${s.reason}`).join(" ")}`);const c=o.map(j).join(`
3
+
4
+ `),l=E(S(o.map(s=>s.results))),f=l.slice(0,u);return{answer:c.slice(0,r),results:f,model:o.map(s=>s.model).join(", "),truncated:c.length>r||l.length>f.length||o.some(s=>s.truncated),usage:w(o.map(s=>s.usage)),...i.length>0?{failed:i}:{}}},{source:t.join(", "),...a?{domains:a}:{}})}export{A as everySearch};
@@ -0,0 +1,7 @@
1
+ export type { EverySearchOptions } from "./every.js";
2
+ export { everySearch } from "./every.js";
3
+ export type { Recency, SearchAsk, SearchDepth, Searched, Searcher, SearcherOptions, SearchFailure, SearchUsage, Source, } from "./search.js";
4
+ export { DEPTHS, NO_TOKENS } from "./search.js";
5
+ export { type SearxngOptions, searxng, textOf } from "./searxng/index.js";
6
+ export { readReply, type SonarOptions, sonar } from "./sonar/index.js";
7
+ export { type TavilyOptions, tavily } from "./tavily/index.js";
@@ -0,0 +1 @@
1
+ import{everySearch as o}from"./every.js";import{DEPTHS as x,NO_TOKENS as f}from"./search.js";import{searxng as a,textOf as m}from"./searxng/index.js";import{readReply as O,sonar as S}from"./sonar/index.js";import{tavily as n}from"./tavily/index.js";export{x as DEPTHS,f as NO_TOKENS,o as everySearch,O as readReply,a as searxng,S as sonar,n as tavily,m as textOf};
@@ -0,0 +1,6 @@
1
+ import type { SearcherOptions } from "./search.js";
2
+ /**
3
+ * Sends again while the answer is a 429 or a 5xx and tries remain, then hands
4
+ * back whatever came last, ok or not, for the caller to read or refuse.
5
+ */
6
+ export declare function retrying(opts: Pick<SearcherOptions, "attempts" | "sleep" | "random">): (send: () => Promise<Response>) => Promise<Response>;
@@ -0,0 +1 @@
1
+ const M=500,f=8e3;function m(t){const r=t.headers.get("retry-after");if(!r)return null;const e=Number(r);return Number.isFinite(e)&&e>=0?e*1e3:null}const u=t=>t===429||t>=500;function A(t){const r=Math.max(1,t.attempts??3),e=t.sleep??(o=>new Promise(n=>setTimeout(n,o))),c=t.random??Math.random;return async o=>{for(let n=1;;n++){const s=await o();if(s.ok||!u(s.status)||n===r)return s;const a=Math.min(8e3,500*2**(n-1)),i=m(s)??a;await e(i+a*c())}}}export{A as retrying};
@@ -0,0 +1,69 @@
1
+ /** How much searching one call is worth. */
2
+ export type SearchDepth = "quick" | "pro" | "deep";
3
+ export declare const DEPTHS: readonly SearchDepth[];
4
+ export type Recency = "day" | "week" | "month" | "year";
5
+ export interface Source {
6
+ url: string;
7
+ title?: string;
8
+ date?: string;
9
+ }
10
+ export interface SearchUsage {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ citationTokens: number;
14
+ reasoningTokens: number;
15
+ searchQueries: number;
16
+ }
17
+ export interface SearchFailure {
18
+ source: string;
19
+ reason: string;
20
+ }
21
+ /** What every searcher hands back, whoever did the searching. */
22
+ export interface Searched {
23
+ answer: string;
24
+ results: Source[];
25
+ model: string;
26
+ truncated: boolean;
27
+ usage: SearchUsage;
28
+ failed?: SearchFailure[];
29
+ }
30
+ export interface SearchAsk {
31
+ query: string;
32
+ depth: SearchDepth;
33
+ recency?: Recency;
34
+ }
35
+ export type Searcher = ((ask: SearchAsk) => Promise<Searched>) & {
36
+ domains?: readonly string[];
37
+ source?: string;
38
+ };
39
+ /** What every searcher is configured with; each adds only where its answers come from. */
40
+ export interface SearcherOptions {
41
+ /** Hosts results are restricted to. Set here, so the model cannot widen it. */
42
+ domains?: string[];
43
+ /** Give up on a request after this. */
44
+ timeoutMs?: number;
45
+ /** Longest answer handed back. Default 20000 characters. */
46
+ maxChars?: number;
47
+ /** Longest source list handed back. Default 20. */
48
+ maxResults?: number;
49
+ /**
50
+ * Tries for a rate limit or a bad minute on the other side. Default 3.
51
+ *
52
+ * A wave of sub-questions searches in parallel, so two calls landing together
53
+ * is ordinary and a 429 on the second is not a failure worth surfacing — it
54
+ * is a wait. `retry-after` is honoured where the server sends one, and the
55
+ * wait is spread by up to one backoff on top: a whole wave limited at the
56
+ * same instant computes the same delay, and retrying in step rebuilds the
57
+ * burst that caused it.
58
+ */
59
+ attempts?: number;
60
+ /** Injected for tests. */
61
+ fetch?: typeof fetch;
62
+ /** Injected for tests. */
63
+ sleep?: (ms: number) => Promise<void>;
64
+ /** Injected for tests. Returns 0 to 1, and spreads a retried wave apart. */
65
+ random?: () => number;
66
+ }
67
+ export declare const DEFAULT_MAX_CHARS = 20000;
68
+ export declare const DEFAULT_MAX_RESULTS = 20;
69
+ export declare const NO_TOKENS: SearchUsage;
@@ -0,0 +1 @@
1
+ const o=["quick","pro","deep"],e=2e4,t=20,n={inputTokens:0,outputTokens:0,citationTokens:0,reasoningTokens:0,searchQueries:1};export{e as DEFAULT_MAX_CHARS,t as DEFAULT_MAX_RESULTS,o as DEPTHS,n as NO_TOKENS};
@@ -0,0 +1,2 @@
1
+ export { type SearxngOptions, searxng } from "./searxng.js";
2
+ export { textOf } from "./text.js";
@@ -0,0 +1 @@
1
+ import{searxng as o}from"./searxng.js";import{textOf as x}from"./text.js";export{o as searxng,x as textOf};
@@ -0,0 +1,14 @@
1
+ import { type SearchDepth, type Searcher, type SearcherOptions } from "../search.js";
2
+ export interface SearxngOptions extends SearcherOptions {
3
+ /** The SearXNG instance, such as http://localhost:8080. Required. */
4
+ baseUrl: string;
5
+ /** Pin which engines answer. Default: whatever the instance runs. */
6
+ engines?: string[];
7
+ /** Pin the language. Default: the instance's own. */
8
+ language?: string;
9
+ /** Text kept from each page read. Default 6000 characters. */
10
+ pageChars?: number;
11
+ /** Pages read at each depth. Default 5, 8 and 10. */
12
+ pages?: Partial<Record<SearchDepth, number>>;
13
+ }
14
+ export declare function searxng(opts: SearxngOptions): Searcher;
@@ -0,0 +1,5 @@
1
+ import{checkUrl as x}from"../../builtin/http/allow.js";import{retrying as y}from"../retry.js";import{DEFAULT_MAX_CHARS as N,DEFAULT_MAX_RESULTS as F,NO_TOKENS as A}from"../search.js";import{textOf as T}from"./text.js";const f="searxng",U=["*"],G=2e4,X=6e3,L=4e5,H={quick:5,pro:8,deep:10},I=/https?:\/\/[^\s"'<>)\]]+/,Q=2;function W(t){return!t.url||!t.title?null:{url:t.url,title:t.title,...t.publishedDate?{date:t.publishedDate}:{},snippet:t.content??""}}function q({url:t,title:r,date:i}){return{url:t,title:r,...i?{date:i}:{}}}function K(t,r){return`## ${t.title}
2
+ ${t.url}
3
+ ${r}`}const Y=3,v=t=>t.status>=300&&t.status<400;function R(t,r){if(!r?.length)return!0;const i=new URL(t.url).hostname.toLowerCase();return r.some(u=>{const h=u.toLowerCase().replace(/^\./,"");return i===h||i.endsWith(`.${h}`)})}function z(t){const r=new Set;return t.flatMap(i=>i.hits).filter(i=>!r.has(i.url)&&r.add(i.url))}function tt(t){if(!t.baseUrl?.trim())throw new Error("searxng needs the baseUrl of a SearXNG instance, such as http://localhost:8080.");const r=t.fetch??globalThis.fetch.bind(globalThis),i=t.baseUrl.replace(/\/+$/,""),u=t.timeoutMs??G,h=Math.max(1,t.maxResults??F),g=t.maxChars??N,d=t.pageChars??X,b={...H,...t.pages},O=y(t);async function p(n,s){const e=new URL(`${i}/search`);e.searchParams.set("q",n),e.searchParams.set("format","json"),s&&e.searchParams.set("time_range",s),t.language&&e.searchParams.set("language",t.language),t.engines?.length&&e.searchParams.set("engines",t.engines.join(","));const a=await O(()=>r(e,{signal:AbortSignal.timeout(u),headers:{accept:"application/json"}}));if(!a.ok){const o=a.status===403?" SearXNG refuses the json format unless settings.yml lists it under search.formats and the limiter is off.":"";throw new Error(`SearXNG returned ${a.status}.${o}`)}const c=await a.json();return{hits:(c.results??[]).flatMap(o=>{const l=W(o);return l&&R(l,t.domains)?[l]:[]}),suggestions:c.suggestions??[]}}async function P(n,s){const e=n.suggestions.slice(0,Q);return[n,...await Promise.all(e.map(a=>p(a,s)))]}async function w(n){const s=AbortSignal.timeout(u);let e=x(n,U),a=await r(e,{signal:s,redirect:"manual"});for(let c=0;c<Y&&v(a);c++){const o=a.headers.get("location");if(!o)break;e=x(new URL(o,a.url||e).toString(),U),a=await r(e,{signal:s,redirect:"manual"})}return a}async function M(n){try{const s=await w(n.url);if(!s.ok)return n.snippet;const e=T((await s.text()).slice(0,L));return e?e.slice(0,d):n.snippet}catch{return n.snippet}}async function D(n){if(!R({url:n,snippet:""},t.domains))return null;try{const s=await w(n);return s.ok&&T((await s.text()).slice(0,L)).slice(0,d)||null}catch{return null}}const C=(n,s)=>({answer:s.slice(0,g),results:[{url:n}],model:f,truncated:s.length>g,usage:{...A,searchQueries:0}});return Object.assign(async({query:n,depth:s,recency:e})=>{const a=n.match(I)?.[0],c=a?await D(a):null;if(a&&c)return C(a,c);const o=await p(n,e),l=s==="deep"?await P(o,e):[o],_=z(l),m=_.slice(0,h),E=m.slice(0,b[s]),k=await Promise.all(E.map(M)),S=E.map(($,j)=>K($,k[j])).join(`
4
+
5
+ `);return{answer:S.slice(0,g),results:m.map(q),model:f,truncated:S.length>g||_.length>m.length,usage:{...A,searchQueries:l.length}}},{source:f,...t.domains?.length?{domains:[...t.domains]}:{}})}export{tt as searxng};
@@ -0,0 +1,2 @@
1
+ /** A page's readable text: the main content where it is marked, with menus, scripts and markup gone. */
2
+ export declare function textOf(html: string): string;
@@ -0,0 +1,3 @@
1
+ const r=/<(nav|header|footer|aside|form)\b[\s\S]*?<\/\1>/gi,c=/<(main|article)\b[^>]*>([\s\S]*?)<\/\1>/i;function t(e){return e.match(c)?.[2]??e}function a(e){return t(e).replace(/<!--[\s\S]*?-->/g," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<noscript[\s\S]*?<\/noscript>/gi," ").replace(r," ").replace(/<\/(p|div|li|h[1-6]|tr|br|section|article)>/gi,`
2
+ `).replace(/<[^>]+>/g," ").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;|&apos;/g,"'").replace(/[ \t]+/g," ").replace(/\s*\n\s*/g,`
3
+ `).trim()}export{a as textOf};
@@ -0,0 +1,2 @@
1
+ export { readReply } from "./reply.js";
2
+ export { type SonarOptions, sonar } from "./sonar.js";
@@ -0,0 +1 @@
1
+ import{readReply as e}from"./reply.js";import{sonar as a}from"./sonar.js";export{e as readReply,a as sonar};
@@ -0,0 +1,13 @@
1
+ import { type Searcher, type SearcherOptions } from "../search.js";
2
+ export interface SonarOptions extends SearcherOptions {
3
+ /** Perplexity key. Required, and read from here rather than the environment. */
4
+ apiKey: string;
5
+ }
6
+ /**
7
+ * The live web through Perplexity's Sonar models, one model per depth.
8
+ *
9
+ * Perplexity searches, reads and writes the answer itself, with citation
10
+ * marks, and bills citation and reasoning tokens on top of the usual two.
11
+ * The timeout defaults to a minute, or five on a deep search.
12
+ */
13
+ export declare function sonar(opts: SonarOptions): Searcher;
@@ -0,0 +1 @@
1
+ import{retrying as m}from"../retry.js";import{DEFAULT_MAX_CHARS as d,DEFAULT_MAX_RESULTS as h}from"../search.js";import{readReply as _}from"./reply.js";const E={quick:"sonar",pro:"sonar-pro",deep:"sonar-deep-research"},f="sonar",p="https://api.perplexity.ai/chat/completions",u=6e4,T=3e5,y=500;function O(e){if(!e.apiKey?.trim())throw new Error("sonar needs a Perplexity apiKey; it will not read one from the process.");const a=e.fetch??globalThis.fetch.bind(globalThis),i=m(e);return Object.assign(async({query:s,depth:o,recency:n})=>{const r=E[o],c=e.timeoutMs??(o==="deep"?T:u),t=await i(()=>a(p,{method:"POST",signal:AbortSignal.timeout(c),headers:{authorization:`Bearer ${e.apiKey}`,"content-type":"application/json"},body:JSON.stringify({model:r,messages:[{role:"user",content:s}],...n?{search_recency_filter:n}:{},...e.domains?.length?{search_domain_filter:e.domains}:{}})}));if(!t.ok){const l=(await t.text()).slice(0,y);throw new Error(`Perplexity returned ${t.status} for ${r}: ${l}`)}return _(await t.json(),r,e.maxChars??d,e.maxResults??h)},{source:f,...e.domains?.length?{domains:[...e.domains]}:{}})}export{O as sonar};
@@ -0,0 +1,4 @@
1
+ export type { TavilyReply } from "./reply.js";
2
+ export { readReply } from "./reply.js";
3
+ export type { TavilyOptions } from "./tavily.js";
4
+ export { tavily } from "./tavily.js";
@@ -0,0 +1 @@
1
+ import{readReply as o}from"./reply.js";import{tavily as t}from"./tavily.js";export{o as readReply,t as tavily};
@@ -0,0 +1,22 @@
1
+ import { type Searched } from "../search.js";
2
+ interface Hit {
3
+ url?: string;
4
+ title?: string;
5
+ content?: string;
6
+ raw_content?: string | null;
7
+ published_date?: string;
8
+ }
9
+ export interface TavilyReply {
10
+ answer?: string | null;
11
+ results?: Hit[];
12
+ auto_parameters?: Record<string, unknown>;
13
+ }
14
+ /**
15
+ * Tavily's reply as a `Searched`.
16
+ *
17
+ * Its own `answer` leads where it gave one, because it is a summary written
18
+ * across every result; the per-result text follows so a later stage can quote
19
+ * a source rather than the summary of it.
20
+ */
21
+ export declare function readReply(raw: TavilyReply, model: string, maxChars: number, maxResults: number): Searched;
22
+ export {};
@@ -0,0 +1,5 @@
1
+ import{NO_TOKENS as c}from"../search.js";function i(e){return e.url?{url:e.url,...e.title?{title:e.title}:{},...e.published_date?{date:e.published_date}:{}}:null}function a(e){const t=(e.raw_content??e.content??"").trim();return`## ${e.title??e.url}
2
+ ${e.url}
3
+ ${t}`}function p(e,t,r,s){const u=(e.results??[]).filter(l=>!!l.url),n=u.slice(0,s),o=[e.answer?.trim(),...n.map(a)].filter(Boolean).join(`
4
+
5
+ `);return{answer:o.slice(0,r),results:n.map(i).filter(l=>l!==null),model:t,truncated:o.length>r||u.length>n.length,usage:{...c}}}export{p as readReply};
@@ -0,0 +1,14 @@
1
+ import { type Searcher, type SearcherOptions } from "../search.js";
2
+ export interface TavilyOptions extends SearcherOptions {
3
+ /** Tavily key. Required, and read from here rather than the environment. */
4
+ apiKey: string;
5
+ }
6
+ /**
7
+ * The live web through Tavily, which searches and extracts in one call.
8
+ *
9
+ * Built for retrieval rather than for a person reading a results page: it
10
+ * returns the text of each page alongside its url, so a finding keeps both
11
+ * what was said and where it was said. Depth buys a wider search and more
12
+ * pages read, not a different model.
13
+ */
14
+ export declare function tavily(opts: TavilyOptions): Searcher;
@@ -0,0 +1 @@
1
+ import{retrying as m}from"../retry.js";import{DEFAULT_MAX_CHARS as u,DEFAULT_MAX_RESULTS as n}from"../search.js";import{readReply as _}from"./reply.js";const y="https://api.tavily.com/search",i="tavily-search",g=6e4,p=500,T={quick:{search_depth:"basic",pages:3},pro:{search_depth:"advanced",pages:5},deep:{search_depth:"advanced",pages:10}},E={day:1,week:7,month:30,year:365};function S(e){if(!e.apiKey?.trim())throw new Error("tavily needs an apiKey; it will not read one from the process.");const r=e.fetch??globalThis.fetch.bind(globalThis),s=m(e);return Object.assign(async({query:o,depth:c,recency:t})=>{const{search_depth:d,pages:h}=T[c],a=await s(()=>r(y,{method:"POST",signal:AbortSignal.timeout(e.timeoutMs??g),headers:{authorization:`Bearer ${e.apiKey}`,"content-type":"application/json"},body:JSON.stringify({query:o,search_depth:d,include_answer:!0,include_raw_content:!0,max_results:Math.min(h,e.maxResults??n),...t?{days:E[t]}:{},...e.domains?.length?{include_domains:e.domains}:{}})}));if(!a.ok){const l=(await a.text()).slice(0,p);throw new Error(`Tavily returned ${a.status}: ${l}`)}return _(await a.json(),i,e.maxChars??u,e.maxResults??n)},{source:i,...e.domains?.length?{domains:[...e.domains]}:{}})}export{S as tavily};
@@ -0,0 +1,2 @@
1
+ export { type Replay, replaying, type Unrecorded } from "./replay.js";
2
+ export { type MemorySeed, type MemoryStore, memoryStore, type ObservedMoment, type StoredMemory, } from "./store.js";
@@ -0,0 +1 @@
1
+ import{replaying as e}from"./replay.js";import{memoryStore as p}from"./store.js";export{p as memoryStore,e as replaying};
@@ -0,0 +1,55 @@
1
+ import type { ToolResult } from "../types/index.js";
2
+ /** A call the recording had no answer for. */
3
+ export interface Unrecorded {
4
+ name: string;
5
+ args: Record<string, unknown>;
6
+ }
7
+ export interface ReplayOptions {
8
+ /**
9
+ * Tools that must still run for real, by name.
10
+ *
11
+ * A replay stands in for the world, not for the agent's own bookkeeping.
12
+ * `finish` carries the answer — the thing actually under test — so replaying
13
+ * it would mean a reworded prompt could only ever produce the old wording,
14
+ * and the run stalls the first time it says anything differently. `think` and
15
+ * `notes` are the same: they change nothing outside the process, so there is
16
+ * nothing to stand in for.
17
+ *
18
+ * Their recordings are dropped rather than kept unused, because a result
19
+ * nothing was ever going to ask for is not a step the new run skipped.
20
+ */
21
+ live?: readonly string[];
22
+ /** What runs the tools named in `live`. Required with it, and refused without. */
23
+ on?: {
24
+ invoke(name: string, args: Record<string, unknown>): Promise<ToolResult>;
25
+ };
26
+ }
27
+ export interface Replay {
28
+ /** Hands back what the recording holds, or a failed result saying it does not. */
29
+ invoke(name: string, args: Record<string, unknown>): Promise<ToolResult>;
30
+ /** Calls this run made that the recording could not answer, in order. */
31
+ unrecorded: Unrecorded[];
32
+ /** Recorded results this run never asked for. */
33
+ unused(): ToolResult[];
34
+ }
35
+ /**
36
+ * The tool results a run already produced, handed back instead of running anything.
37
+ *
38
+ * What a recorded trace is for: re-run the reasoning against a new prompt or a
39
+ * new model while the tools answer exactly as they did, so the only thing that
40
+ * changed is the thing under test. Nothing reaches a database, an API or a
41
+ * customer, and the comparison is not confounded by the world having moved.
42
+ *
43
+ * Matched on the arguments as well as the name, which is the whole value and
44
+ * also the part that looks like a limitation. A replay that answered
45
+ * `track("ORD-9")` with the recording of `track("ORD-1")` would be a green run
46
+ * proving nothing. A call the recording cannot answer is the finding: the new
47
+ * prompt asked something the old one never did, and the recording is not
48
+ * evidence about it either way.
49
+ *
50
+ * It offers no `effectOf`, and that is not an omission. Nothing runs during a
51
+ * replay, so nothing is destructive — a rail that gates on effect has nothing
52
+ * to guard, and giving it the original effects would have it blocking calls
53
+ * that are already only paper.
54
+ */
55
+ export declare function replaying(recorded: readonly ToolResult[], opts?: ReplayOptions): Replay;
@@ -0,0 +1 @@
1
+ const u=(i,n,e)=>i.name===n&&JSON.stringify(i.args)===JSON.stringify(e),f=(i,n,e)=>({name:i,args:n,status:"failed",output:"",error:`Nothing recorded for ${i}(${JSON.stringify(n)}). The recording holds: ${[...new Set(e.map(o=>o.name))].join(", ")||"nothing"}`,latencyMs:0});function l(i,n={}){const e=new Set(n.live??[]);if(e.size>0&&!n.on)throw new Error(`replaying names ${[...e].join(", ")} as live, but nothing to run them on.`);const o=i.filter(t=>!e.has(t.name)),s=new Set,c=[];return{unrecorded:c,unused:()=>o.filter((t,r)=>!s.has(r)),invoke:async(t,r)=>{if(e.has(t))return n.on.invoke(t,r);const a=o.findIndex((d,h)=>!s.has(h)&&u(d,t,r));return a===-1?(c.push({name:t,args:r}),f(t,r,o)):(s.add(a),{...o[a]})}}}export{l as replaying};
@@ -0,0 +1,35 @@
1
+ import type { MemoryClient, MemorySource } from "../memories/client.js";
2
+ export interface StoredMemory {
3
+ id: string;
4
+ context: string;
5
+ kind: string;
6
+ source: string;
7
+ text: string;
8
+ details: Record<string, unknown>;
9
+ extends: string[];
10
+ related_to: string[];
11
+ reinforces: string[];
12
+ contradicts: string[];
13
+ }
14
+ export interface ObservedMoment {
15
+ source: string;
16
+ payload: string | Record<string, unknown>;
17
+ stream?: string;
18
+ }
19
+ export interface MemorySeed {
20
+ context: string;
21
+ text: string;
22
+ details?: Record<string, unknown>;
23
+ kind?: string;
24
+ source?: MemorySource;
25
+ extends?: string[];
26
+ related_to?: string[];
27
+ reinforces?: string[];
28
+ contradicts?: string[];
29
+ }
30
+ export interface MemoryStore extends MemoryClient {
31
+ memories: StoredMemory[];
32
+ observed: ObservedMoment[];
33
+ remember(seed: MemorySeed): StoredMemory;
34
+ }
35
+ export declare function memoryStore(seeds?: MemorySeed[]): MemoryStore;