@spendgraph/tools 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/builtin/index.d.ts +1 -0
  2. package/dist/builtin/index.js +1 -1
  3. package/dist/builtin/openalex/api.d.ts +8 -4
  4. package/dist/builtin/openalex/api.js +1 -1
  5. package/dist/builtin/openalex/index.d.ts +0 -1
  6. package/dist/builtin/openalex/index.js +1 -1
  7. package/dist/builtin/openalex/openalex.d.ts +30 -17
  8. package/dist/builtin/openalex/openalex.js +1 -1
  9. package/dist/builtin/paper-search/index.d.ts +1 -0
  10. package/dist/builtin/paper-search/index.js +1 -0
  11. package/dist/builtin/paper-search/paper-search.d.ts +38 -0
  12. package/dist/builtin/paper-search/paper-search.js +3 -0
  13. package/dist/builtin/polite.d.ts +1 -0
  14. package/dist/builtin/polite.js +1 -1
  15. package/dist/builtin/publish/publish.d.ts +1 -1
  16. package/dist/builtin/wikidata/wikidata.d.ts +3 -1
  17. package/dist/builtin/wikidata/wikidata.js +1 -1
  18. package/dist/bus/output.d.ts +0 -17
  19. package/dist/bus/output.js +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +1 -1
  22. package/dist/papers/consensus/consensus.d.ts +12 -0
  23. package/dist/papers/consensus/consensus.js +1 -0
  24. package/dist/papers/consensus/index.d.ts +4 -0
  25. package/dist/papers/consensus/index.js +1 -0
  26. package/dist/papers/consensus/reply.d.ts +24 -0
  27. package/dist/papers/consensus/reply.js +1 -0
  28. package/dist/papers/every.d.ts +6 -0
  29. package/dist/papers/every.js +1 -0
  30. package/dist/papers/index.d.ts +11 -0
  31. package/dist/papers/index.js +1 -0
  32. package/dist/papers/openalex/index.d.ts +2 -0
  33. package/dist/papers/openalex/index.js +1 -0
  34. package/dist/papers/openalex/openalex.d.ts +8 -0
  35. package/dist/papers/openalex/openalex.js +1 -0
  36. package/dist/papers/papers.d.ts +54 -0
  37. package/dist/papers/papers.js +1 -0
  38. package/dist/{builtin/openalex → papers}/render.d.ts +1 -1
  39. package/dist/papers/render.js +4 -0
  40. package/dist/papers/semantic-scholar/index.d.ts +4 -0
  41. package/dist/papers/semantic-scholar/index.js +1 -0
  42. package/dist/papers/semantic-scholar/reply.d.ts +29 -0
  43. package/dist/papers/semantic-scholar/reply.js +1 -0
  44. package/dist/papers/semantic-scholar/semantic-scholar.d.ts +8 -0
  45. package/dist/papers/semantic-scholar/semantic-scholar.js +1 -0
  46. package/dist/search/every.d.ts +6 -0
  47. package/dist/search/every.js +4 -0
  48. package/dist/search/index.d.ts +4 -2
  49. package/dist/search/index.js +1 -1
  50. package/dist/search/search.d.ts +6 -0
  51. package/dist/search/searxng/searxng.js +3 -3
  52. package/dist/search/sonar/sonar.js +1 -1
  53. package/dist/search/tavily/tavily.js +1 -1
  54. package/dist/tool/tool.d.ts +4 -0
  55. package/dist/tool/tool.js +1 -1
  56. package/dist/types/tool.d.ts +13 -0
  57. package/docs/bogus.mdx +8 -8
  58. package/docs/builtins.mdx +76 -15
  59. package/docs/bus.mdx +7 -7
  60. package/docs/declaring.mdx +6 -6
  61. package/docs/effects.mdx +6 -6
  62. package/docs/overview.mdx +14 -12
  63. package/docs/selecting.mdx +10 -10
  64. package/docs/stored.mdx +8 -8
  65. package/docs/turns.mdx +5 -5
  66. package/package.json +10 -4
  67. package/dist/agents/index.d.ts +0 -1
  68. package/dist/agents/index.js +0 -1
  69. package/dist/agents/moa/design.d.ts +0 -49
  70. package/dist/agents/moa/design.js +0 -5
  71. package/dist/agents/moa/http.d.ts +0 -34
  72. package/dist/agents/moa/http.js +0 -1
  73. package/dist/agents/moa/index.d.ts +0 -74
  74. package/dist/agents/moa/index.js +0 -1
  75. package/dist/agents/moa/moa.d.ts +0 -56
  76. package/dist/agents/moa/moa.js +0 -13
  77. package/dist/agents/moa/types.d.ts +0 -33
  78. package/dist/agents/moa/types.js +0 -0
  79. package/dist/builtin/openalex/render.js +0 -4
