@wrenautomation/birdwatch 0.29.0 → 0.30.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 +36 -53
- package/dist/app/cli.js +2 -2
- package/dist/app/cli.js.map +1 -1
- package/dist/app/engine-cli.d.ts +2 -8
- package/dist/app/engine-cli.js +18 -160
- package/dist/app/engine-cli.js.map +1 -1
- package/dist/app/index.d.ts +3 -3
- package/dist/app/index.js +3 -3
- package/dist/app/index.js.map +1 -1
- package/dist/document/extract.d.ts +1 -1
- package/dist/document/types.d.ts +2 -2
- package/dist/document/types.js +2 -2
- package/dist/engine/agent.d.ts +12 -10
- package/dist/engine/agent.js +21 -20
- package/dist/engine/agent.js.map +1 -1
- package/dist/engine/context.d.ts +9 -0
- package/dist/engine/{runner.js → context.js} +10 -78
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +2 -2
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/judge.d.ts +1 -1
- package/dist/engine/plan.d.ts +4 -4
- package/dist/engine/plan.js +4 -4
- package/dist/engine/plan.js.map +1 -1
- package/dist/{plan → engine}/plans/index.d.ts +4 -2
- package/dist/engine/plans/index.js +104 -0
- package/dist/engine/plans/index.js.map +1 -0
- package/dist/engine/ports.d.ts +9 -57
- package/dist/engine/ports.js +3 -3
- package/dist/engine/ports.js.map +1 -1
- package/dist/engine/recall.d.ts +1 -1
- package/dist/engine/state.d.ts +0 -3
- package/dist/engine/state.js +1 -25
- package/dist/engine/state.js.map +1 -1
- package/dist/engine/steps.d.ts +20 -20
- package/dist/engine/steps.js +42 -167
- package/dist/engine/steps.js.map +1 -1
- package/dist/engine/tree.d.ts +1 -1
- package/dist/engine/tree.js.map +1 -1
- package/dist/{plan → engine}/types.d.ts +14 -165
- package/dist/{plan → engine}/types.js +18 -73
- package/dist/engine/types.js.map +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +9 -9
- package/dist/ledger/files.d.ts +1 -1
- package/dist/ledger/files.js +1 -1
- package/dist/ledger/files.js.map +1 -1
- package/dist/ledger/ledger.d.ts +2 -2
- package/dist/ledger/records.d.ts +139 -139
- package/dist/ledger/records.js +6 -6
- package/dist/ledger/records.js.map +1 -1
- package/dist/sources/arxiv.d.ts +1 -1
- package/dist/sources/google.d.ts +1 -1
- package/dist/sources/paste.d.ts +1 -1
- package/dist/sources/sites.d.ts +1 -1
- package/package.json +4 -8
- package/dist/engine/runner.d.ts +0 -30
- package/dist/engine/runner.js.map +0 -1
- package/dist/plan/index.d.ts +0 -7
- package/dist/plan/index.js +0 -8
- package/dist/plan/index.js.map +0 -1
- package/dist/plan/model.d.ts +0 -6
- package/dist/plan/model.js +0 -7
- package/dist/plan/model.js.map +0 -1
- package/dist/plan/plan.d.ts +0 -31
- package/dist/plan/plan.js +0 -87
- package/dist/plan/plan.js.map +0 -1
- package/dist/plan/plans/index.js +0 -236
- package/dist/plan/plans/index.js.map +0 -1
- package/dist/plan/render.d.ts +0 -41
- package/dist/plan/render.js +0 -75
- package/dist/plan/render.js.map +0 -1
- package/dist/plan/review.d.ts +0 -11
- package/dist/plan/review.js +0 -39
- package/dist/plan/review.js.map +0 -1
- package/dist/plan/triage.d.ts +0 -18
- package/dist/plan/triage.js +0 -65
- package/dist/plan/triage.js.map +0 -1
- package/dist/plan/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,23 +7,22 @@ run read. `skills/birdwatch/SKILL.md` (in this repo) is how an agent drives it.
|
|
|
7
7
|
|
|
8
8
|
Reading the web and files (the fetch ladder, HTML and PDF to text, video) is
|
|
9
9
|
[`@wrenautomation/read`](https://github.com/wrenautomation/engine/tree/main/packages/read)'s. Every model call goes through
|
|
10
|
-
[`@wrenautomation/models`](https://github.com/wrenautomation/engine/tree/main/packages/models). Until 0.29.0 this was three packages: raven (
|
|
11
|
-
|
|
10
|
+
[`@wrenautomation/models`](https://github.com/wrenautomation/engine/tree/main/packages/models). Until 0.29.0 this was three packages: raven (a model planner, gone at 0.30.0: the
|
|
11
|
+
agent plans), falcon (`./document`) and weaver (the engine, sources, ledger and CLI).
|
|
12
12
|
|
|
13
13
|
| Import | Does |
|
|
14
14
|
|---|---|
|
|
15
|
-
| `@wrenautomation/birdwatch/engine` | `Source` (a name, `search` and/or `open`, a cost), `registry` and the plan's allow-list; the agent's calls (`planRound`, `search`, `open`, `extract`, `check`, `add`, `accept`, `brief`), one step each; the judge's passes (`judgeHits`, `judgeDocuments`, `judgeFindings`, `judgeStats`); `
|
|
15
|
+
| `@wrenautomation/birdwatch/engine` | `Source` (a name, `search` and/or `open`, a cost), `registry` and the plan's allow-list; the agent's calls (`planRound`, `search`, `open`, `extract`, `check`, `add`, `accept`, `brief`), one step each; the judge's passes (`judgeHits`, `judgeDocuments`, `judgeFindings`, `judgeStats`); `fold` rebuilds a run's state from its records, so a step run again skips what is done; `Transient`, the failure worth another try; the ask and plan file shapes (`Ask`, `PlanFile`, `Query`, `QueryTree`, `Edge`, `Snapshot`) and a stock plan file per ask kind (`web`, `video`, `channel`, `person`, `site`: sources, budget, `age`, `rules`, the query tree's guide) |
|
|
16
16
|
| `@wrenautomation/birdwatch/sources` | `searxng`, `exa` (search), `youtube` (search and read, yt-dlp), `fetcher` (open, the polite fetcher), `ladder` (open, autobrowse's fetch ladder up to a browser), `reddit` and `facebook` (open a thread or a group post through autobrowse's site routes), `google` (Google's own page through the desk's browser: each result a hit, the AI Overview one hit with its text as the snippet, each page it cites a hit), `arxiv` (search and read: the API inside `BIRDWATCH_ARXIV_CATEGORIES`, each hit led by its OpenAlex citation count and comment; the paper read whole from its HTML, else its PDF; one call per 3 s), `paste` (a person's URLs), `ledger` (hits earlier runs saw). A query with a `site` skips a searcher that cannot keep to it (`Source.keeps`: `youtube` keeps to YouTube only). A follow (`Source.follows`, `follow`) lists a page's author's posts (`youtube`: a channel's uploads, from a video or the channel, and a TikTok profile; `reddit`: a user's top posts and comments) or pages like it (`exa`: findSimilar) |
|
|
17
17
|
| `@wrenautomation/birdwatch/ledger` | the records (`query`, `hit`, `document`, `finding`, `verdict`, `call`, `spend`, `snapshot`, `edge`, `done`, `steer`, `gap`, `check`, `pattern`, `found`, `judge`, `person`), `FilesLedger` (a folder: `runs/<run>/<writer>.jsonl`, `plans/<kind>.json`), `MemoryLedger`, yield rows per source, person, channel and kind |
|
|
18
|
-
| `@wrenautomation/birdwatch/plan` | the planner: `plan` (an ask, proposals and yield rows in; typed queries out, each with `why`, `from`, `by`), `triage` (read or skip per hit), `review` (the edge after a round: `stop`, `again`, `back`), the shapes (`Ask`, `Plan`, `PlanFile`, `Query`, `Edge`, `Snapshot`), a stock plan file per ask kind (`web`, `video`, `channel`, `person`, `site`: prompts, sources, budget, `age`, the query tree's guide), the renderers. Pure functions and prompts; the model is injected. The runner uses all of it; an agent makes the three decisions itself and uses the shapes |
|
|
19
18
|
| `@wrenautomation/birdwatch/document` | the document side: `Document`, `Finding`, `Proof`, `Verdict` (zod), readers by media (`html`, `pdf`, `text`), `claimExtractor` (findings that quote the document, through the gate, and a verdict on the document: its quality and the ask's gap; no cap on findings or their length, and an answer the model's output cap cut is continued from its last kept quote), the `Model` seam it takes and the `Cut` a model throws when its cap cut an answer |
|
|
20
19
|
|
|
21
|
-
Every record carries `from` (ai, human or tool) and `by` (
|
|
22
|
-
person
|
|
23
|
-
|
|
24
|
-
`birdwatch learn <kind>` writes its next version from what a run taught
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
Every record carries `from` (ai, human or tool) and `by` (an agent or model id, a source, a
|
|
21
|
+
person). A person's keep or drop is the same record as the agent's. The plan file in
|
|
22
|
+
`plans/<kind>.json` beside the ledger (sources, budget, age, rules, the tree guide) is where a run
|
|
23
|
+
learns: `birdwatch learn <kind>` writes its next version from what a run taught, on the person's
|
|
24
|
+
agreement; a kind with no tuned file runs the stock one (`birdwatch plans <kind>` prints it).
|
|
25
|
+
`brief.guide` shows the kind's age sentence and rules to the agent.
|
|
27
26
|
|
|
28
27
|
## CLI
|
|
29
28
|
|
|
@@ -41,12 +40,9 @@ COHERE_API_KEY=... npx birdwatch watch talk.mp4 # spee
|
|
|
41
40
|
npx birdwatch plans web > ledger/plans/web.json # a stock plan file, to tune
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
records. `run` is the runner: the planner's prompts make those decisions and the loop runs alone. Both
|
|
48
|
-
write the same ledger, so `run --resume` takes over a run an agent started and `brief` reads a
|
|
49
|
-
run the runner made. Every command prints JSON.
|
|
43
|
+
An agent (Claude Code, or any agent with a shell) drives the engine one command at a time: it
|
|
44
|
+
plans, triages, reviews and stops in its own head, and the engine does the fixed work (search,
|
|
45
|
+
open, extract behind the quote gate, check) and keeps the records. Every command prints JSON.
|
|
50
46
|
|
|
51
47
|
```sh
|
|
52
48
|
export BIRDWATCH_AGENT=claude-code # the agent's id on every record it writes
|
|
@@ -115,21 +111,14 @@ hit with the facets that found it, most first; `brief.tree` shows every try with
|
|
|
115
111
|
|
|
116
112
|
The agent reads for itself: `show <run> <document>` prints a document's stored text, `add
|
|
117
113
|
finding` writes what it read behind the same quote gate, `add check` its own check. `extract` and
|
|
118
|
-
`check` put a model behind the same gate instead, for bulk
|
|
119
|
-
the same records.
|
|
114
|
+
`check` put a model behind the same gate instead, for bulk; both paths write the same records.
|
|
120
115
|
|
|
121
|
-
The
|
|
116
|
+
The person, through the agent or by hand:
|
|
122
117
|
|
|
123
118
|
```sh
|
|
124
|
-
|
|
125
|
-
npx birdwatch
|
|
126
|
-
npx birdwatch
|
|
127
|
-
npx birdwatch run --resume 01M5YS... --url https://a.example/report # from the step it stopped on; same --url, same --model
|
|
128
|
-
npx birdwatch run "..." --pause # stop after each round's review
|
|
129
|
-
npx birdwatch add steer 01M5YS... "numbers only; skip vendor pages" --human # what you say, first in every prompt after
|
|
130
|
-
npx birdwatch run --resume 01M5YS... --pause # the next round, steered
|
|
131
|
-
npx birdwatch add gap 01M5YS... "no UK numbers yet" --human # what the run lacks: its review and next plan read it
|
|
132
|
-
npx birdwatch add edge 01M5YS... back --to fetch --why "searxng was down" --human # rerun one step of the round; then --resume
|
|
119
|
+
npx birdwatch add steer 01M5YS... "numbers only; skip vendor pages" --human # what you say: the brief shows it first
|
|
120
|
+
npx birdwatch add gap 01M5YS... "no UK numbers yet" --human # what the run lacks: the next plan reads it
|
|
121
|
+
npx birdwatch add edge 01M5YS... back --to fetch --why "searxng was down" --human # the step to run again
|
|
133
122
|
npx birdwatch trace 01M5YS... --type finding spend # the run's records, JSON lines
|
|
134
123
|
npx birdwatch add call 01M5YS... 01M5YT... drop --why "a forecast, not a result" --human # a call in your name
|
|
135
124
|
npx birdwatch yield --source exa # hits, reads, proposed, kept
|
|
@@ -141,8 +130,7 @@ The model is `anthropic` by default (Claude Haiku direct, `ANTHROPIC_API_KEY`; `
|
|
|
141
130
|
anthropic:sonnet|opus|id` for another), `--model gateway[:alias]` for Wren's LLM gateway
|
|
142
131
|
(`WREN_LLM_GATEWAY_URL`, `WREN_LLM_GATEWAY_TOKEN`), or any command that reads a prompt on stdin
|
|
143
132
|
and prints the answer (`claude -p --output-format json` is Claude Code headless). The model does
|
|
144
|
-
the bulk (extract, check);
|
|
145
|
-
comes next (plan, triage, review): `anthropic:sonnet` when the model is Haiku, else the model. Sources come
|
|
133
|
+
the bulk (extract, check) only; the agent decides. Sources come
|
|
146
134
|
from the environment: `SEARXNG_URL` (and `SEARXNG_TOKEN`, a bearer, for an instance behind a
|
|
147
135
|
proxy), `EXA_API_KEY` (or `_1..n`), `GEMINI_API_KEY` for videos without captions,
|
|
148
136
|
`BIRDWATCH_CONTACT` for the fetcher and `arxiv` (no contact, no page opens; `OPENALEX_API_KEY`, free, gives `arxiv` its own citation budget, and `SEMANTIC_SCHOLAR_API_KEY`, free, its own rate for `--as refs`; `--sources arxiv` names it, since no stock plan file does), and for the ladder either
|
|
@@ -191,24 +179,21 @@ that still cannot go on writes no `done`, and `run` waits and resumes it, five t
|
|
|
191
179
|
before it exits 2. Searches, reads and extracts fan out (four reads or extracts at a time);
|
|
192
180
|
each document's records land as its batch settles, and a step run again skips what its records
|
|
193
181
|
show done. An extract that fails for good (the model's answer would not parse, a 400) stops the
|
|
194
|
-
step with its findings so far on record and
|
|
182
|
+
step with its findings so far on record and exits 2: `extract` again asks only for the
|
|
195
183
|
documents without a verdict. A model answer the output cap cut is not a failure: the whole
|
|
196
184
|
findings before the cut go through the gate and the model is asked again for the rest of the
|
|
197
185
|
document, with those findings known (`./document`'s `Cut`; the `spend` carries both calls' tokens).
|
|
198
186
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
a round past `budget.rounds` parks the run the same way (`parked` says so, no exit code): the
|
|
204
|
-
budget is a guard rail, not a stop rule. `--resume` goes one more round; `plan --run` opens it
|
|
205
|
-
by hand.
|
|
187
|
+
Between rounds the person reads `brief` (or the agent tells them), keeps or drops findings (`add
|
|
188
|
+
call`), and steers the run with free text (`add steer`). Every `steer` record shows first in the
|
|
189
|
+
brief and in the extract's prompt of the rounds after it. `budget.rounds` is a guard
|
|
190
|
+
rail, not a stop rule: past it, the edge's why says why one more round is worth it.
|
|
206
191
|
|
|
207
192
|
The steps talk in English records. A query every searcher answered with no new hit, and a kept
|
|
208
193
|
hit no source could open (a 403, a block the ladder could not pass), each write a `gap`; so does
|
|
209
194
|
`add gap`. The round's review reads the gaps and a person's keep or drop with its why; the next
|
|
210
195
|
round's plan reads them, and the review's why on `again`, under "The last round left open".
|
|
211
|
-
`add edge back --to <step>` writes a person's edge: the fold takes it like the
|
|
196
|
+
`add edge back --to <step>` writes a person's edge: the fold takes it like the agent's. A `pattern`
|
|
212
197
|
(`add pattern`) is what several kept findings say together, in the agent's words; its count of
|
|
213
198
|
distinct sites is computed from the findings' proofs, never claimed, and `brief.patterns` lists
|
|
214
199
|
them for the report. A run can be a child (`--parent <run> --why`): its snapshot names the parent, its plan reads
|
|
@@ -258,16 +243,14 @@ gates on it yet.
|
|
|
258
243
|
From 0.25.0 the judge reads a page whole: the body to its references (an appendix stays), in
|
|
259
244
|
parts of 64,000 characters, 4 at most, each answer the most yes of any part. Jev's input is
|
|
260
245
|
cheap (about $0.04 per million tokens), so a paper costs about a tenth of a cent. A run's
|
|
261
|
-
criteria (`--criterion name="question"`, on `
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
and each test) as a hint. Their mean sorts `hits.uncalled` and `documents.toExtract`; `brief.judge.criteria` gives each one's AUC against
|
|
246
|
+
criteria (`--criterion name="question"`, on `plan`) are the person's own yes-or-no tests of a
|
|
247
|
+
good source: the judge asks each one on every hit and every page, and the brief shows its odds
|
|
248
|
+
on each hit (serves and each test) as a hint. Their mean sorts `hits.uncalled` and `documents.toExtract`; `brief.judge.criteria` gives each one's AUC against
|
|
265
249
|
"a finding on it was kept", on hits and on pages. A sort and a hint, never a gate.
|
|
266
250
|
|
|
267
251
|
From 0.27.0 the extractor reads the body too (the reference list holds no finding). Each review
|
|
268
252
|
sees every earlier round's new hits and kept findings, and decides when to stop. A paper's
|
|
269
|
-
`follow --as refs` and `--as citers` list what it cites and what cites it.
|
|
270
|
-
triage and review on Sonnet while Haiku does the bulk. A check (25 papers, Sonnet as reference):
|
|
253
|
+
`follow --as refs` and `--as citers` list what it cites and what cites it. A check (25 papers, Sonnet as reference):
|
|
271
254
|
on the whole page, Jev's criteria odds rank Sonnet's yes over its no with AUC 0.94 to 1.00; on the
|
|
272
255
|
abstract, 0.77 to 1.00.
|
|
273
256
|
|
|
@@ -308,21 +291,21 @@ The engine, with your own model and sources:
|
|
|
308
291
|
|
|
309
292
|
```ts
|
|
310
293
|
import { claimExtractor } from "@wrenautomation/birdwatch/document";
|
|
311
|
-
import { registry,
|
|
294
|
+
import { extract, open, planRound, registry, search } from "@wrenautomation/birdwatch/engine";
|
|
312
295
|
import { FilesLedger, ulids } from "@wrenautomation/birdwatch/ledger";
|
|
313
296
|
import { exa, fetcherSource } from "@wrenautomation/birdwatch/sources";
|
|
314
297
|
|
|
315
298
|
const ledger = new FilesLedger("./ledger", "will");
|
|
316
299
|
const sources = registry(exa({ keys }), fetcherSource({ fetcher }));
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
);
|
|
321
|
-
|
|
300
|
+
const deps = { sources, ledger, model, extractor: claimExtractor, clock: () => new Date(), ids: ulids(), writer: "will" };
|
|
301
|
+
const agent = { from: "ai" as const, by: "my-agent" };
|
|
302
|
+
const { run } = await planRound(deps, { ask: { text: "how do staffing firms win repeat clients", kind: "web" }, planFile, model: model.id });
|
|
303
|
+
await search(deps, run, [{ terms: "staffing repeat client rate", why: "the ask's words" }], agent);
|
|
304
|
+
await open(deps, run, { urls: ["https://a.example/report"], why: "a survey" }, agent);
|
|
305
|
+
await extract(deps, run); // findings, each with its proof: a quote on a page the run read
|
|
322
306
|
```
|
|
323
307
|
|
|
324
|
-
|
|
325
|
-
passes a few and calls again with `resume`. A host with its own ledger, sources or model mounts
|
|
308
|
+
A host with its own ledger, sources or model mounts
|
|
326
309
|
the same commands: `addEngineCommands(program, { ledger, sources, model, extractor })` from
|
|
327
310
|
`@wrenautomation/birdwatch` (wren's `research` command is these over Postgres).
|
|
328
311
|
|
package/dist/app/cli.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* text and links, `watch <video>` what a video file shows and says. `plans [kind]` prints a stock
|
|
7
7
|
* plan file to tune. Keys come from the environment: COHERE_API_KEY (or COHERE_API_KEY_1..n),
|
|
8
8
|
* GEMINI_API_KEY. The engine's commands are in `engine-cli.ts`: the agent's (`plan`, `search`,
|
|
9
|
-
* `open`, `extract`, `check`, `add`, `brief`), the
|
|
9
|
+
* `open`, `extract`, `check`, `add`, `brief`), the plan file's (`learn`) and
|
|
10
10
|
* the ledger's (`trace`, `yield`, `sources`). A person's word goes through `add ... --human`.
|
|
11
11
|
*/
|
|
12
12
|
import { readFile } from "node:fs/promises";
|
|
@@ -14,7 +14,7 @@ import { PoliteFetcher, userAgent } from "@wrenautomation/read/fetch";
|
|
|
14
14
|
import { htmlText, pdfText, readPage } from "@wrenautomation/read/text";
|
|
15
15
|
import { cohereEye, cohereSpeech, flatPlaylist, frameCounts, geminiVideo, readVideo, readYoutube, renderWatched, watchFile, } from "@wrenautomation/read/video";
|
|
16
16
|
import { Command } from "commander";
|
|
17
|
-
import { AskKind, defaultPlanFile, PLAN_KINDS } from "../plan
|
|
17
|
+
import { AskKind, defaultPlanFile, PLAN_KINDS } from "../engine/plan.js";
|
|
18
18
|
import { addEngineCommands, keys } from "./engine-cli.js";
|
|
19
19
|
const program = new Command()
|
|
20
20
|
.name("birdwatch")
|
package/dist/app/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/app/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EACL,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,EACT,WAAW,EACX,aAAa,EAEb,SAAS,GACV,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/app/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/app/engine-cli.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Model } from "@wrenautomation/models";
|
|
2
2
|
import { type Judge } from "@wrenautomation/models/judge";
|
|
3
3
|
import type { Command } from "commander";
|
|
4
4
|
import type { Extractor } from "../document/index.js";
|
|
5
5
|
import { costOf, type Sources } from "../engine/index.js";
|
|
6
|
+
import { type AskKind, Criterion } from "../engine/plan.js";
|
|
6
7
|
import { FilesLedger, type Ledger, type Problem } from "../ledger/index.js";
|
|
7
|
-
import { type AskKind, Criterion } from "../plan/index.js";
|
|
8
|
-
/** Resumes `run` makes on its own after a transient failure. */
|
|
9
|
-
export declare const RESUMES = 5;
|
|
10
8
|
export { costOf };
|
|
11
|
-
/** The pause before a resume: to `retryAfter` (five minutes at most), else 15 s per resume so far. */
|
|
12
|
-
export declare function resumeWaitMs(n: number, retryAfter: string | undefined, now?: number): number;
|
|
13
9
|
/** `NAME`, then `NAME_1`, `NAME_2`, ... in order: a fleet of keys for one vendor. */
|
|
14
10
|
export declare function keys(printed: NodeJS.ProcessEnv, name: string): string[];
|
|
15
11
|
/** `name=question` lines as criteria; a name the judge already asks, or a repeat, fails. */
|
|
@@ -31,8 +27,6 @@ export interface EngineCliDeps {
|
|
|
31
27
|
/** The judge for a `--judge` spec; the default is models' `judgeFromSpec`. */
|
|
32
28
|
judge?(spec: string, printed: NodeJS.ProcessEnv): Judge | null;
|
|
33
29
|
extractor?: Extractor;
|
|
34
|
-
/** The pause before a resume; a test passes a stub. */
|
|
35
|
-
sleep?(ms: number): Promise<void>;
|
|
36
30
|
}
|
|
37
31
|
/** The sources this machine can run, from the environment. */
|
|
38
32
|
export declare function sourcesHere(printed: NodeJS.ProcessEnv, ledger: FilesLedger, ytDlp: string, warn?: (line: string) => void): Sources;
|
package/dist/app/engine-cli.js
CHANGED
|
@@ -1,45 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The engine from a shell
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* can drive it. The ledger is a folder (`--ledger`, BIRDWATCH_LEDGER, default `./ledger`); a
|
|
2
|
+
* The engine from a shell, one step per command for the agent that drives it: `plan`, `search`,
|
|
3
|
+
* `open`, `extract`, `check`, `add`, `brief`, `trace` a run, `yield` per source, `sources` on this
|
|
4
|
+
* machine. Every command prints JSON. The ledger is a folder (`--ledger`, BIRDWATCH_LEDGER, default `./ledger`); a
|
|
6
5
|
* writer names this terminal's file in it (`--writer`, BIRDWATCH_WRITER, default the user name).
|
|
7
6
|
* The model is a `@wrenautomation/models` spec: `anthropic` by default (Claude Haiku direct,
|
|
8
7
|
* ANTHROPIC_API_KEY), `gateway[:alias]`, `gemini`, `claude-code`, `command:<cmd>` and the rest
|
|
9
8
|
* of its grammar. A host with its own ledger, sources or
|
|
10
9
|
* model passes them in `EngineCliDeps`; wren's `research` command is these commands over
|
|
11
|
-
* Postgres. A
|
|
12
|
-
*
|
|
10
|
+
* Postgres. A step that stops on a transient failure (a 429, a 5xx, a dropped connection) exits
|
|
11
|
+
* with its note; the agent runs it again, and it skips what its records show done.
|
|
13
12
|
*/
|
|
14
13
|
import { mkdir, readFile } from "node:fs/promises";
|
|
15
14
|
import { userInfo } from "node:os";
|
|
16
15
|
import { serve } from "@hono/node-server";
|
|
17
|
-
import { isTransient } from "@wrenautomation/models";
|
|
18
16
|
import { judgeFromSpec } from "@wrenautomation/models/judge";
|
|
19
17
|
import { modelFromSpec } from "@wrenautomation/models/spec";
|
|
20
18
|
import { PoliteFetcher, userAgent } from "@wrenautomation/read/fetch";
|
|
21
19
|
import { geminiVideo } from "@wrenautomation/read/video";
|
|
22
20
|
import { claimExtractor } from "../document/index.js";
|
|
23
|
-
import { accept, add, brief, check, costOf, extract, fold, follow, joined, open, planRound, RESERVED, recall, registry,
|
|
21
|
+
import { accept, add, brief, check, costOf, extract, fold, follow, joined, open, planRound, RESERVED, recall, registry, search, searchTree, tunePlan, } from "../engine/index.js";
|
|
22
|
+
import { Criterion, defaultPlanFile, PLAN_KINDS, QueryTree, TreeGuide, treeOf, } from "../engine/plan.js";
|
|
23
|
+
import { Step as PlanStep } from "../engine/types.js";
|
|
24
24
|
import { FilesLedger, FollowAs, SOURCE_CLASSES, SourceClass, ulids, } from "../ledger/index.js";
|
|
25
|
-
import {
|
|
26
|
-
import { accountsFrom, arxivSource, exa, facebookSource, fetcherSource, googleSource, httpLadderClient, ingressLadderClient, ladderSource, ledgerSource, paste, redditSource, searxng, ytdlpSource, } from "../sources/index.js";
|
|
25
|
+
import { accountsFrom, arxivSource, exa, facebookSource, fetcherSource, googleSource, httpLadderClient, ingressLadderClient, ladderSource, ledgerSource, redditSource, searxng, ytdlpSource, } from "../sources/index.js";
|
|
27
26
|
import { SERVE_ORIGINS, SERVE_PORT, serveApp, watchRuns } from "./serve.js";
|
|
28
27
|
const DEFAULT_MODEL = "anthropic";
|
|
29
|
-
/** Resumes `run` makes on its own after a transient failure. */
|
|
30
|
-
export const RESUMES = 5;
|
|
31
|
-
/** The longest pause before a resume, whatever `retry-after` said. */
|
|
32
|
-
const MAX_RESUME_WAIT_MS = 300_000;
|
|
33
28
|
export { costOf };
|
|
34
|
-
/** The pause before a resume: to `retryAfter` (five minutes at most), else 15 s per resume so far. */
|
|
35
|
-
export function resumeWaitMs(n, retryAfter, now = Date.now()) {
|
|
36
|
-
if (retryAfter) {
|
|
37
|
-
const until = Date.parse(retryAfter) - now;
|
|
38
|
-
if (!Number.isNaN(until))
|
|
39
|
-
return Math.max(0, Math.min(until, MAX_RESUME_WAIT_MS));
|
|
40
|
-
}
|
|
41
|
-
return 15_000 * n;
|
|
42
|
-
}
|
|
43
29
|
/** `NAME`, then `NAME_1`, `NAME_2`, ... in order: a fleet of keys for one vendor. */
|
|
44
30
|
export function keys(printed, name) {
|
|
45
31
|
const out = [];
|
|
@@ -110,7 +96,7 @@ export function sourcesHere(printed, ledger, ytDlp, warn = (l) => console.error(
|
|
|
110
96
|
url: printed.RESTATE_INGRESS_URL,
|
|
111
97
|
token: printed.RESTATE_AUTH_TOKEN ?? null,
|
|
112
98
|
...(printed.AUTOBROWSE_SERVICE ? { service: printed.AUTOBROWSE_SERVICE } : {}),
|
|
113
|
-
caller: `birdwatch:${printed.BIRDWATCH_AGENT ?? "
|
|
99
|
+
caller: `birdwatch:${printed.BIRDWATCH_AGENT ?? "cli"}`,
|
|
114
100
|
})
|
|
115
101
|
: null;
|
|
116
102
|
if (sites) {
|
|
@@ -140,7 +126,6 @@ export function allowList(sources, wanted, byHand, warn = (l) => console.error(l
|
|
|
140
126
|
: [...sources.values()].filter((s) => s.open && !out.includes(s.name)).map((s) => s.name);
|
|
141
127
|
return { sources: out, opens };
|
|
142
128
|
}
|
|
143
|
-
const progress = (e) => console.error(`${e.run} ${e.round}.${e.step} ${e.ok ? "ok" : "gave up"} ${e.ms}ms${e.note ? `: ${e.note}` : ""}`);
|
|
144
129
|
/** A kind the engine plans for; anything else is the caller's mistake, said in words. */
|
|
145
130
|
export const kindOf = (kind) => {
|
|
146
131
|
const k = PLAN_KINDS.find((p) => p === kind);
|
|
@@ -148,7 +133,7 @@ export const kindOf = (kind) => {
|
|
|
148
133
|
throw new Error(`no kind "${kind}": the kinds are ${PLAN_KINDS.join(", ")}`);
|
|
149
134
|
return k;
|
|
150
135
|
};
|
|
151
|
-
/** Prints what a command did as JSON; a step that gave up exits 2
|
|
136
|
+
/** Prints what a command did as JSON; a step that gave up exits 2. */
|
|
152
137
|
function printDid(did) {
|
|
153
138
|
console.error(`${did.run} ${did.round}.${did.step} ${did.ok ? "ok" : "gave up"}${did.note ? `: ${did.note}` : ""}`);
|
|
154
139
|
console.log(JSON.stringify(did, null, 2));
|
|
@@ -164,14 +149,12 @@ async function stdinText() {
|
|
|
164
149
|
}
|
|
165
150
|
export function addEngineCommands(program, deps = {}) {
|
|
166
151
|
const ledgerOf = deps.ledger ?? filesLedger;
|
|
167
|
-
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
168
152
|
const common = (c) => c
|
|
169
153
|
.option("--ledger <dir>", "the ledger folder", process.env.BIRDWATCH_LEDGER ?? "./ledger")
|
|
170
154
|
.option("--writer <name>", "this terminal's file in the ledger", process.env.BIRDWATCH_WRITER ?? userInfo().username);
|
|
171
155
|
const machine = (c) => c
|
|
172
156
|
.option("--model <spec>", "a models spec: anthropic[:haiku|sonnet|opus], gateway[:alias], gemini[:id], claude-code, command:<cmd>, ...", process.env.BIRDWATCH_MODEL ?? DEFAULT_MODEL)
|
|
173
157
|
.option("--no-model", "no model: nothing extracted or checked")
|
|
174
|
-
.option("--decider <spec>", "the model for the steps that decide what comes next (plan, triage, review); default: anthropic:sonnet when --model is Haiku, else --model", process.env.BIRDWATCH_DECIDER)
|
|
175
158
|
.option("--judge <spec>", "cheap typed answers on every hit, page and finding (a hint and a router): jev[:model], fake, off", process.env.BIRDWATCH_JUDGE ?? (process.env.TYPESAFE_API_KEY ? "jev" : "off"))
|
|
176
159
|
.option("--no-judge", "no judge")
|
|
177
160
|
.option("--yt-dlp <cmd>", "the yt-dlp command", process.env.BIRDWATCH_YTDLP ?? "yt-dlp");
|
|
@@ -185,12 +168,8 @@ export function addEngineCommands(program, deps = {}) {
|
|
|
185
168
|
? deps.sources(o, ledger)
|
|
186
169
|
: sourcesHere(process.env, ledger, o.ytDlp);
|
|
187
170
|
const model = o.model === false ? null : (deps.model ?? modelFromSpec)(o.model, process.env);
|
|
188
|
-
// Haiku restates, Sonnet decides: a few cents a run.
|
|
189
|
-
const deciderSpec = o.decider ??
|
|
190
|
-
(o.model === "anthropic" || o.model === "anthropic:haiku" ? "anthropic:sonnet" : null);
|
|
191
|
-
const decider = model && deciderSpec ? (deps.model ?? modelFromSpec)(deciderSpec, process.env) : null;
|
|
192
171
|
const judge = o.judge === false ? null : (deps.judge ?? judgeFromSpec)(o.judge, process.env);
|
|
193
|
-
return { ledger, sources, model, judge
|
|
172
|
+
return { ledger, sources, model, judge };
|
|
194
173
|
};
|
|
195
174
|
/**
|
|
196
175
|
* The ledger's tuned plan file for the kind (the stock one when none), its sources the run's
|
|
@@ -203,16 +182,16 @@ export function addEngineCommands(program, deps = {}) {
|
|
|
203
182
|
if (tuned.version < stock.version)
|
|
204
183
|
console.error(`plan file ${kind} is version ${tuned.version}; birdwatch ships ${stock.version}: delete it to take the new one`);
|
|
205
184
|
const wanted = byHand ?? [...first, ...tuned.sources];
|
|
206
|
-
//
|
|
185
|
+
// A run narrows to the plan file's sources, so the run's allow-list goes in as the plan file's.
|
|
207
186
|
const { sources: list, opens } = allowList(sources, wanted, !!byHand);
|
|
208
187
|
return { planFile: { ...tuned, sources: list }, opens };
|
|
209
188
|
};
|
|
210
|
-
const depsOf = (s,
|
|
189
|
+
const depsOf = (s, writer) => ({
|
|
211
190
|
...s,
|
|
212
191
|
extractor: deps.extractor ?? claimExtractor,
|
|
213
192
|
clock: () => new Date(),
|
|
214
193
|
ids: ulids(),
|
|
215
|
-
|
|
194
|
+
writer,
|
|
216
195
|
});
|
|
217
196
|
/** The agent's id: `--by` or BIRDWATCH_AGENT. Every record it writes names it. */
|
|
218
197
|
const agentOf = (o) => {
|
|
@@ -224,123 +203,6 @@ export function addEngineCommands(program, deps = {}) {
|
|
|
224
203
|
const whoOf = (o) => o.human
|
|
225
204
|
? { from: "human", by: o.human === true ? o.writer : o.human }
|
|
226
205
|
: { from: "ai", by: agentOf(o) };
|
|
227
|
-
machine(common(program.command("run [ask]")))
|
|
228
|
-
.description("run an ask through plan, fetch, triage, read, extract, check and review; prints the run as JSON")
|
|
229
|
-
.option("--kind <kind>", `one of ${PLAN_KINDS.join(", ")}`, "web")
|
|
230
|
-
.option("--known <line...>", "what is known already, one line each, with its URL or ledger id; a line without one is a premise")
|
|
231
|
-
.option("--premise <line...>", "what the ask takes as given (a buyer, an offer, a price): the tree tests each")
|
|
232
|
-
.option("--criterion <name=question...>", 'a yes-or-no test of a good source (code="Is code released?"): the judge asks it on every hit and every page read whole, a hint only')
|
|
233
|
-
.option("--steer <line...>", "what you say to the model, first in every prompt")
|
|
234
|
-
.option("--site <host>", "the site the ask is about")
|
|
235
|
-
.option("--sources <name...>", "the allow-list for this run (default: the plan file's, as far as this machine has them)")
|
|
236
|
-
.option("--propose <terms...>", "queries from you, merged with the model's in round 1")
|
|
237
|
-
.option("--url <url...>", "pages you want read: a paste source, first in the allow-list")
|
|
238
|
-
.option("--by <who>", "who proposes or pastes", process.env.BIRDWATCH_WRITER ?? userInfo().username)
|
|
239
|
-
.option("--resume <run>", "continue an earlier run from the step it stopped on")
|
|
240
|
-
.option("--run <id>", "the new run's id")
|
|
241
|
-
.option("--pause", "stop after each round's review: `birdwatch steer`, then --resume")
|
|
242
|
-
.option("--parent <run>", "a new run's parent: this run is a child of it (one niche of a wide run)")
|
|
243
|
-
.option("--why <why>", "with --parent: why this child")
|
|
244
|
-
.action(async (ask, o) => {
|
|
245
|
-
if (!ask && !o.resume)
|
|
246
|
-
throw new Error("run needs an ask, or --resume <run>");
|
|
247
|
-
const kind = kindOf(o.kind);
|
|
248
|
-
const s = setup(o);
|
|
249
|
-
const { ledger, sources } = s;
|
|
250
|
-
if (o.url?.length)
|
|
251
|
-
sources.set("paste", paste(o.url.map((url) => ({ url })), o.by));
|
|
252
|
-
const { planFile, opens } = await planFileOf(ledger, sources, kind, o.sources, o.url?.length ? ["paste"] : []);
|
|
253
|
-
const byHand = [
|
|
254
|
-
...(o.propose ?? []).map((terms) => ({ kind, terms, why: "proposed by hand" })),
|
|
255
|
-
// The paste answers the first query it sees: this one, when nothing else is proposed.
|
|
256
|
-
...(o.url?.length && !o.propose?.length && ask
|
|
257
|
-
? [{ kind, terms: ask, why: "the pasted pages" }]
|
|
258
|
-
: []),
|
|
259
|
-
];
|
|
260
|
-
const proposals = byHand.length
|
|
261
|
-
? [{ from: "human", by: o.by, queries: byHand }]
|
|
262
|
-
: [];
|
|
263
|
-
const id = o.resume ?? o.run ?? ulids()();
|
|
264
|
-
const engineDeps = depsOf(s, o.writer);
|
|
265
|
-
const options = {
|
|
266
|
-
...(ask
|
|
267
|
-
? {
|
|
268
|
-
ask: {
|
|
269
|
-
text: ask,
|
|
270
|
-
kind,
|
|
271
|
-
known: o.known ?? [],
|
|
272
|
-
premises: o.premise ?? [],
|
|
273
|
-
criteria: criteriaOf(o.criterion),
|
|
274
|
-
steer: o.steer ?? [],
|
|
275
|
-
...(o.site ? { site: o.site } : {}),
|
|
276
|
-
},
|
|
277
|
-
}
|
|
278
|
-
: {}),
|
|
279
|
-
planFile,
|
|
280
|
-
opens,
|
|
281
|
-
proposals,
|
|
282
|
-
onStep: progress,
|
|
283
|
-
...(o.pause ? { pause: true } : {}),
|
|
284
|
-
...(o.parent ? { parent: { run: o.parent, why: o.why ?? "" } } : {}),
|
|
285
|
-
};
|
|
286
|
-
let result = null;
|
|
287
|
-
for (let n = 0; result === null; n++) {
|
|
288
|
-
try {
|
|
289
|
-
result = await run(engineDeps, {
|
|
290
|
-
...options,
|
|
291
|
-
...(n === 0 && !o.resume ? { run: id } : { resume: id }),
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
catch (err) {
|
|
295
|
-
if (!isTransient(err))
|
|
296
|
-
throw err;
|
|
297
|
-
if (n >= RESUMES) {
|
|
298
|
-
console.error(`${id}: ${err.message}; stopped after ${RESUMES} resumes`);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
const ms = resumeWaitMs(n + 1, err.retryAfter);
|
|
302
|
-
console.error(`${id}: ${err.message}; resume ${n + 1} of ${RESUMES} in ${Math.round(ms / 1000)}s`);
|
|
303
|
-
await sleep(ms);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
const state = result?.state ?? fold(id, await ledger.trace(id));
|
|
307
|
-
if (result?.paused)
|
|
308
|
-
console.error(`${id}: paused after round ${state.edges.length}; birdwatch add steer ${id} "<their words>" --human, then birdwatch run --resume ${id} --pause`);
|
|
309
|
-
if (result?.parked)
|
|
310
|
-
console.error(`${id}: ${result.parked}; birdwatch run --resume ${id} goes one more round, birdwatch plan --run ${id} opens it by hand`);
|
|
311
|
-
const cost = costOf(state.spends);
|
|
312
|
-
// The last review's word: what the ask still lacks, or that it is answered.
|
|
313
|
-
const last = state.edges.at(-1);
|
|
314
|
-
if (last)
|
|
315
|
-
console.error(`${id}: ${last.next}: ${last.why}`);
|
|
316
|
-
console.error(`${id}: ${cost.line}`);
|
|
317
|
-
console.log(JSON.stringify({
|
|
318
|
-
run: id,
|
|
319
|
-
rounds: state.round,
|
|
320
|
-
cost: cost.total,
|
|
321
|
-
finished: result?.finished ?? false,
|
|
322
|
-
...(result?.paused ? { paused: true } : {}),
|
|
323
|
-
...(result?.parked ? { parked: result.parked } : {}),
|
|
324
|
-
note: result ? result.note : "stopped on a transient failure: --resume to go on",
|
|
325
|
-
...(last ? { edge: { next: last.next, why: last.why } } : {}),
|
|
326
|
-
hits: state.hits.length,
|
|
327
|
-
documents: state.documents.length,
|
|
328
|
-
findings: state.findings.filter((f) => state.called.get(f.id) !== "drop"),
|
|
329
|
-
spends: state.spends.map(({ source, op, about, ok, cost, ms, usd, tokens }) => ({
|
|
330
|
-
source,
|
|
331
|
-
op,
|
|
332
|
-
about,
|
|
333
|
-
ok,
|
|
334
|
-
cost,
|
|
335
|
-
ms,
|
|
336
|
-
...(usd !== undefined ? { usd } : {}),
|
|
337
|
-
...(tokens ? { tokens } : {}),
|
|
338
|
-
})),
|
|
339
|
-
problems: ledger.problems ?? [],
|
|
340
|
-
}, null, 2));
|
|
341
|
-
if (!result?.finished && !result?.paused && !result?.parked)
|
|
342
|
-
process.exitCode = 2;
|
|
343
|
-
});
|
|
344
206
|
agent(machine(common(program.command("plan [ask]"))))
|
|
345
207
|
.description("open a round: a new run from an ask, or the next round of --run after an edge that said again. Prints the run id")
|
|
346
208
|
.option("--run <id>", "the run whose next round to open")
|
|
@@ -605,12 +467,10 @@ export function addEngineCommands(program, deps = {}) {
|
|
|
605
467
|
.option("--sources <name...>", "the allow-list, in order of preference")
|
|
606
468
|
.option("--queries <n>", "queries per round", Number)
|
|
607
469
|
.option("--reads <n>", "reads per round", Number)
|
|
608
|
-
.option("--rounds <n>", "rounds
|
|
470
|
+
.option("--rounds <n>", "rounds a run should take: a guard rail", Number)
|
|
609
471
|
.option("--checks <n>", "findings checked per round", Number)
|
|
610
472
|
.option("--age <sentence>", "how much a source's age counts")
|
|
611
|
-
.option("--
|
|
612
|
-
.option("--triage <file>", "the triage prompt, from a file")
|
|
613
|
-
.option("--review <file>", "the review prompt, from a file")
|
|
473
|
+
.option("--rule <line...>", "the rules the agent heeds for this kind, one line each: replaces them all")
|
|
614
474
|
.option("--thin <n>", "new hits under which a tree's try is thin, so the next try runs", Number)
|
|
615
475
|
.option("--tree <file>", "the query tree's guide, as JSON {facets: [{name, why}], thin}")
|
|
616
476
|
.action(async (kind, o) => {
|
|
@@ -634,9 +494,7 @@ export function addEngineCommands(program, deps = {}) {
|
|
|
634
494
|
...(o.sources ? { sources: o.sources } : {}),
|
|
635
495
|
budget,
|
|
636
496
|
...(o.age !== undefined ? { age: o.age } : {}),
|
|
637
|
-
...(o.
|
|
638
|
-
...(o.triage ? { triage: await text(o.triage) } : {}),
|
|
639
|
-
...(o.review ? { review: await text(o.review) } : {}),
|
|
497
|
+
...(o.rule ? { rules: o.rule } : {}),
|
|
640
498
|
...(o.tree
|
|
641
499
|
? { tree: TreeGuide.parse(JSON.parse(await text(o.tree))) }
|
|
642
500
|
: o.thin !== undefined
|