@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.
- package/README.md +3 -2
- package/dist/builtin/calculate/calculate.d.ts +2 -0
- package/dist/builtin/calculate/calculate.js +1 -1
- package/dist/builtin/calculate/index.d.ts +2 -1
- package/dist/builtin/calculate/index.js +1 -1
- package/dist/builtin/code-exec/code-exec.d.ts +14 -0
- package/dist/builtin/date/date.d.ts +1 -1
- package/dist/builtin/date/date.js +1 -1
- package/dist/builtin/date/index.d.ts +1 -0
- package/dist/builtin/date/index.js +1 -1
- package/dist/builtin/date/shift.d.ts +1 -1
- package/dist/builtin/date/shift.js +1 -1
- package/dist/builtin/date/words.d.ts +13 -0
- package/dist/builtin/date/words.js +1 -0
- package/dist/builtin/decline/decline.d.ts +49 -0
- package/dist/builtin/decline/decline.js +1 -0
- package/dist/builtin/decline/index.d.ts +1 -0
- package/dist/builtin/decline/index.js +1 -0
- package/dist/builtin/escalate/escalate.d.ts +19 -0
- package/dist/builtin/escalate/escalate.js +1 -1
- package/dist/builtin/escalate/index.d.ts +1 -1
- package/dist/builtin/escalate/index.js +1 -1
- package/dist/builtin/extract/extract.d.ts +25 -0
- package/dist/builtin/extract/extract.js +1 -0
- package/dist/builtin/extract/index.d.ts +3 -0
- package/dist/builtin/extract/index.js +1 -0
- package/dist/builtin/extract/kinds.d.ts +6 -0
- package/dist/builtin/extract/kinds.js +1 -0
- package/dist/builtin/extract/readers.d.ts +7 -0
- package/dist/builtin/extract/readers.js +9 -0
- package/dist/builtin/finish/finish.d.ts +4 -0
- package/dist/builtin/finish/finish.js +1 -1
- package/dist/builtin/http/allow.d.ts +2 -0
- package/dist/builtin/http/allow.js +1 -1
- package/dist/builtin/http/hops.d.ts +10 -0
- package/dist/builtin/http/hops.js +1 -0
- package/dist/builtin/http/http.d.ts +9 -1
- package/dist/builtin/http/http.js +1 -1
- package/dist/builtin/http/index.d.ts +2 -0
- package/dist/builtin/http/index.js +1 -1
- package/dist/builtin/http/retry-after.d.ts +14 -0
- package/dist/builtin/http/retry-after.js +1 -0
- package/dist/builtin/index.d.ts +11 -6
- package/dist/builtin/index.js +1 -1
- package/dist/builtin/json/index.d.ts +1 -0
- package/dist/builtin/json/index.js +1 -1
- package/dist/builtin/json/json.js +1 -1
- package/dist/builtin/json/parse.d.ts +15 -0
- package/dist/builtin/json/parse.js +1 -0
- package/dist/builtin/notes/index.d.ts +1 -0
- package/dist/builtin/notes/index.js +1 -0
- package/dist/builtin/notes/notes.d.ts +45 -0
- package/dist/builtin/notes/notes.js +2 -0
- package/dist/builtin/openalex/api.d.ts +26 -0
- package/dist/builtin/openalex/api.js +1 -0
- package/dist/builtin/openalex/index.d.ts +3 -0
- package/dist/builtin/openalex/index.js +1 -0
- package/dist/builtin/openalex/openalex.d.ts +36 -0
- package/dist/builtin/openalex/openalex.js +1 -0
- package/dist/builtin/paper-search/index.d.ts +1 -0
- package/dist/builtin/paper-search/index.js +1 -0
- package/dist/builtin/paper-search/paper-search.d.ts +38 -0
- package/dist/builtin/paper-search/paper-search.js +3 -0
- package/dist/builtin/polite.d.ts +15 -0
- package/dist/builtin/polite.js +1 -0
- package/dist/builtin/publish/index.d.ts +1 -0
- package/dist/builtin/publish/index.js +1 -0
- package/dist/builtin/publish/publish.d.ts +46 -0
- package/dist/builtin/publish/publish.js +1 -0
- package/dist/builtin/web-search/web-search.d.ts +10 -35
- package/dist/builtin/web-search/web-search.js +1 -1
- package/dist/builtin/wikidata/api.d.ts +40 -0
- package/dist/builtin/wikidata/api.js +1 -0
- package/dist/builtin/wikidata/index.d.ts +5 -0
- package/dist/builtin/wikidata/index.js +1 -0
- package/dist/builtin/wikidata/render.d.ts +13 -0
- package/dist/builtin/wikidata/render.js +2 -0
- package/dist/builtin/wikidata/wikidata.d.ts +23 -0
- package/dist/builtin/wikidata/wikidata.js +1 -0
- package/dist/bus/bus.d.ts +23 -1
- package/dist/bus/bus.js +1 -1
- package/dist/bus/group.d.ts +14 -1
- package/dist/bus/group.js +1 -1
- package/dist/bus/index.d.ts +4 -1
- package/dist/bus/index.js +1 -1
- package/dist/bus/invoke.d.ts +1 -1
- package/dist/bus/invoke.js +3 -1
- package/dist/bus/namespace.d.ts +21 -0
- package/dist/bus/namespace.js +1 -0
- package/dist/bus/once.d.ts +32 -0
- package/dist/bus/once.js +1 -0
- package/dist/bus/only.d.ts +29 -0
- package/dist/bus/options.d.ts +31 -0
- package/dist/bus/output.d.ts +0 -17
- package/dist/bus/output.js +2 -2
- package/dist/bus/select.d.ts +0 -16
- package/dist/bus/select.js +1 -1
- package/dist/bus/spent.d.ts +16 -0
- package/dist/bus/spent.js +1 -0
- package/dist/bus/trace.d.ts +14 -1
- package/dist/bus/trace.js +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1 -1
- package/dist/internals.d.ts +8 -1
- package/dist/internals.js +1 -1
- package/dist/memories/client.d.ts +91 -0
- package/dist/memories/client.js +0 -0
- package/dist/memories/context.d.ts +5 -0
- package/dist/memories/context.js +1 -0
- package/dist/{builtin → memories}/deep-recall/deep-recall.d.ts +3 -0
- package/dist/memories/deep-recall/deep-recall.js +1 -0
- package/dist/memories/explore/explore.d.ts +18 -0
- package/dist/memories/explore/explore.js +7 -0
- package/dist/memories/explore/subgraph.d.ts +12 -0
- package/dist/memories/explore/subgraph.js +3 -0
- package/dist/memories/index.d.ts +30 -0
- package/dist/memories/index.js +1 -0
- package/dist/memories/observe/observe.d.ts +7 -0
- package/dist/memories/observe/observe.js +1 -0
- package/dist/memories/recall/recall.d.ts +42 -0
- package/dist/memories/recall/recall.js +1 -0
- package/dist/memories/recall/render.d.ts +5 -0
- package/dist/memories/recall/render.js +3 -0
- package/dist/memories/remember/remember.d.ts +49 -0
- package/dist/memories/remember/remember.js +1 -0
- package/dist/papers/consensus/consensus.d.ts +12 -0
- package/dist/papers/consensus/consensus.js +1 -0
- package/dist/papers/consensus/index.d.ts +4 -0
- package/dist/papers/consensus/index.js +1 -0
- package/dist/papers/consensus/reply.d.ts +24 -0
- package/dist/papers/consensus/reply.js +1 -0
- package/dist/papers/every.d.ts +6 -0
- package/dist/papers/every.js +1 -0
- package/dist/papers/index.d.ts +11 -0
- package/dist/papers/index.js +1 -0
- package/dist/papers/openalex/index.d.ts +2 -0
- package/dist/papers/openalex/index.js +1 -0
- package/dist/papers/openalex/openalex.d.ts +8 -0
- package/dist/papers/openalex/openalex.js +1 -0
- package/dist/papers/papers.d.ts +54 -0
- package/dist/papers/papers.js +1 -0
- package/dist/papers/render.d.ts +2 -0
- package/dist/papers/render.js +4 -0
- package/dist/papers/semantic-scholar/index.d.ts +4 -0
- package/dist/papers/semantic-scholar/index.js +1 -0
- package/dist/papers/semantic-scholar/reply.d.ts +29 -0
- package/dist/papers/semantic-scholar/reply.js +1 -0
- package/dist/papers/semantic-scholar/semantic-scholar.d.ts +8 -0
- package/dist/papers/semantic-scholar/semantic-scholar.js +1 -0
- package/dist/search/every.d.ts +6 -0
- package/dist/search/every.js +4 -0
- package/dist/search/index.d.ts +7 -0
- package/dist/search/index.js +1 -0
- package/dist/search/retry.d.ts +6 -0
- package/dist/search/retry.js +1 -0
- package/dist/search/search.d.ts +69 -0
- package/dist/search/search.js +1 -0
- package/dist/search/searxng/index.d.ts +2 -0
- package/dist/search/searxng/index.js +1 -0
- package/dist/search/searxng/searxng.d.ts +14 -0
- package/dist/search/searxng/searxng.js +5 -0
- package/dist/search/searxng/text.d.ts +2 -0
- package/dist/search/searxng/text.js +3 -0
- package/dist/search/sonar/index.d.ts +2 -0
- package/dist/search/sonar/index.js +1 -0
- package/dist/search/sonar/sonar.d.ts +13 -0
- package/dist/search/sonar/sonar.js +1 -0
- package/dist/search/tavily/index.d.ts +4 -0
- package/dist/search/tavily/index.js +1 -0
- package/dist/search/tavily/reply.d.ts +22 -0
- package/dist/search/tavily/reply.js +5 -0
- package/dist/search/tavily/tavily.d.ts +14 -0
- package/dist/search/tavily/tavily.js +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/replay.d.ts +55 -0
- package/dist/testing/replay.js +1 -0
- package/dist/testing/store.d.ts +35 -0
- package/dist/testing/store.js +4 -0
- package/dist/tool/tool.d.ts +8 -1
- package/dist/tool/tool.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/result.d.ts +38 -0
- package/dist/types/tool.d.ts +74 -0
- package/docs/bogus.mdx +85 -0
- package/docs/builtins.mdx +217 -0
- package/docs/bus.mdx +90 -0
- package/docs/declaring.mdx +85 -0
- package/docs/effects.mdx +49 -0
- package/docs/overview.mdx +76 -0
- package/docs/selecting.mdx +79 -0
- package/docs/stored.mdx +83 -0
- package/docs/turns.mdx +57 -0
- package/package.json +28 -9
- package/dist/builtin/deep-recall/deep-recall.js +0 -1
- package/dist/builtin/write-document/commission.d.ts +0 -56
- package/dist/builtin/write-document/commission.js +0 -13
- package/dist/builtin/write-document/design.d.ts +0 -49
- package/dist/builtin/write-document/design.js +0 -5
- package/dist/builtin/write-document/http.d.ts +0 -34
- package/dist/builtin/write-document/http.js +0 -1
- package/dist/builtin/write-document/index.d.ts +0 -6
- package/dist/builtin/write-document/index.js +0 -1
- package/dist/builtin/write-document/types.d.ts +0 -33
- package/dist/builtin/write-document/write-document.d.ts +0 -69
- package/dist/builtin/write-document/write-document.js +0 -1
- /package/dist/{builtin/write-document/types.js → bus/only.js} +0 -0
- /package/dist/{builtin → memories}/deep-recall/index.d.ts +0 -0
- /package/dist/{builtin → memories}/deep-recall/index.js +0 -0
- /package/dist/{builtin/web-search → search/sonar}/reply.d.ts +0 -0
- /package/dist/{builtin/web-search → search/sonar}/reply.js +0 -0
package/README.md
CHANGED
|
@@ -28,7 +28,8 @@ const turn = bus.trace(question);
|
|
|
28
28
|
has said anything, and more tools makes a model worse at picking.
|
|
29
29
|
- **Effects**, so a gate can refuse what it must not retry — and name the tools
|
|
30
30
|
nobody labelled.
|
|
31
|
-
- **
|
|
31
|
+
- **Twenty-two builtins and twelve mocks**, none registered for you. Four of them
|
|
32
|
+
are one memory set, with an in-process store for testing it offline.
|
|
32
33
|
|
|
33
34
|
## Docs
|
|
34
35
|
|
|
@@ -41,7 +42,7 @@ const turn = bus.trace(question);
|
|
|
41
42
|
| [The bus](https://spendgraph.locusgraph.com/docs/tools/bus) | Holds, picks, converts, runs — and nests |
|
|
42
43
|
| [Selecting](https://spendgraph.locusgraph.com/docs/tools/selecting) | The shortlist, and why it is a shortlist |
|
|
43
44
|
| [A turn](https://spendgraph.locusgraph.com/docs/tools/turns) | Offered, called, and what the rollout keeps |
|
|
44
|
-
| [Builtins](https://spendgraph.locusgraph.com/docs/tools/builtins) |
|
|
45
|
+
| [Builtins](https://spendgraph.locusgraph.com/docs/tools/builtins) | Sixteen factories, none registered for you |
|
|
45
46
|
| [Bogus tools](https://spendgraph.locusgraph.com/docs/tools/bogus) | Twelve mocks for testing the pipeline, not the provider |
|
|
46
47
|
|
|
47
48
|
## License
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as
|
|
1
|
+
import{tool as a}from"../../tool/index.js";import{exact as n}from"./exact.js";import{evaluate as o}from"./parse.js";const c=/[0-9+\-*/%^().\s]+/g;function u(r){return(r.match(c)??[]).map(t=>t.trim()).filter(t=>/\d/.test(t)&&/[+\-*/%^]/.test(t)).sort((t,s)=>s.length-t.length)[0]??null}function f(){return a({name:"calculate",effect:"readonly",description:"Works out an arithmetic expression exactly. Use it for any sum that matters \u2014 totals, percentages, differences, rates. Supports + - * / % ^, brackets, and sqrt, abs, round, floor, ceil, ln, log.",args:[{name:"expression",type:"string",required:!0,maxLength:500,description:"For example (1250 * 0.2) + 99, or sqrt(144)."}],run:({expression:r})=>{try{return n(o(r))}catch(e){const t=u(r);if(t===null)throw new Error("Send arithmetic only, such as 812 * 0.2611. Words, units and currency signs are not understood.");if(t===r.trim())throw e;return n(o(t))}}})}export{f as calculate,u as sumIn};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{calculate as
|
|
1
|
+
import{calculate as r,sumIn as t}from"./calculate.js";import{exact as m}from"./exact.js";import{evaluate as c}from"./parse.js";export{r as calculate,c as evaluate,m as exact,t as sumIn};
|
|
@@ -19,6 +19,20 @@ export interface CodeExecOptions {
|
|
|
19
19
|
timeoutMs?: number;
|
|
20
20
|
maxChars?: number;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A tool that runs a short program the model wrote, in a sandbox you supply.
|
|
24
|
+
*
|
|
25
|
+
* Cheaper than calling tools one at a time when a task needs several: the model
|
|
26
|
+
* writes the orchestration once, and only what the program prints comes back, so
|
|
27
|
+
* intermediate results never reach the context at all.
|
|
28
|
+
*
|
|
29
|
+
* **Whatever the sandbox reaches, the model reaches, and no hook sees it.** A
|
|
30
|
+
* `beforeTool` guard runs on the call to `code_exec` and on nothing the program
|
|
31
|
+
* does inside it — a tool exposed to the sandbox is callable without the effect
|
|
32
|
+
* check, the argument rail or the approval that guards it on the bus. The
|
|
33
|
+
* sandbox is the boundary, and it is yours: what goes in it is the whole of the
|
|
34
|
+
* decision about what a model-written program may do.
|
|
35
|
+
*/
|
|
22
36
|
export declare function codeExec(opts: CodeExecOptions): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
23
37
|
readonly name: "code";
|
|
24
38
|
readonly type: "text";
|
|
@@ -18,7 +18,7 @@ export declare function dateMath(): import("../../index.js").Tool<import("../../
|
|
|
18
18
|
readonly name: "unit";
|
|
19
19
|
readonly type: "enum";
|
|
20
20
|
readonly required: true;
|
|
21
|
-
readonly options: readonly ["seconds", "minutes", "hours", "days", "weeks", "months", "years"];
|
|
21
|
+
readonly options: readonly ["seconds", "minutes", "hours", "days", "weeks", "months", "years", "weekdays"];
|
|
22
22
|
}, {
|
|
23
23
|
readonly name: "amount";
|
|
24
24
|
readonly type: "number";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as i}from"../../tool/index.js";import{between as
|
|
1
|
+
import{tool as i}from"../../tool/index.js";import{between as u,parseDate as d,shift as f}from"./shift.js";const y=["seconds","minutes","hours","days","weeks","months","years","weekdays"],c=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function h(){return i({name:"date_math",effect:"readonly",description:"Works out a date exactly. `add` and `subtract` move a date by an amount of a unit; `difference` says how far apart two dates are in whole units. `weekdays` is Monday to Friday, for anything written in working days; it knows nothing about holidays. Use it for deadlines, notice periods, ages and 'how long ago' \u2014 never work a date out yourself.",args:[{name:"operation",type:"enum",required:!0,options:["add","subtract","difference"]},{name:"date",type:"string",required:!0,description:"ISO, as in 2026-03-31."},{name:"unit",type:"enum",required:!0,options:[...y]},{name:"amount",type:"number",required:!1,description:"For add and subtract."},{name:"to",type:"string",required:!1,description:"The other date, for difference."}],run:({operation:e,date:s,unit:t,amount:r,to:n})=>{const o=d(s,"date");if(e==="difference"){if(!n)throw new Error("difference needs a second date in `to`.");return{unit:t,difference:u(o,d(n,"to"),t)}}if(r===void 0)throw new Error(`${e} needs an \`amount\`.`);const a=f(o,e==="subtract"?-r:r,t);return{iso:a.toISOString(),date:a.toISOString().slice(0,10),weekday:c[a.getUTCDay()]}}})}export{h as dateMath};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dateMath as r}from"./date.js";import{between as
|
|
1
|
+
import{dateMath as r}from"./date.js";import{between as a,parseDate as f,shift as p}from"./shift.js";import{dates as s}from"./words.js";export{a as between,r as dateMath,s as dates,f as parseDate,p as shift};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type Unit = "seconds" | "minutes" | "hours" | "days" | "weeks" | "months" | "years";
|
|
1
|
+
export type Unit = "seconds" | "minutes" | "hours" | "days" | "weeks" | "months" | "years" | "weekdays";
|
|
2
2
|
/** Parses an ISO instant, or says which argument was not one. */
|
|
3
3
|
export declare function parseDate(value: string, label: string): Date;
|
|
4
4
|
/** Moves an instant by a whole number of units. Negative goes backwards. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const T={seconds:1e3,minutes:6e4,hours:36e5,days:864e5,weeks:6048e5};function D(e,t){const n=new Date(e);if(!Number.isFinite(n.getTime()))throw new Error(`${t} "${e}" is not a date. Use an ISO instant, as in 2026-03-31.`);return n}function w(e,t){const n=e.getUTCDate(),s=new Date(e.getTime());s.setUTCDate(1),s.setUTCMonth(s.getUTCMonth()+t);const a=new Date(Date.UTC(s.getUTCFullYear(),s.getUTCMonth()+1,0)).getUTCDate();return s.setUTCDate(Math.min(n,a)),s}const i=864e5,g=e=>e.getUTCDay()===0||e.getUTCDay()===6;function h(e,t){const n=t<0?-i:i;let s=new Date(e.getTime()),a=Math.abs(t);for(;a>0;)s=new Date(s.getTime()+n),g(s)||(a-=1);return s}function l(e,t,n){if(!Number.isInteger(t))throw new Error("amount must be a whole number.");if(n==="months")return w(e,t);if(n==="years")return w(e,t*12);if(n==="weekdays")return h(e,t);const s=T[n];if(!s)throw new Error(`"${n}" is not a unit this tool knows.`);return new Date(e.getTime()+t*s)}function U(e,t,n){if(n==="months"||n==="years"){const a=(t.getUTCFullYear()-e.getUTCFullYear())*12+(t.getUTCMonth()-e.getUTCMonth())-(t.getUTCDate()<e.getUTCDate()?1:0);return n==="years"?Math.trunc(a/12):a}if(n==="weekdays")return c(e,t);const s=T[n];if(!s)throw new Error(`"${n}" is not a unit this tool knows.`);return Math.trunc((t.getTime()-e.getTime())/s)}function c(e,t){const n=t.getTime()<e.getTime(),[s,a]=n?[t,e]:[e,t];let o=0,r=new Date(s.getTime());for(;r.getTime()+i<=a.getTime();)r=new Date(r.getTime()+i),g(r)||(o+=1);return n?-o:o}export{U as between,D as parseDate,l as shift};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `date_math` asked in words, for a caller that can only send a question.
|
|
3
|
+
*
|
|
4
|
+
* Three forms are read — a span between two dates, and a date moved forward
|
|
5
|
+
* or back — with dates as ISO. Anything else is refused with the forms named,
|
|
6
|
+
* so a miss is recorded rather than a date guessed at.
|
|
7
|
+
*/
|
|
8
|
+
export declare function dates(): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
9
|
+
readonly name: "question";
|
|
10
|
+
readonly type: "string";
|
|
11
|
+
readonly required: true;
|
|
12
|
+
readonly maxLength: 2000;
|
|
13
|
+
}]>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{tool as f}from"../../tool/index.js";import{dateMath as h}from"./date.js";const a=String.raw`(\d{4}-\d{2}-\d{2}(?:T[\d:.]+Z?)?)`,m=String.raw`(-?\d+(?:\.\d+)?)`,r=String.raw`(seconds?|minutes?|hours?|days?|weeks?|months?|years?)`,w=new RegExp(`${r}\\s+(?:from|between)\\s+${a}\\s+(?:to|and)\\s+${a}`,"i"),p=new RegExp(`${m}\\s+${r}\\s+after\\s+${a}`,"i"),E=new RegExp(`${m}\\s+${r}\\s+before\\s+${a}`,"i"),u="'days from 2026-03-01 to 2026-09-09', '30 days after 2026-03-01', or '2 months before 2026-03-01'",i=t=>(t.endsWith("s")?t:`${t}s`).toLowerCase();function $(t){const o=t.match(w);if(o){const[,e,n,s]=o;return{operation:"difference",unit:i(e),date:n,to:s}}const d=t.match(p);if(d){const[,e,n,s]=d;return{operation:"add",amount:Number(e),unit:i(n),date:s}}const c=t.match(E);if(c){const[,e,n,s]=c;return{operation:"subtract",amount:Number(e),unit:i(n),date:s}}throw new Error(`Ask date_math as one of ${u}, with dates as YYYY-MM-DD.`)}function b(){const t=h();return f({name:"date_math",description:`Date arithmetic, exact: how far apart two dates are, or a date moved by an amount. Ask as ${u}. Units: seconds, minutes, hours, days, weeks, months, years.`,effect:"readonly",args:[{name:"question",type:"string",required:!0,maxLength:2e3}],run:({question:o})=>t.run($(o))})}export{b as dates};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ToolResult } from "../../types/index.js";
|
|
2
|
+
/** Why an answer was not given. */
|
|
3
|
+
export type Unanswerable = "unknowable" | "not_covered" | "out_of_scope" | "unsafe";
|
|
4
|
+
/** What `decline` returns, so a loop can tell it from an answer. */
|
|
5
|
+
export interface Declined {
|
|
6
|
+
declined: true;
|
|
7
|
+
because: Unanswerable;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* True when this result is the model saying it will not answer.
|
|
12
|
+
*
|
|
13
|
+
* The counterpart to `isFinished`. Both end a run and they are opposite
|
|
14
|
+
* outcomes, so a caller that reads only "the loop finished" cannot tell a
|
|
15
|
+
* question answered from a question refused — and a refusal recorded as an
|
|
16
|
+
* answer is a support queue that looks healthy and a customer who got nothing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isDeclined(result: ToolResult): boolean;
|
|
19
|
+
/** Reads the refusal out of a result, or null if it is not one. */
|
|
20
|
+
export declare function declinedFor(result: ToolResult): Declined | null;
|
|
21
|
+
/**
|
|
22
|
+
* Say the question cannot be answered, rather than answering it anyway.
|
|
23
|
+
*
|
|
24
|
+
* The third thing an agent can do, beside answering and asking. Asking only
|
|
25
|
+
* helps where the missing piece is the person's to supply: "which subscription"
|
|
26
|
+
* has an answer they know, and "what will the base rate be next March" has no
|
|
27
|
+
* answer anybody knows. A model with only `finish` and a clarifying question
|
|
28
|
+
* handles the second by guessing or by asking something that cannot help.
|
|
29
|
+
*
|
|
30
|
+
* `because` is a small fixed set rather than free text, so the rate of each is
|
|
31
|
+
* countable. `unknowable` and `not_covered` want opposite fixes — one is the
|
|
32
|
+
* world, the other is your records — and a single "declined" number cannot tell
|
|
33
|
+
* you which you have.
|
|
34
|
+
*
|
|
35
|
+
* Pinned for the reason `finish` is: it is how a run ends, not a tool competing
|
|
36
|
+
* to be relevant, and a turn whose shortlist dropped it has no way to say no.
|
|
37
|
+
*/
|
|
38
|
+
export declare function decline(): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
39
|
+
readonly name: "because";
|
|
40
|
+
readonly type: "enum";
|
|
41
|
+
readonly required: true;
|
|
42
|
+
readonly options: Unanswerable[];
|
|
43
|
+
}, {
|
|
44
|
+
readonly name: "reason";
|
|
45
|
+
readonly type: "text";
|
|
46
|
+
readonly required: true;
|
|
47
|
+
readonly maxLength: 2000;
|
|
48
|
+
readonly description: "One sentence the person who asked can read.";
|
|
49
|
+
}]>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{tool as t}from"../../tool/index.js";const o=["unknowable","not_covered","out_of_scope","unsafe"];function r(e){if(e.name!=="decline"||e.status!=="completed")return!1;try{return JSON.parse(e.output).declined===!0}catch{return!1}}function u(e){return r(e)?JSON.parse(e.output):null}function i(){return t({name:"decline",effect:"readonly",pinned:!0,description:"Call this when the question cannot be answered: nobody knows (unknowable), your records do not cover it (not_covered), it is not what you are for (out_of_scope), or answering would be unsafe. Use it instead of guessing. If the person could supply what is missing, ask them instead.",args:[{name:"because",type:"enum",required:!0,options:o},{name:"reason",type:"text",required:!0,maxLength:2e3,description:"One sentence the person who asked can read."}],run:({because:e,reason:n})=>({declined:!0,because:e,reason:n})})}export{i as decline,u as declinedFor,r as isDeclined};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type Declined, decline, declinedFor, isDeclined, type Unanswerable } from "./decline.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{decline as i,declinedFor as c,isDeclined as l}from"./decline.js";export{i as decline,c as declinedFor,l as isDeclined};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolResult } from "../../types/index.js";
|
|
1
2
|
export interface EscalateOptions {
|
|
2
3
|
/**
|
|
3
4
|
* What actually happens — page someone, open a ticket, write to a queue.
|
|
@@ -19,6 +20,24 @@ export interface EscalationRequest {
|
|
|
19
20
|
summary?: string;
|
|
20
21
|
urgency: "low" | "normal" | "high";
|
|
21
22
|
}
|
|
23
|
+
/** What `escalate` returns, so a run can be read back for whether a person was told. */
|
|
24
|
+
export interface Escalated {
|
|
25
|
+
escalated: true;
|
|
26
|
+
/** False when nothing was wired to receive it, or the handler was not reached. */
|
|
27
|
+
delivered: boolean;
|
|
28
|
+
request: EscalationRequest;
|
|
29
|
+
handled?: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* True when this result is the model handing the work to a person.
|
|
33
|
+
*
|
|
34
|
+
* The counterpart to `isFinished`. A run that called `escalate` and a run that
|
|
35
|
+
* escalated are not the same thing — the call can fail, and telling a customer
|
|
36
|
+
* they have been passed on when nobody was told is the worse half of the two.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isEscalated(result: ToolResult): boolean;
|
|
39
|
+
/** Reads the request out of an escalated result, or null if it is not one. */
|
|
40
|
+
export declare function escalationOf(result: ToolResult): Escalated | null;
|
|
22
41
|
/**
|
|
23
42
|
* Hand back to a person rather than guess.
|
|
24
43
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as
|
|
1
|
+
import{tool as u}from"../../tool/index.js";function s(e){if(e.name!=="escalate"||e.status!=="completed")return!1;try{return JSON.parse(e.output).escalated===!0}catch{return!1}}function l(e){return s(e)?JSON.parse(e.output):null}function c(e={}){return u({name:"escalate",description:"Hand this over to a person. Call it when you are missing something you cannot get, when the request needs an authority you do not have, or when acting on a guess would be worse than waiting. Say plainly why.",pinned:e.pinned??!0,args:[{name:"reason",type:"text",required:!0,maxLength:2e3},{name:"summary",type:"text",required:!1,maxLength:4e3},{name:"urgency",type:"enum",required:!1,options:["low","normal","high"]}],run:async({reason:t,summary:a,urgency:r="normal"})=>{const n={reason:t,summary:a,urgency:r},o=e.onEscalate?await e.onEscalate(n):void 0;return{escalated:!0,delivered:!!e.onEscalate,request:n,handled:o}}})}export{c as escalate,l as escalationOf,s as isEscalated};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { type EscalateOptions, type EscalationRequest, escalate } from "./escalate.js";
|
|
1
|
+
export { type Escalated, type EscalateOptions, type EscalationRequest, escalate, escalationOf, isEscalated, } from "./escalate.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{escalate as
|
|
1
|
+
import{escalate as s,escalationOf as t,isEscalated as c}from"./escalate.js";export{s as escalate,t as escalationOf,c as isEscalated};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ExtractOptions {
|
|
2
|
+
/** Hosts a file may be read from. Default any public https host. */
|
|
3
|
+
allow?: string[];
|
|
4
|
+
/** Longest text handed back. Default 20000 characters. */
|
|
5
|
+
maxChars?: number;
|
|
6
|
+
/** Largest file read. Default 20 MB. */
|
|
7
|
+
maxBytes?: number;
|
|
8
|
+
/** Give up after this. Default 20000. */
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
/** Injected for tests. */
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The text of a file at a URL — a PDF, a Word document, a page, or plain
|
|
15
|
+
* text — so a model can read what a link points at instead of guessing.
|
|
16
|
+
*
|
|
17
|
+
* The same guard as `http_request`: https only, no private addresses, and an
|
|
18
|
+
* allowlist you choose. Nothing is stored.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extract(opts?: ExtractOptions): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
21
|
+
readonly name: "url";
|
|
22
|
+
readonly type: "string";
|
|
23
|
+
readonly required: true;
|
|
24
|
+
readonly maxLength: 2000;
|
|
25
|
+
}]>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{tool as u}from"../../tool/index.js";import{ANY_HOST as w,checkUrl as y}from"../http/allow.js";import{followChecked as g}from"../http/hops.js";import{KINDS as x,kindOf as b,nameOf as l}from"./kinds.js";import{read as E}from"./readers.js";const T=2e4,_=20*1024*1024,p=2e4;function M(t={}){const o=t.allow?.length?t.allow:[w],m=t.fetch??globalThis.fetch.bind(globalThis),i=t.maxChars??T,s=t.maxBytes??_;return u({name:"extract",effect:"readonly",trust:"external",description:"Reads the file at a URL and returns its text: a PDF, a Word document (.docx), a web page, Markdown or plain text. Use it when a finding or a person hands you a link and you need what is actually on it, not a summary of it.",args:[{name:"url",type:"string",required:!0,maxLength:2e3}],run:async({url:d})=>{const e=y(d,o),h=AbortSignal.timeout(t.timeoutMs??p),r=await g(f=>m(f,{signal:h,redirect:"manual"}),e,o);if(!r.ok)throw new Error(`${e.hostname} answered ${r.status}.`);const c=b(r.headers.get("content-type"),e);if(!c)throw new Error(`Cannot read ${l(e)}: not one of ${x.join(", ")}, by type or by name.`);const n=await r.arrayBuffer();if(n.byteLength>s)throw new Error(`${l(e)} is ${n.byteLength} bytes, over the ${s} read.`);const a=await E(c,n);return a.length>i?`${a.slice(0,i).trimEnd()}\u2026`:a}})}export{M as extract};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{extract as f}from"./extract.js";import{KINDS as e,kindOf as t,nameOf as x}from"./kinds.js";import{fromDocx as d,fromHtml as p,fromPdf as c,read as n}from"./readers.js";export{e as KINDS,f as extract,d as fromDocx,p as fromHtml,c as fromPdf,t as kindOf,x as nameOf,n as read};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const KINDS: readonly ["pdf", "docx", "html", "md", "txt"];
|
|
2
|
+
export type Kind = (typeof KINDS)[number];
|
|
3
|
+
/** What a file is: its declared type first, the URL's extension when the type says nothing. */
|
|
4
|
+
export declare function kindOf(contentType: string | null, url: URL): Kind | null;
|
|
5
|
+
/** The name the file goes by, from the last path segment, or the host when there is none. */
|
|
6
|
+
export declare function nameOf(url: URL): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=["pdf","docx","html","md","txt"],p=[["application/pdf","pdf"],["application/vnd.openxmlformats-officedocument.wordprocessingml.document","docx"],["text/html","html"],["application/xhtml+xml","html"],["text/markdown","md"],["text/x-markdown","md"],["text/plain","txt"]],l={pdf:"pdf",docx:"docx",html:"html",htm:"html",md:"md",markdown:"md",txt:"txt",text:"txt"};function c(t,o){const e=(t??"").split(";")[0]?.trim().toLowerCase()??"",n=p.find(([d])=>d===e)?.[1];if(n)return n;const m=o.pathname.split(".").pop()?.toLowerCase()??"";return l[m]??null}function x(t){const o=t.pathname.split("/").filter(Boolean).pop();return o?decodeURIComponent(o):t.hostname}export{a as KINDS,c as kindOf,x as nameOf};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Kind } from "./kinds.js";
|
|
2
|
+
/** The words in a page: scripts and styles gone, block ends become breaks, tags dropped. */
|
|
3
|
+
export declare function fromHtml(html: string): string;
|
|
4
|
+
/** The words in a Word document: each paragraph in `word/document.xml`, in order. */
|
|
5
|
+
export declare function fromDocx(bytes: ArrayBuffer): Promise<string>;
|
|
6
|
+
export declare function fromPdf(bytes: ArrayBuffer): Promise<string>;
|
|
7
|
+
export declare function read(kind: Kind, bytes: ArrayBuffer): Promise<string>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import p from"jszip";import{extractText as m}from"unpdf";const u={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" "};function o(t){return t.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/gi,(r,e)=>e.startsWith("#x")?String.fromCodePoint(Number.parseInt(e.slice(2),16)):e.startsWith("#")?String.fromCodePoint(Number(e.slice(1))):u[e.toLowerCase()]??r)}function n(t){return t.replace(/[ \t]+\n/g,`
|
|
2
|
+
`).replace(/\n{3,}/g,`
|
|
3
|
+
|
|
4
|
+
`).trim()}function l(t){const r=t.replace(/<(script|style)\b[\s\S]*?<\/\1>/gi,"").replace(/<br\s*\/?>/gi,`
|
|
5
|
+
`).replace(/<\/(p|div|h[1-6]|li|tr|blockquote|pre|section|article|header|footer)>/gi,`
|
|
6
|
+
|
|
7
|
+
`).replace(/<[^>]+>/g,"");return n(o(r))}async function d(t){const e=(await p.loadAsync(t)).file("word/document.xml");if(!e)throw new Error("This is not a Word document: it has no word/document.xml.");const c=[...(await e.async("string")).matchAll(/<w:p\b[\s\S]*?<\/w:p>/g)].map(i=>[...i[0].matchAll(/<w:t\b[^>]*>([\s\S]*?)<\/w:t>/g)].map(s=>s[1]??"").join(""));return n(c.map(o).join(`
|
|
8
|
+
|
|
9
|
+
`))}async function f(t){const{text:r}=await m(new Uint8Array(t),{mergePages:!0});return n(r)}const a=new TextDecoder;async function h(t,r){switch(t){case"pdf":return f(r);case"docx":return d(r);case"html":return l(a.decode(r));default:return n(a.decode(r))}}export{d as fromDocx,l as fromHtml,f as fromPdf,h as read};
|
|
@@ -18,6 +18,10 @@ export declare function answerOf(result: ToolResult): string | null;
|
|
|
18
18
|
*
|
|
19
19
|
* Without it a loop ends by the model going quiet, which is indistinguishable
|
|
20
20
|
* from it losing the thread.
|
|
21
|
+
*
|
|
22
|
+
* Pinned, so a shortlist cannot drop it. It is not a tool competing to be
|
|
23
|
+
* relevant to the question — it is how the loop is told to stop, and a turn
|
|
24
|
+
* offered four tools that are not this one has no way to say it is done.
|
|
21
25
|
*/
|
|
22
26
|
export declare function finish(): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
23
27
|
readonly name: "answer";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as n}from"../../tool/index.js";function t(e){if(e.name!=="finish"||e.status!=="completed")return!1;try{return JSON.parse(e.output).done===!0}catch{return!1}}function o(e){return t(e)?JSON.parse(e.output).answer:null}function a(){return n({name:"finish",effect:"readonly",description:"Call this when you have the answer and need no more tools. Pass the final answer as it should be given to the person who asked.",args:[{name:"answer",type:"text",required:!0,maxLength:2e4}],run:({answer:e})=>({done:!0,answer:e})})}export{o as answerOf,a as finish,t as isFinished};
|
|
1
|
+
import{tool as n}from"../../tool/index.js";function t(e){if(e.name!=="finish"||e.status!=="completed")return!1;try{return JSON.parse(e.output).done===!0}catch{return!1}}function o(e){return t(e)?JSON.parse(e.output).answer:null}function a(){return n({name:"finish",effect:"readonly",pinned:!0,description:"Call this when you have the answer and need no more tools. Pass the final answer as it should be given to the person who asked.",args:[{name:"answer",type:"text",required:!0,maxLength:2e4}],run:({answer:e})=>({done:!0,answer:e})})}export{o as answerOf,a as finish,t as isFinished};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const n=/^(10\.|127\.|0\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/,s=new Set(["localhost","metadata.google.internal"]);function i(
|
|
1
|
+
const n=/^(10\.|127\.|0\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/,s=new Set(["localhost","metadata.google.internal"]);function i(e){const t=e.toLowerCase();return s.has(t)||t.endsWith(".local")||t.endsWith(".internal")||t.endsWith(".localhost")||t.startsWith("[")?!0:n.test(t)}const a="*";function h(e,t){if(t===a)return!0;const r=e.toLowerCase(),o=t.toLowerCase();return o.startsWith(".")?r===o.slice(1)||r.endsWith(o):r===o}function c(e,t){let r;try{r=new URL(e)}catch{throw new Error(`"${e}" is not a URL.`)}if(r.protocol!=="https:")throw new Error(`Only https is allowed, and this is ${r.protocol.replace(":","")}.`);if(i(r.hostname))throw new Error(`${r.hostname} is a private or link-local address.`);if(!t.some(o=>h(r.hostname,o)))throw new Error(`${r.hostname} is not in the allowlist: ${t.join(", ")}.`);return r}export{a as ANY_HOST,c as checkUrl};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The response, with every hop checked against the allowlist rather than only
|
|
3
|
+
* the URL the model named.
|
|
4
|
+
*
|
|
5
|
+
* Followed automatically, an allowlisted host answering `302 Location: …` sends
|
|
6
|
+
* the request anywhere it likes — a private address, a metadata endpoint — and
|
|
7
|
+
* carries any configured header with it. `send` must therefore pass
|
|
8
|
+
* `redirect: "manual"`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function followChecked(send: (to: URL) => Promise<Response>, target: URL, allow: string[]): Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{checkUrl as s}from"./allow.js";const a=3,c=e=>e.status>=300&&e.status<400;async function l(e,o,i){let t=await e(o);for(let r=0;r<a&&c(t);r++){const n=t.headers.get("location");if(!n)break;t=await e(s(new URL(n,t.url||o).toString(),i))}if(c(t))throw new Error(`Gave up after ${a} redirects from ${o.hostname}.`);return t}export{l as followChecked};
|
|
@@ -3,7 +3,8 @@ export interface HttpOptions {
|
|
|
3
3
|
* Hosts this tool may reach. Required, and there is no default.
|
|
4
4
|
*
|
|
5
5
|
* `"api.example.com"` matches that host; `".example.com"` matches it and any
|
|
6
|
-
* subdomain
|
|
6
|
+
* subdomain; `"*"` matches every public host, and is the choice to make only
|
|
7
|
+
* when the tool is a reader of the open web rather than a client of your APIs.
|
|
7
8
|
*/
|
|
8
9
|
allow: string[];
|
|
9
10
|
/** Sent on every request — an API key belongs here, never in the args. */
|
|
@@ -14,6 +15,8 @@ export interface HttpOptions {
|
|
|
14
15
|
maxChars?: number;
|
|
15
16
|
/** Injected for tests. */
|
|
16
17
|
fetch?: typeof fetch;
|
|
18
|
+
/** Injected for tests, for reading a `Retry-After` given as a date. */
|
|
19
|
+
now?: () => number;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* An HTTP GET or POST, to hosts you named.
|
|
@@ -24,6 +27,11 @@ export interface HttpOptions {
|
|
|
24
27
|
*
|
|
25
28
|
* Its `effect` is left unset on purpose: a GET is readonly and a POST to your
|
|
26
29
|
* allowlist may be anything at all, and one label cannot be true of both.
|
|
30
|
+
*
|
|
31
|
+
* It makes one request and never retries. Retrying is the caller's, because
|
|
32
|
+
* only the caller knows whether the call is safe to repeat — and the one thing
|
|
33
|
+
* a caller needs to retry well, `retryAfterMs`, is handed back rather than
|
|
34
|
+
* discarded with the rest of the headers.
|
|
27
35
|
*/
|
|
28
36
|
export declare function httpRequest(opts: HttpOptions): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
29
37
|
readonly name: "url";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as
|
|
1
|
+
import{tool as d}from"../../tool/index.js";import{ANY_HOST as m,checkUrl as f}from"./allow.js";import{followChecked as g}from"./hops.js";import{retryAfterMs as p}from"./retry-after.js";const y=1e4,w=1e5;function T(e){return e.includes(m)?"Any public https host is reachable.":`Only these hosts are reachable: ${e.join(", ")}.`}function q(e){if(!e.allow?.length)throw new Error("httpRequest needs an allow list of hosts; there is no safe default.");const i=e.fetch??globalThis.fetch.bind(globalThis),a=e.maxChars??w;return d({name:"http_request",description:`Fetches a URL and returns its status and body. ${T(e.allow)} Use it to read an API or a page you have been pointed at.`,args:[{name:"url",type:"string",required:!0,maxLength:2e3},{name:"method",type:"enum",required:!1,options:["GET","POST"]},{name:"body",type:"text",required:!1,maxLength:2e4}],run:async({url:l,method:n="GET",body:r})=>{const c=f(l,e.allow),h=AbortSignal.timeout(e.timeoutMs??y),t=await g(u=>i(u,{method:n,signal:h,redirect:"manual",headers:{...e.headers,...r?{"content-type":"application/json"}:{}},...r&&n==="POST"?{body:r}:{}}),c,e.allow),s=await t.text(),o=p(t.headers.get("retry-after"),e.now);return{status:t.status,ok:t.ok,body:s.slice(0,a),truncated:s.length>a,...o===null?{}:{retryAfterMs:o}}}})}export{q as httpRequest};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{checkUrl as
|
|
1
|
+
import{checkUrl as o}from"./allow.js";import{followChecked as f}from"./hops.js";import{httpRequest as m}from"./http.js";import{retryAfterMs as c}from"./retry-after.js";export{o as checkUrl,f as followChecked,m as httpRequest,c as retryAfterMs};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a server asked you to wait, in milliseconds, or null.
|
|
3
|
+
*
|
|
4
|
+
* `Retry-After` is the one part of a 429 or a 503 that is not guesswork: the
|
|
5
|
+
* server knows when its window resets and says so. Dropping it leaves a caller
|
|
6
|
+
* backing off by a schedule it invented, which is either too slow or — the
|
|
7
|
+
* expensive one — too fast, and a retry inside the window spends a request to
|
|
8
|
+
* be told to wait again.
|
|
9
|
+
*
|
|
10
|
+
* Two forms, both in the spec: whole seconds, or an HTTP date. A date in the
|
|
11
|
+
* past is zero rather than negative, because "wait -4 seconds" is not something
|
|
12
|
+
* a caller should have to guard against.
|
|
13
|
+
*/
|
|
14
|
+
export declare function retryAfterMs(header: string | null, now?: () => number): number | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function u(r,n=Date.now){if(!r)return null;const t=r.trim();if(/^\d+$/.test(t))return Number(t)*1e3;const e=Date.parse(t);return Number.isNaN(e)?null:Math.max(0,e-n())}export{u as retryAfterMs};
|
package/dist/builtin/index.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
export { calculate, evaluate } from "./calculate/index.js";
|
|
2
2
|
export { type ClockOptions, currentTime } from "./clock/index.js";
|
|
3
3
|
export { type CodeExecOptions, codeExec, type Sandbox, type SandboxRequest, type SandboxResult, } from "./code-exec/index.js";
|
|
4
|
-
export { dateMath } from "./date/index.js";
|
|
5
|
-
export { type
|
|
6
|
-
export { type EscalateOptions, type EscalationRequest, escalate } from "./escalate/index.js";
|
|
4
|
+
export { dateMath, dates } from "./date/index.js";
|
|
5
|
+
export { type Declined, decline, declinedFor, isDeclined, type Unanswerable, } from "./decline/index.js";
|
|
6
|
+
export { type Escalated, type EscalateOptions, type EscalationRequest, escalate, escalationOf, isEscalated, } from "./escalate/index.js";
|
|
7
|
+
export { type ExtractOptions, extract } from "./extract/index.js";
|
|
7
8
|
export { answerOf, type Finished, finish, isFinished } from "./finish/index.js";
|
|
8
|
-
export { checkUrl, type HttpOptions, httpRequest } from "./http/index.js";
|
|
9
|
-
export { type JsonQueryOptions, jsonQuery } from "./json/index.js";
|
|
9
|
+
export { checkUrl, type HttpOptions, httpRequest, retryAfterMs } from "./http/index.js";
|
|
10
|
+
export { type JsonQueryOptions, jsonFrom, jsonQuery } from "./json/index.js";
|
|
11
|
+
export { type NotesOptions, type NotesStore, notes, notesStore } from "./notes/index.js";
|
|
12
|
+
export { type OpenAlexApi, type OpenAlexOptions, openalex } from "./openalex/index.js";
|
|
13
|
+
export { type PaperSearchOptions, paperSearch } from "./paper-search/index.js";
|
|
14
|
+
export { type Publisher, type PublishOptions, publish } from "./publish/index.js";
|
|
10
15
|
export { think } from "./think/index.js";
|
|
11
16
|
export { type SearchDepth, type WebSearchOptions, webSearch } from "./web-search/index.js";
|
|
12
|
-
export { type
|
|
17
|
+
export { type WikidataApi, type WikidataOptions, wikidata } from "./wikidata/index.js";
|
package/dist/builtin/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{calculate as o,evaluate as t}from"./calculate/index.js";import{currentTime as
|
|
1
|
+
import{calculate as o,evaluate as t}from"./calculate/index.js";import{currentTime as a}from"./clock/index.js";import{codeExec as x}from"./code-exec/index.js";import{dateMath as s,dates as c}from"./date/index.js";import{decline as n,declinedFor as l,isDeclined as d}from"./decline/index.js";import{escalate as u,escalationOf as k,isEscalated as w}from"./escalate/index.js";import{extract as S}from"./extract/index.js";import{answerOf as j,finish as y,isFinished as E}from"./finish/index.js";import{checkUrl as O,httpRequest as q,retryAfterMs as v}from"./http/index.js";import{jsonFrom as D,jsonQuery as Q}from"./json/index.js";import{notes as T,notesStore as U}from"./notes/index.js";import{openalex as z}from"./openalex/index.js";import{paperSearch as C}from"./paper-search/index.js";import{publish as H}from"./publish/index.js";import{think as J}from"./think/index.js";import{webSearch as L}from"./web-search/index.js";import{wikidata as P}from"./wikidata/index.js";export{j as answerOf,o as calculate,O as checkUrl,x as codeExec,a as currentTime,s as dateMath,c as dates,n as decline,l as declinedFor,u as escalate,k as escalationOf,t as evaluate,S as extract,y as finish,q as httpRequest,d as isDeclined,w as isEscalated,E as isFinished,D as jsonFrom,Q as jsonQuery,T as notes,U as notesStore,z as openalex,C as paperSearch,H as publish,v as retryAfterMs,J as think,L as webSearch,P as wikidata};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsonQuery as e}from"./json.js";import{readPath as
|
|
1
|
+
import{jsonQuery as e}from"./json.js";import{jsonFrom as t}from"./parse.js";import{readPath as p}from"./path.js";export{t as jsonFrom,e as jsonQuery,p as readPath};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tool as
|
|
1
|
+
import{tool as s}from"../../tool/index.js";import{jsonFrom as i}from"./parse.js";import{readPath as l}from"./path.js";const d=2e4;function g(r={}){const e=r.maxChars??d;return s({name:"json_query",effect:"readonly",description:"Reads one value out of a JSON document by path. Paths look like `invoices[0].total`, or `invoices[*].total` for every element. Use it instead of reading a long document yourself.",args:[{name:"json",type:"text",required:!0,maxLength:2e5},{name:"path",type:"string",required:!0,maxLength:500}],run:({json:a,path:n})=>{const u=i(a),t=l(u,n);if(t===void 0)return{found:!1,path:n,value:null};const o=JSON.stringify(t)??"null";return{found:!0,path:n,value:t,truncated:o.length>e,...o.length>e?{value:`${o.slice(0,e)}\u2026`}:{}}}})}export{g as jsonQuery};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JSON inside a model's reply, whatever it wrapped it in.
|
|
3
|
+
*
|
|
4
|
+
* The two commonest structured-output failures are not bad JSON at all: a
|
|
5
|
+
* fenced block, and a sentence before or after it. Both parse perfectly once
|
|
6
|
+
* the payload is found, and every caller otherwise writes the same fence
|
|
7
|
+
* stripping by hand and gets the second case wrong.
|
|
8
|
+
*
|
|
9
|
+
* It finds the JSON; it never rewrites it. A trailing comma, a single-quoted
|
|
10
|
+
* key or a missing brace stays an error, because repairing those is guessing at
|
|
11
|
+
* what was meant and a wrong guess is a confidently wrong record. Those belong
|
|
12
|
+
* in a bounded repair round, where the model is told what failed and answers
|
|
13
|
+
* again.
|
|
14
|
+
*/
|
|
15
|
+
export declare function jsonFrom(text: string): unknown;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const l=/```(?:[a-zA-Z0-9_-]+)?\s*\n?([\s\S]*?)```/,f={"{":"}","[":"]"};function a(e){const n=e.search(/[[{]/);if(n===-1)return null;const t=f[e[n]];let o=0,i=!1,s=!1;for(let r=n;r<e.length;r+=1){const c=e[r];if(s){s=!1;continue}if(c==="\\"&&i){s=!0;continue}if(c==='"'){i=!i;continue}if(!i){if(c===e[n])o+=1;else if(c===t&&(o-=1,o===0))return e.slice(n,r+1)}}return null}function u(e){const n=e.trim();for(const t of[n,l.exec(n)?.[1]?.trim(),a(n)])if(t)try{return JSON.parse(t)}catch{}throw new Error(`No JSON in ${n.length>80?`${n.slice(0,80)}\u2026`:n||"an empty reply"}`)}export{u as jsonFrom};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ACTIONS, type NotesOptions, type NotesStore, notes, notesStore } from "./notes.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ACTIONS as t,notes as r,notesStore as n}from"./notes.js";export{t as ACTIONS,r as notes,n as notesStore};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Boards by session, one per process unless you hand every bus the same one. */
|
|
2
|
+
export type NotesStore = Map<string, string[]>;
|
|
3
|
+
export interface NotesOptions {
|
|
4
|
+
/** Whose board: a session id, so a follow-up question sees what the last run worked out. */
|
|
5
|
+
session: string;
|
|
6
|
+
/** Where boards live. Default one store for the process; pass your own to share or to inspect. */
|
|
7
|
+
store?: NotesStore;
|
|
8
|
+
/** Notes a board holds before `post` is refused. Default 50. */
|
|
9
|
+
maxNotes?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const ACTIONS: readonly ["post", "read", "replace", "clear"];
|
|
12
|
+
export declare function notesStore(): NotesStore;
|
|
13
|
+
/**
|
|
14
|
+
* A board the model writes to and reads back during a session — what it is
|
|
15
|
+
* doing, what is still missing, what it decided — kept in memory and gone when
|
|
16
|
+
* the process is.
|
|
17
|
+
*
|
|
18
|
+
* `think` is write-once and never seen again; this is the model's own chosen
|
|
19
|
+
* state, readable three steps later. It is not memory: a note worth keeping
|
|
20
|
+
* goes to `remember`, on purpose, so half-thoughts never surface as facts.
|
|
21
|
+
*
|
|
22
|
+
* `replace` is what makes it usable as a running plan. Append-only, a plan
|
|
23
|
+
* re-posted each turn becomes twenty notes where the twentieth contradicts the
|
|
24
|
+
* third and `read` shows both — and the only way back is `clear`, which throws
|
|
25
|
+
* the plan away with the noise. One note, rewritten, is the whole pattern.
|
|
26
|
+
*
|
|
27
|
+
* No effect label: `read` changes nothing and `post` changes only this board.
|
|
28
|
+
*/
|
|
29
|
+
export declare function notes(opts: NotesOptions): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
|
|
30
|
+
readonly name: "action";
|
|
31
|
+
readonly type: "enum";
|
|
32
|
+
readonly required: true;
|
|
33
|
+
readonly options: readonly ["post", "read", "replace", "clear"];
|
|
34
|
+
}, {
|
|
35
|
+
readonly name: "note";
|
|
36
|
+
readonly type: "text";
|
|
37
|
+
readonly required: false;
|
|
38
|
+
readonly maxLength: 2000;
|
|
39
|
+
}, {
|
|
40
|
+
readonly name: "number";
|
|
41
|
+
readonly type: "number";
|
|
42
|
+
readonly required: false;
|
|
43
|
+
readonly min: 1;
|
|
44
|
+
readonly description: "Which note to replace, as `read` numbers them.";
|
|
45
|
+
}]>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{tool as d}from"../../tool/index.js";const l=["post","read","replace","clear"],u=50,c=2e3,w=new Map;function p(){return new Map}function m(e){return e.map((t,n)=>`#${n+1} ${t}`).join(`
|
|
2
|
+
`)}function f(e,t,n){if(t===void 0)throw new Error("replace needs the `number` of the note.");if(e.length===0)throw new Error("There is nothing on the board to replace.");if(t<1||t>e.length)throw new Error(`There is no note #${t}; the board holds #1 to #${e.length}.`);const o=[...e];return o[t-1]=n,o}function y(e){if(!e.session?.trim())throw new Error("notes needs the session whose board this is.");const t=e.store??w,n=e.maxNotes??u,o=()=>t.get(e.session)??[];return d({name:"notes",description:"Your notes for this session. `post` writes one \u2014 what you are doing, what is still missing, what you decided. `read` shows them all, numbered, in order. `replace` rewrites one by its number \u2014 keep a plan as a single note and rewrite it as you go, rather than posting its status again each turn. `clear` wipes them. They last the session and nobody else reads them; use `remember` for anything that should outlast it.",args:[{name:"action",type:"enum",required:!0,options:[...l]},{name:"note",type:"text",required:!1,maxLength:c},{name:"number",type:"number",required:!1,min:1,description:"Which note to replace, as `read` numbers them."}],run:({action:s,note:h,number:a})=>{const r=o();if(s==="read")return r.length?m(r):"Nothing on the board yet.";if(s==="clear")return t.delete(e.session),`Cleared ${r.length} note${r.length===1?"":"s"}.`;const i=h?.trim();if(!i)throw new Error(`${s} needs a \`note\`.`);if(s==="replace")return t.set(e.session,f(r,a,i)),`#${a} rewritten.`;if(r.length>=n)throw new Error(`The board holds ${n} notes; clear it, or say less.`);return t.set(e.session,[...r,i]),`#${r.length+1} noted.`}})}export{l as ACTIONS,y as notes,p as notesStore};
|