package/docs/stored.mdx CHANGED
@@ -1,5 +1,5 @@
1
1
  export const meta = {
2
- title: "A stored tool spendgraph docs",
2
+ title: "A stored tool: spendgraph docs",
3
3
  description:
4
4
  "The dashboard owns the description, your code owns the arguments and the handler, and bindTool checks the two against each other before the model ever sees it.",
5
5
  };
@@ -19,7 +19,7 @@ const lookup = await bindTool(sg, {
19
19
  });
20
20
  ```
21
21
 
22
- No `description` in that call. That is the point one source for the string the model reads, and it is the one you can edit without a release.
22
+ No `description` in that call. That is the point, one source for the string the model reads, and it is the one you can edit without a release.
23
23
 
24
24
  ## Who owns what
25
25
 
@@ -31,13 +31,13 @@ No `description` in that call. That is the point — one source for the string t
31
31
  pinned a deploy
32
32
  ```
33
33
 
34
- Arguments stay in code because the handler's parameter types are inferred from them. Pull those from the server and every `run` is handed a `Record<string, unknown>`, which gives up the one guarantee [declaring a tool](/docs/tools/declaring) exists to provide.
34
+ Arguments stay in code because the handler's parameter types are inferred from them. Pull those from the server and every `run` is handed a `Record<string, unknown>`, which gives up the one guarantee [declaring a tool](/spendgraph/tools/declaring) exists to provide.
35
35
 
36
36
  Write `as const` here exactly as you would for `tool()`. Without it inference falls back to nothing, and the drift check below becomes the only thing still catching a rename.
37
37
 
38
38
  ## Drift fails at bind, not mid-loop
39
39
 
40
- The stored arguments and the declared ones are compared on the way through, by **name, type and requiredness** the three a handler is written against.
40
+ The stored arguments and the declared ones are compared on the way through, by **name, type and requiredness**: the three a handler is written against.
41
41
 
42
42
  ```ts
43
43
  import { ToolDriftError, ToolNotStoredError } from "@spendgraph/tools";
@@ -58,21 +58,21 @@ try {
58
58
  "mode" is stored but not declared here
59
59
  ```
60
60
 
61
- A description or a bound that differs is **not** drift that is the dashboard being edited, which is the whole feature. A renamed argument is a handler about to be passed `undefined` halfway through an agent loop, so it throws.
61
+ A description or a bound that differs is **not** drift: that is the dashboard being edited, which is the whole feature. A renamed argument is a handler about to be passed `undefined` halfway through an agent loop, so it throws.
62
62
 
63
63
  <Callout tone="warn" title="Bind at startup, not per request">
64
- Every `bindTool` is a round trip to the API. Bind once when the process starts and hold the result, the same as you would a database connection a bind inside a request handler puts a network hop in front of every model call.
64
+ Every `bindTool` is a round trip to the API. Bind once when the process starts and hold the result, the same as you would a database connection, a bind inside a request handler puts a network hop in front of every model call.
65
65
  </Callout>
66
66
 
67
67
  <Callout tone="trap" title="A rename in the dashboard is a handler passed `undefined`">
68
- The wording is editable without a deploy; the argument names are not. `bindTool` compares them at bind time and refuses a deploy that fails is recoverable, and the same rename noticed mid-loop is a handler halfway through somebody's refund reading an argument that is not there.
68
+ The wording is editable without a deploy; the argument names are not. `bindTool` compares them at bind time and refuses: a deploy that fails is recoverable, and the same rename noticed mid-loop is a handler halfway through somebody's refund reading an argument that is not there.
69
69
  </Callout>
70
70
 
71
71
  ## When nothing is stored yet
72
72
 
73
73
  `ToolNotStoredError` is the ordinary case of "you have not written this one in the dashboard yet", not an exotic failure. It exists so a 404 from three layers down arrives as a sentence naming the tool.
74
74
 
75
- Create the row first `sg.tools.create` on the [client](/docs/sdk/client), or the Tools page in the dashboard then bind to it.
75
+ Create the row first, `sg.tools.create` on the [client](/spendgraph/sdk/client), or the Tools page in the dashboard, then bind to it.
76
76
 
77
77
  ## It is an ordinary tool afterwards
78
78
 
package/docs/turns.mdx CHANGED
@@ -1,5 +1,5 @@
1
1
  export const meta = {
2
- title: "A turn spendgraph docs",
2
+ title: "A turn: spendgraph docs",
3
3
  description:
4
4
  "A turn is selected once and remembers what it offered. Without that a record cannot tell 'chose not to' from 'was never offered', which are opposite bugs.",
5
5
  };
@@ -19,7 +19,7 @@ A turn is **selected once** and remembers what it offered. A trace cannot disagr
19
19
 
20
20
  ## Offered is recorded, not just called
21
21
 
22
- Which tools were offered is recorded as well as which were called. Without it a record cannot tell **"chose not to"** from **"was never offered"** which are opposite bugs, and look identical in a log that only lists calls.
22
+ Which tools were offered is recorded as well as which were called. Without it a record cannot tell **"chose not to"** from **"was never offered"**, which are opposite bugs, and look identical in a log that only lists calls.
23
23
 
24
24
  ## What a turn answers
25
25
 
@@ -34,17 +34,17 @@ Which tools were offered is recorded as well as which were called. Without it a
34
34
 
35
35
  ## Never throwing is the point
36
36
 
37
- A tool that fails is an **outcome the agent can react to** and a step worth recording, not an exception that ends the run the same argument `status` and `error` exist on a rollout for.
37
+ A tool that fails is an **outcome the agent can react to** and a step worth recording, not an exception that ends the run, the same argument `status` and `error` exist on a rollout for.
38
38
 
39
39
  A tool the bus does not have is a failure that says what was offered, since the usual cause is a hallucinated name.
40
40
 
41
41
  <Callout tone="trap" title="`undefined` means two different things">
42
- A tool that was offered and stayed quiet, and a tool this turn never offered, both answer `undefined`. `offeredTools` is what tells them apart which is the difference between a model that declined to look something up and a model that could not.
42
+ A tool that was offered and stayed quiet, and a tool this turn never offered, both answer `undefined`. `offeredTools` is what tells them apart, which is the difference between a model that declined to look something up and a model that could not.
43
43
  </Callout>
44
44
 
45
45
  ## Usage drives the tiebreaker
46
46
 
47
- `bus.usage()` counts what has actually been called, which is what selection falls back on when a query singles out nothing. A call that never reached the tool a name that does not exist, arguments that did not validate is not counted, because it was not a use.
47
+ `bus.usage()` counts what has actually been called, which is what selection falls back on when a query singles out nothing. A call that never reached the tool: a name that does not exist, arguments that did not validate, is not counted, because it was not a use.
48
48
 
49
49
  ## With a prompt
50
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spendgraph/tools",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Declare a tool once, offer the right few, and record what was called.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -37,6 +37,10 @@
37
37
  "types": "./dist/wire/index.d.ts",
38
38
  "import": "./dist/wire/index.js"
39
39
  },
40
+ "./papers": {
41
+ "types": "./dist/papers/index.d.ts",
42
+ "import": "./dist/papers/index.js"
43
+ },
40
44
  "./search": {
41
45
  "types": "./dist/search/index.d.ts",
42
46
  "import": "./dist/search/index.js"
@@ -57,13 +61,14 @@
57
61
  "README.md"
58
62
  ],
59
63
  "dependencies": {
60
- "@spendgraph/sdk": "^0.7.0",
64
+ "@spendgraph/sdk": "^0.8.1",
61
65
  "jszip": "^3.10.2",
62
66
  "unpdf": "^1.8.1"
63
67
  },
64
68
  "devDependencies": {
65
69
  "@locusgraph/client": "^0.8.1",
66
- "@spendgraph/prompt": "^0.7.0",
70
+ "@spendgraph/config": "0.8.1",
71
+ "@spendgraph/prompt": "^0.8.1",
67
72
  "pdf-lib": "^1.17.1",
68
73
  "typescript": "^5"
69
74
  },
@@ -75,7 +80,8 @@
75
80
  },
76
81
  "scripts": {
77
82
  "build": "rm -rf dist && tsc -p tsconfig.json --emitDeclarationOnly && tsc -p tsconfig.json --declaration false --removeComments && node ../../scripts/minify.mjs dist",
83
+ "examples": "pnpm run build && node examples/02-select/01-shortlist.mjs",
78
84
  "test": "vitest run",
79
- "examples": "pnpm run build && node examples/02-select/01-shortlist.mjs"
85
+ "typecheck": "tsc -p tsconfig.tests.json"
80
86
  }
81
87
  }
@@ -1 +0,0 @@
1
- export { type ArtifactFormat, DEFAULT_DESIGN, type DesignGuide, MoaAbortedError, type MoaClient, MoaTimeoutError, mergeDesign, renderDesign, type WriteDocumentOptions, writeDocument, } from "./moa/index.js";
@@ -1 +0,0 @@
1
- import{DEFAULT_DESIGN as o,MoaAbortedError as t,MoaTimeoutError as D,mergeDesign as i,renderDesign as m,writeDocument as n}from"./moa/index.js";export{o as DEFAULT_DESIGN,t as MoaAbortedError,D as MoaTimeoutError,i as mergeDesign,m as renderDesign,n as writeDocument};
@@ -1,49 +0,0 @@
1
- import type { ArtifactFormat } from "./types.js";
2
- /**
3
- * The house style a commissioned document is built to.
4
- *
5
- * Every field is optional because a caller overrides only what its brand
6
- * actually fixes; the rest falls back to {@link DEFAULT_DESIGN}.
7
- */
8
- export interface DesignGuide {
9
- /** What the style is called, so the document can say which one it followed. */
10
- name?: string;
11
- fonts?: {
12
- heading?: string;
13
- body?: string;
14
- mono?: string;
15
- };
16
- palette?: {
17
- primary?: string;
18
- accent?: string;
19
- text?: string;
20
- muted?: string;
21
- surface?: string;
22
- };
23
- /** Page size, margins, grid, density. */
24
- layout?: string;
25
- /** How a figure earns its place and what it may not do. */
26
- charts?: string;
27
- /** How the writing sounds. */
28
- tone?: string;
29
- /** Fetchable logo, dropped on the cover and nowhere else. */
30
- logoUrl?: string;
31
- /** Hard rules, appended to the defaults rather than replacing them. */
32
- rules?: readonly string[];
33
- }
34
- /**
35
- * What a document looks like when nobody has said.
36
- *
37
- * Chosen to be defensible rather than distinctive: one typeface, one accent,
38
- * real whitespace, and figures that carry a single message.
39
- */
40
- export declare const DEFAULT_DESIGN: DesignGuide;
41
- /**
42
- * A caller's guide over the defaults: named fields win, `rules` accumulate.
43
- *
44
- * Rules append because they are constraints — a brand adding "never use red"
45
- * means it as well as the defaults, not instead of them.
46
- */
47
- export declare function mergeDesign(base: DesignGuide, over?: DesignGuide): DesignGuide;
48
- /** The guide as the block Moa reads, since its artifact pipeline is prompt-driven. */
49
- export declare function renderDesign(guide: DesignGuide, format: ArtifactFormat, notes?: string): string;
@@ -1,5 +0,0 @@
1
- const c={name:"Spendgraph house style",fonts:{heading:"Inter",body:"Inter",mono:"JetBrains Mono"},palette:{primary:"#0F172A",accent:"#2563EB",text:"#111827",muted:"#64748B",surface:"#FFFFFF"},layout:"A4 portrait, 20mm margins, a single text column at 60-75 characters, a 4pt spacing scale, and one heading scale used consistently (28/20/16/12). Whitespace is the design.",charts:"One message per figure, stated in its caption. Label series directly where there are three or fewer. No 3D, no gradients, no gridline heavier than the data, no pie chart past three slices, and axes that start at zero unless the caption says why not.",tone:"Plain and specific. Lead with the finding, then the evidence. No filler adjectives, no throat-clearing, no sentence that survives being deleted.",rules:["A cover carrying the title, date and author, and no other ornament.","Every number traceable to something in the source material; invent nothing.","Tables for anything being compared; prose for anything being argued.","No stock imagery, no clip art, no placeholder text in the finished file.","Page numbers on every page after the cover."]},d={pdf:"Cover, then contents if it runs past four pages, then the body. Figures captioned and referenced from the text. Nothing straddles a page break: no orphan heading, no split table row.",pptx:"16:9. One idea per slide, and the title is that idea as a sentence \u2014 a claim, not a label. At most six lines of body per slide, one figure per slide at most, and everything you cut goes into the speaker notes. Close on decisions and next steps, not on 'thank you'.",docx:"Real Word styles for headings rather than manual bold, a table-of-contents field rather than a typed list, a header row on every table, and captions on figures.",md:"A title heading, ATX headings throughout, tables for comparisons, fenced code with a language tag, and no HTML."};function i(e,t){return e?t?{...e,...t}:e:t}function h(e,t){return t?{...e,...t,fonts:i(e.fonts,t.fonts),palette:i(e.palette,t.palette),rules:[...e.rules??[],...t.rules??[]]}:e}const a=(e,t)=>t?[`${e}: ${t}`]:[];function p(e,t,o){const{fonts:r,palette:n}=e;return[...a("Style",e.name),...a("Typefaces",[r?.heading&&`headings ${r.heading}`,r?.body&&`body ${r.body}`,r?.mono&&`code ${r.mono}`].filter(Boolean).join(", ")||void 0),...a("Palette",[n?.primary&&`primary ${n.primary}`,n?.accent&&`accent ${n.accent}`,n?.text&&`text ${n.text}`,n?.muted&&`muted ${n.muted}`,n?.surface&&`surface ${n.surface}`].filter(Boolean).join(", ")||void 0),...a("Layout",e.layout),...a("Figures",e.charts),...a("Voice",e.tone),...a("Logo",e.logoUrl),`Format craft (.${t}): ${d[t]}`,...e.rules?.length?[`Rules:
2
- ${e.rules.map(s=>`- ${s}`).join(`
3
- `)}`]:[],...o?.trim()?[`Overrides for this document, which win over everything above:
4
- ${o.trim()}`]:[]].join(`
5
- `)}export{c as DEFAULT_DESIGN,h as mergeDesign,p as renderDesign};
@@ -1,34 +0,0 @@
1
- /** Anything shaped like an `AbortSignal`, so a Worker's own signal fits too. */
2
- export interface AbortSignalLike {
3
- readonly aborted: boolean;
4
- addEventListener(type: "abort", listener: () => void, opts?: {
5
- once?: boolean;
6
- }): void;
7
- removeEventListener(type: "abort", listener: () => void): void;
8
- }
9
- /** Where Moa lives, unless a config says otherwise. */
10
- export declare const MOA_BASE_URL = "https://api.fnmoa.com";
11
- export interface MoaClientConfig {
12
- /** Read from here rather than the environment, as every other builtin does. */
13
- apiKey?: string;
14
- baseUrl?: string;
15
- /** Ceiling on one HTTP call, not on the commission. Default 60000. */
16
- timeoutMs?: number;
17
- /** Injected for tests. */
18
- fetch?: typeof fetch;
19
- }
20
- /** A non-2xx from Moa, carrying the status so a caller can tell 4xx from 5xx. */
21
- export declare class MoaApiError extends Error {
22
- readonly status: number;
23
- readonly detail?: string;
24
- constructor(message: string, status: number, detail?: string);
25
- }
26
- interface RequestOptions {
27
- method?: string;
28
- body?: unknown;
29
- signal?: AbortSignalLike;
30
- timeoutMs?: number;
31
- }
32
- /** One `request<T>(path, opts)` against Moa, with auth, timeout and error shape settled. */
33
- export declare function createRequest(config?: MoaClientConfig): <T>(path: string, opts?: RequestOptions) => Promise<T>;
34
- export {};
@@ -1 +0,0 @@
1
- const f="https://api.fnmoa.com",M=6e4,_=500;class c extends Error{status;detail;constructor(t,s,n){super(n?`${t}: ${n}`:t),this.name="MoaApiError",this.status=s,this.detail=n}}function T(e){const t=e.cause;return t?.code??t?.message??e.message}function m(e,t){const s=new AbortController,n=()=>s.abort();let r=!1;const o=setTimeout(()=>{r=!0,n()},e);return t?.addEventListener("abort",n,{once:!0}),t?.aborted&&n(),{signal:s.signal,expired:()=>r,release:()=>{clearTimeout(o),t?.removeEventListener("abort",n)}}}function $(e={}){const t=e.fetch??globalThis.fetch.bind(globalThis),s=(e.baseUrl??f).replace(/\/+$/,"");return async function(r,o={}){const u=o.timeoutMs??e.timeoutMs??6e4,i=o.method??"GET",d=`${s}${r}`,{signal:E,expired:h,release:b}=m(u,o.signal);try{const a=await t(d,{method:i,signal:E,headers:{...e.apiKey?{authorization:`Bearer ${e.apiKey}`}:{},...o.body===void 0?{}:{"content-type":"application/json"}},...o.body===void 0?{}:{body:JSON.stringify(o.body)}}).catch(l=>{throw o.signal?.aborted?l:h()?new c(`Moa did not answer ${i} ${r}`,504,`${u}ms`):new c(`Moa is unreachable at ${d}`,0,T(l))});if(!a.ok)throw new c(`Moa returned ${a.status} for ${i} ${r}`,a.status,(await a.text()).slice(0,500));return a.status===204?void 0:await a.json()}finally{b()}}}export{f as MOA_BASE_URL,c as MoaApiError,$ as createRequest};
@@ -1,74 +0,0 @@
1
- import { type DesignGuide } from "./design.js";
2
- import type { AbortSignalLike } from "./http.js";
3
- import { type MoaClient } from "./moa.js";
4
- import type { ArtifactFormat } from "./types.js";
5
- export interface WriteDocumentOptions {
6
- /** Moa key. Required, and read from here rather than the environment. */
7
- apiKey: string;
8
- /** Defaults to `https://api.fnmoa.com`. */
9
- baseUrl?: string;
10
- /**
11
- * House style laid over {@link DEFAULT_DESIGN}: named fields win, `rules`
12
- * accumulate. This is where a brand goes, so the model cannot dilute it.
13
- */
14
- design?: DesignGuide;
15
- /** Formats this deployment will produce. Default all four. */
16
- formats?: readonly ArtifactFormat[];
17
- /** Give up waiting after this. Default 600000 — rendering is minutes, not seconds. */
18
- timeoutMs?: number;
19
- /** Stops the wait. With `cancelOnAbort`, stops the task too. */
20
- signal?: AbortSignalLike;
21
- /** Cancel the task server-side when `signal` fires. Default true. */
22
- cancelOnAbort?: boolean;
23
- /** Return artifact bytes inline. Default false: base64 of a PDF is not context. */
24
- includeData?: boolean;
25
- /** Longest write-up handed back. Default 4000 characters. */
26
- maxChars?: number;
27
- /** Injected for tests, and for anyone fronting Moa with their own transport. */
28
- client?: MoaClient;
29
- /** Injected for tests. */
30
- fetch?: typeof fetch;
31
- }
32
- /**
33
- * A document agent, briefed in your house style.
34
- *
35
- * Style is the deployment's business and content is the model's: the guide
36
- * comes from `options.design`, and `design_notes` may bend it for one document
37
- * rather than replace it. `effect` is `destructive` because a second call is a
38
- * second bill and a second set of files — never something to retry blindly.
39
- */
40
- export declare function writeDocument(opts: WriteDocumentOptions): import("../../index.js").Tool<import("../../index.js").ArgsOf<readonly [{
41
- readonly name: "brief";
42
- readonly type: "text";
43
- readonly required: true;
44
- readonly maxLength: 8000;
45
- readonly description: string;
46
- }, {
47
- readonly name: "format";
48
- readonly type: "enum";
49
- readonly required: true;
50
- readonly options: readonly ["pdf", "pptx", "docx", "md"];
51
- readonly description: "pptx for anything presented, pdf for anything sent, docx for anything edited.";
52
- }, {
53
- readonly name: "source";
54
- readonly type: "text";
55
- readonly required: false;
56
- readonly maxLength: 40000;
57
- readonly description: string;
58
- }, {
59
- readonly name: "design_notes";
60
- readonly type: "text";
61
- readonly required: false;
62
- readonly maxLength: 2000;
63
- readonly description: string;
64
- }, {
65
- readonly name: "session_id";
66
- readonly type: "string";
67
- readonly required: false;
68
- readonly description: string;
69
- }]>>;
70
- export { DEFAULT_DESIGN, type DesignGuide, mergeDesign, renderDesign } from "./design.js";
71
- export { MOA_BASE_URL, MoaApiError, type MoaClientConfig } from "./http.js";
72
- export type { MoaClient, PollOptions } from "./moa.js";
73
- export { createMoaClient, INTERACTIVE_TIMEOUT_MS, MoaAbortedError, MoaTimeoutError, pollUntilDone, } from "./moa.js";
74
- export type { ArtifactFormat, MoaArtifact, MoaInput, MoaResult, MoaStatus, } from "./types.js";
@@ -1 +0,0 @@
1
- import{tool as f}from"../../tool/index.js";import{DEFAULT_DESIGN as l,mergeDesign as h,renderDesign as g}from"./design.js";import{createMoaClient as p,MoaAbortedError as w,MoaTimeoutError as y,pollUntilDone as M}from"./moa.js";const _=["pdf","pptx","docx","md"],E=6e5,x=2e3,T=4e3;function A(e,r){return`Commissions a document agent to write and render a finished file \u2014 ${e.map(t=>`.${t}`).join(", ")} \u2014 and hands back where to download it. Use it when the answer has to be a document somebody opens: a report, a deck, a proposal, a one-pager. Not for text you can simply write in your reply. It runs for minutes and it costs money, so brief it once and brief it fully \u2014 the agent cannot see this conversation, so every fact, figure and quote it must use goes in \`source\`. Design is already settled by ${r.name??"the house style"}; use \`design_notes\` only for what this one document must do differently.`}function U(e,r,t,d){const i=(e.artifacts??[]).map(o=>({name:o.name,format:o.format,url:o.url,mime:o.mime,sizeBytes:o.sizeBytes,...d&&o.data?{data:o.data}:{}}));return{taskId:e.id,sessionId:e.sessionId,status:"done",style:r,documents:i,delivered:i.length>0,writeUp:e.output?.slice(0,t),truncated:(e.output?.length??0)>t,turns:e.numTurns,costMicroUsd:e.costUsd===void 0?void 0:Math.round(e.costUsd*1e6)}}function b(e,r,t){return{taskId:e,status:"running",style:r,documents:[],delivered:!1,note:`Still rendering after ${Math.round(t.timeoutMs/1e3)}s. The task is not lost \u2014 say so rather than commissioning it again.`}}function L(e){if(!e.client&&!e.apiKey?.trim())throw new Error("writeDocument needs a Moa apiKey; it will not read one from the process.");const r=e.formats?.length?e.formats:_,t=h(l,e.design),d=e.maxChars??T,i=e.client??p({apiKey:e.apiKey,baseUrl:e.baseUrl,...e.fetch?{fetch:e.fetch}:{}});return f({name:"write_document",description:A(r,t),effect:"destructive",args:[{name:"brief",type:"text",required:!0,maxLength:8e3,description:"What to produce, who reads it, what it must cover and how long it runs. Write it as you would brief a person who knows the craft and nothing about this account."},{name:"format",type:"enum",required:!0,options:["pdf","pptx","docx","md"],description:"pptx for anything presented, pdf for anything sent, docx for anything edited."},{name:"source",type:"text",required:!1,maxLength:4e4,description:"The material it must build from \u2014 figures, findings, quotes, an outline \u2014 verbatim. Anything missing here the agent will either omit or invent."},{name:"design_notes",type:"text",required:!1,maxLength:2e3,description:"How this one document departs from the house style, in plain words. Leave it empty unless the user asked for something specific."},{name:"session_id",type:"string",required:!1,description:"Continue an earlier write_document instead of starting over \u2014 take it from that result's sessionId. Use it to revise a document or to recast one into another format."}],run:async({brief:o,format:s,source:u,design_notes:m,session_id:c})=>{if(!r.includes(s))throw new Error(`write_document was asked for .${s}, which this deployment does not produce (${r.map(n=>`.${n}`).join(", ")}).`);const a=await i.create({task:o,context:u,design:g(t,s,m),artifacts:[s],...c?{sessionId:c}:{}});try{const n=await M(i,a,{timeoutMs:e.timeoutMs??E,settleMs:x,expectArtifacts:!0,...e.signal?{signal:e.signal}:{}});if(n.status==="failed")throw new Error(`write_document: Moa task ${a} failed: ${n.error??"no reason given"}`);return U(n,t.name,d,e.includeData===!0)}catch(n){if(n instanceof w&&e.cancelOnAbort!==!1&&await i.cancel?.(a).catch(()=>{}),n instanceof y)return b(a,t.name,n);throw n}}})}import{DEFAULT_DESIGN as $,mergeDesign as q,renderDesign as C}from"./design.js";import{MOA_BASE_URL as O,MoaApiError as R}from"./http.js";import{createMoaClient as N,INTERACTIVE_TIMEOUT_MS as B,MoaAbortedError as j,MoaTimeoutError as z,pollUntilDone as G}from"./moa.js";export{$ as DEFAULT_DESIGN,B as INTERACTIVE_TIMEOUT_MS,O as MOA_BASE_URL,j as MoaAbortedError,R as MoaApiError,z as MoaTimeoutError,N as createMoaClient,q as mergeDesign,G as pollUntilDone,C as renderDesign,L as writeDocument};
@@ -1,56 +0,0 @@
1
- import { type AbortSignalLike, type MoaClientConfig } from "./http.js";
2
- import type { MoaInput, MoaResult } from "./types.js";
3
- /**
4
- * Moa's task surface — fire, then await.
5
- *
6
- * `create()` hands back an id immediately and `get()` reads it; pass `waitMs`
7
- * (or use {@link pollUntilDone}) to long-poll, so one call is awaited rather
8
- * than a loop tight-polling. `cancel()` is optional so a test stub stays small.
9
- */
10
- export interface MoaClient {
11
- create(input: MoaInput): Promise<string>;
12
- get(id: string, opts?: {
13
- waitMs?: number;
14
- signal?: AbortSignalLike;
15
- }): Promise<MoaResult>;
16
- cancel?(id: string): Promise<void>;
17
- }
18
- /**
19
- * HTTP {@link MoaClient} over Moa's task API (`POST /tasks` → `GET /tasks/:id`).
20
- *
21
- * The request body carries the ask as prose — Moa's artifact pipeline is
22
- * prompt-driven and `POST /tasks` strips unknown fields, so context, design and
23
- * deliverables are blocks on the task rather than parameters beside it.
24
- */
25
- export declare function createMoaClient(config?: MoaClientConfig): MoaClient;
26
- /** The sane ceiling for a chat-bound call. A rendered document wants minutes, not this. */
27
- export declare const INTERACTIVE_TIMEOUT_MS = 30000;
28
- /** Thrown by {@link pollUntilDone} when the caller's abort signal fires. */
29
- export declare class MoaAbortedError extends Error {
30
- readonly taskId: string;
31
- constructor(taskId: string);
32
- }
33
- /** Thrown when the wait ran out. The task itself is still running server-side. */
34
- export declare class MoaTimeoutError extends Error {
35
- readonly taskId: string;
36
- readonly timeoutMs: number;
37
- constructor(taskId: string, timeoutMs: number);
38
- }
39
- export interface PollOptions {
40
- timeoutMs?: number;
41
- pollIntervalMs?: number;
42
- /**
43
- * Stops the wait and throws {@link MoaAbortedError}, cutting the held
44
- * long-poll with it. The task keeps running — cancelling it is the caller's call.
45
- */
46
- signal?: AbortSignalLike;
47
- /**
48
- * How long to wait before re-reading a done task whose result has not landed
49
- * yet. Moa flips the status slightly before it writes. Default 0.
50
- */
51
- settleMs?: number;
52
- /** Keep settling while a done task carries no artifacts. Set it when files were asked for. */
53
- expectArtifacts?: boolean;
54
- }
55
- /** Wait for a Moa task to settle, long-polling rather than tight-polling. */
56
- export declare function pollUntilDone(client: MoaClient, id: string, opts?: PollOptions): Promise<MoaResult>;
@@ -1,13 +0,0 @@
1
- import{createRequest as w,MoaApiError as M}from"./http.js";const h={queued:"pending",running:"running",succeeded:"done",failed:"failed",canceled:"failed"},T=["md","pdf","docx","pptx"],g=n=>{const t=n?.split(".").pop()?.toLowerCase();return t&&T.includes(t)?t:void 0};function E(n){if(!n?.length)return;const t=n.flatMap(e=>{const s=g(e.name);return!s||!(e.url||e.data)?[]:[{name:e.name??`document.${s}`,format:s,url:e.url,data:e.data,mime:e.contentType,sizeBytes:e.sizeBytes}]});return t.length?t:void 0}function I(n){return`
2
-
3
- <deliverables>
4
- Besides the text answer, save the finished deliverable into the artifacts/ directory of your working dir as: ${n.map(t=>`one .${t} file`).join(", ")}.
5
- </deliverables>`}function x(n){let t=n.task;return n.context&&(t+=`
6
-
7
- <context>
8
- ${n.context}
9
- </context>`),n.design&&(t+=`
10
-
11
- <design>
12
- ${n.design}
13
- </design>`),n.artifacts?.length&&(t+=I(n.artifacts)),t}function p(n={}){const t=w(n);return{async create(e){const s=await t("/tasks",{method:"POST",body:{task:x(e),mode:"striker",...e.sessionId?{sessionId:e.sessionId}:{},...e.newSessionId?{newSessionId:e.newSessionId}:{}}});if(!s.id)throw new M("Moa did not return a task id",502);return s.id},async get(e,s){const c=s?.waitMs&&s.waitMs>0?`?waitMs=${Math.floor(s.waitMs)}`:"",r=await t(`/tasks/${encodeURIComponent(e)}${c}`,{signal:s?.signal}),a=r.status?h[r.status]:void 0;return{id:r.id??e,status:a??"running",output:r.result,error:r.error,costUsd:r.costUsd,numTurns:r.numTurns,sessionId:r.sessionId,artifacts:E(r.artifacts)}},async cancel(e){await t(`/tasks/${encodeURIComponent(e)}`,{method:"DELETE"})}}}const k=3e4,b=2e3,A=3,y=25e3;class f extends Error{taskId;constructor(t){super(`Moa task ${t} was cancelled by the caller.`),this.name="MoaAbortedError",this.taskId=t}}class S extends Error{taskId;timeoutMs;constructor(t,e){super(`Moa task ${t} did not complete within ${e/1e3}s. The task may still be running in the background.`),this.name="MoaTimeoutError",this.taskId=t,this.timeoutMs=e}}const m=(n,t)=>new Promise(e=>{const s=()=>e();t?.addEventListener("abort",s,{once:!0}),setTimeout(()=>{t?.removeEventListener("abort",s),e()},n)});async function L(n,t,e={}){const s=e.timeoutMs??k,c=e.pollIntervalMs??b,r=e.settleMs??0,a=e.signal,l=Date.now()+s;for(;;){if(a?.aborted)throw new f(t);const u=l-Date.now();if(u<=0)throw new S(t,s);let o;try{o=await n.get(t,{waitMs:Math.min(u,y),signal:a})}catch(d){throw a?.aborted?new f(t):d}if(o.status==="failed")return o;if(o.status==="done"){const d=i=>!i.output?.trim()||e.expectArtifacts===!0&&!i.artifacts?.length;for(let i=0;r>0&&i<A&&d(o);i++)await m(r),o=await n.get(t);return o}await m(Math.min(c,Math.max(0,l-Date.now())),a)}}export{k as INTERACTIVE_TIMEOUT_MS,f as MoaAbortedError,S as MoaTimeoutError,p as createMoaClient,L as pollUntilDone};
@@ -1,33 +0,0 @@
1
- /** A document format Moa can leave behind in `artifacts/`. */
2
- export type ArtifactFormat = "md" | "pdf" | "docx" | "pptx";
3
- /** Where a Moa task has got to. `pending` is queued, `failed` is settled. */
4
- export type MoaStatus = "pending" | "running" | "done" | "failed";
5
- /** One finished file, either downloadable or carried inline as base64. */
6
- export interface MoaArtifact {
7
- name: string;
8
- format: ArtifactFormat;
9
- url?: string;
10
- data?: string;
11
- mime?: string;
12
- sizeBytes?: number;
13
- }
14
- /** What to hand Moa: the ask, the material it rests on, and the house style. */
15
- export interface MoaInput {
16
- task: string;
17
- context?: string;
18
- design?: string;
19
- artifacts?: readonly ArtifactFormat[];
20
- sessionId?: string;
21
- newSessionId?: string;
22
- }
23
- /** A Moa task as it stands right now. */
24
- export interface MoaResult {
25
- id: string;
26
- status: MoaStatus;
27
- output?: string;
28
- error?: string;
29
- costUsd?: number;
30
- numTurns?: number;
31
- sessionId?: string;
32
- artifacts?: MoaArtifact[];
33
- }
File without changes
@@ -1,4 +0,0 @@
1
- const h=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 $(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 d(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 "):null}function f(n,t){const o=n.abstract.trim();return o?o.length>t?`${o.slice(0,t).trimEnd()}\u2026`:o:null}function m(n,t,o){const e=`${t.length} paper${t.length===1?"":"s"} on "${n}", most relevant first`,u=t.map((l,s)=>[`${s+1}. ${$(l)}`,d(l),f(l,o)].filter(i=>i!==null).map((i,r)=>r===0?i:` ${i}`).join(`
2
- `));return[e,...u].join(`
3
-
4
- `)}export{m as rendered};