@wrenautomation/read 0.0.0-stage → 0.29.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/LICENSE.md +61 -0
- package/README.md +36 -2
- package/dist/fetch/fetcher.d.ts +84 -0
- package/dist/fetch/fetcher.js +180 -0
- package/dist/fetch/fetcher.js.map +1 -0
- package/dist/fetch/index.d.ts +9 -0
- package/dist/fetch/index.js +10 -0
- package/dist/fetch/index.js.map +1 -0
- package/dist/fetch/ladder.d.ts +44 -0
- package/dist/fetch/ladder.js +18 -0
- package/dist/fetch/ladder.js.map +1 -0
- package/dist/fetch/robots.d.ts +14 -0
- package/dist/fetch/robots.js +92 -0
- package/dist/fetch/robots.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/study/claims.d.ts +19 -0
- package/dist/study/claims.js +13 -0
- package/dist/study/claims.js.map +1 -0
- package/dist/study/drafts.d.ts +43 -0
- package/dist/study/drafts.js +93 -0
- package/dist/study/drafts.js.map +1 -0
- package/dist/study/evidence.d.ts +17 -0
- package/dist/study/evidence.js +72 -0
- package/dist/study/evidence.js.map +1 -0
- package/dist/study/grounding.d.ts +29 -0
- package/dist/study/grounding.js +58 -0
- package/dist/study/grounding.js.map +1 -0
- package/dist/study/index.d.ts +11 -0
- package/dist/study/index.js +12 -0
- package/dist/study/index.js.map +1 -0
- package/dist/study/report.d.ts +55 -0
- package/dist/study/report.js +56 -0
- package/dist/study/report.js.map +1 -0
- package/dist/text/entities.d.ts +2 -0
- package/dist/text/entities.js +4 -0
- package/dist/text/entities.js.map +1 -0
- package/dist/text/htmltext.d.ts +30 -0
- package/dist/text/htmltext.js +208 -0
- package/dist/text/htmltext.js.map +1 -0
- package/dist/text/index.d.ts +7 -0
- package/dist/text/index.js +8 -0
- package/dist/text/index.js.map +1 -0
- package/dist/text/text.d.ts +8 -0
- package/dist/text/text.js +38 -0
- package/dist/text/text.js.map +1 -0
- package/dist/video/gemini.d.ts +46 -0
- package/dist/video/gemini.js +93 -0
- package/dist/video/gemini.js.map +1 -0
- package/dist/video/index.d.ts +39 -0
- package/dist/video/index.js +16 -0
- package/dist/video/index.js.map +1 -0
- package/dist/video/list.d.ts +35 -0
- package/dist/video/list.js +32 -0
- package/dist/video/list.js.map +1 -0
- package/dist/video/read.d.ts +53 -0
- package/dist/video/read.js +92 -0
- package/dist/video/read.js.map +1 -0
- package/dist/video/shots.d.ts +35 -0
- package/dist/video/shots.js +139 -0
- package/dist/video/shots.js.map +1 -0
- package/dist/video/tools.d.ts +16 -0
- package/dist/video/tools.js +29 -0
- package/dist/video/tools.js.map +1 -0
- package/dist/video/url.d.ts +73 -0
- package/dist/video/url.js +255 -0
- package/dist/video/url.js.map +1 -0
- package/dist/video/watch.d.ts +94 -0
- package/dist/video/watch.js +321 -0
- package/dist/video/watch.js.map +1 -0
- package/package.json +55 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/study/claims.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,MAAwC,EAAU,EAAE,CACzF,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A study's claims turned into something to use: offer candidates, cold
|
|
3
|
+
* emails, a brief. The model sees only the numbered claims, never the pages,
|
|
4
|
+
* so a draft is two steps from a quote: draft cites claim, claim quotes page.
|
|
5
|
+
* An item that cites nothing, cites a claim that isn't there, or has a number
|
|
6
|
+
* no cited claim (or the task) gave it, is dropped.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { type Claim } from "./claims.js";
|
|
10
|
+
/** One draft a study writes from its claims: its key in the report and what to ask for. */
|
|
11
|
+
export interface StudyDraftSpec {
|
|
12
|
+
key: string;
|
|
13
|
+
ask: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const DraftProposals: z.ZodObject<{
|
|
16
|
+
items: z.ZodArray<z.ZodObject<{
|
|
17
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
|
+
cites: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodCoercedNumber<unknown>>>>;
|
|
20
|
+
}, z.core.$strip>>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type DraftProposals = z.infer<typeof DraftProposals>;
|
|
23
|
+
/** A claim as drafts see it: its report number. */
|
|
24
|
+
export interface NumberedClaim extends Claim {
|
|
25
|
+
n: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function buildDraftPrompt(question: string, spec: StudyDraftSpec, claims: readonly NumberedClaim[]): string;
|
|
28
|
+
export interface DraftItem {
|
|
29
|
+
title: string;
|
|
30
|
+
body: string;
|
|
31
|
+
cites: number[];
|
|
32
|
+
}
|
|
33
|
+
type DraftRejection = "empty" | "no_cites" | "unknown_cite" | "number_not_cited";
|
|
34
|
+
export interface DroppedDraft {
|
|
35
|
+
title: string;
|
|
36
|
+
why: DraftRejection;
|
|
37
|
+
}
|
|
38
|
+
/** Every item through the gate: kept (citation marks stripped), or dropped with why. */
|
|
39
|
+
export declare function gateDrafts(proposals: DraftProposals, spec: StudyDraftSpec, claims: readonly NumberedClaim[]): {
|
|
40
|
+
kept: DraftItem[];
|
|
41
|
+
dropped: DroppedDraft[];
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A study's claims turned into something to use: offer candidates, cold
|
|
3
|
+
* emails, a brief. The model sees only the numbered claims, never the pages,
|
|
4
|
+
* so a draft is two steps from a quote: draft cites claim, claim quotes page.
|
|
5
|
+
* An item that cites nothing, cites a claim that isn't there, or has a number
|
|
6
|
+
* no cited claim (or the task) gave it, is dropped.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { fill } from "./claims.js";
|
|
10
|
+
import { numbers } from "./grounding.js";
|
|
11
|
+
const DRAFT_ITEMS_MAX = 8;
|
|
12
|
+
const DRAFT = `You turn research into something a small business can use. Use ONLY the numbered claims below: they are everything we know.
|
|
13
|
+
|
|
14
|
+
Return ONLY a JSON object, no prose, exactly this shape:
|
|
15
|
+
{"items": [{"title": "...", "body": "...", "cites": [1, 4]}]}
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
- cites: the numbers of the claims the item rests on. At least one.
|
|
19
|
+
- No citation marks like [1] or (claim 1) in the title or body: cites carries them.
|
|
20
|
+
- Every number in the title or body must appear in a claim it cites, or in the task.
|
|
21
|
+
- Never invent a statistic, a client, a result, a quote or a price. We have no clients yet: never say what clients or owners "tell us".
|
|
22
|
+
- Plain words a person says out loud. No dashes as punctuation.
|
|
23
|
+
|
|
24
|
+
TASK:
|
|
25
|
+
{ask}
|
|
26
|
+
|
|
27
|
+
Study: {question}
|
|
28
|
+
|
|
29
|
+
CLAIMS:
|
|
30
|
+
{claims}
|
|
31
|
+
`;
|
|
32
|
+
export const DraftProposals = z.object({
|
|
33
|
+
items: z.array(z.object({
|
|
34
|
+
title: z.string().nullable().optional(),
|
|
35
|
+
body: z.string().nullable().optional(),
|
|
36
|
+
cites: z.array(z.coerce.number()).nullable().optional(),
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
const hostOf = (url) => {
|
|
40
|
+
try {
|
|
41
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return url;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export function buildDraftPrompt(question, spec, claims) {
|
|
48
|
+
return fill(DRAFT, {
|
|
49
|
+
ask: spec.ask,
|
|
50
|
+
question,
|
|
51
|
+
claims: claims.map((c) => `[${c.n}] ${c.claim} (${hostOf(c.source_url)})`).join("\n"),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** "[3]", "[1, 4]", "(claim 2)", "(claims 1 and 5)": the model's citation marks, not text. */
|
|
55
|
+
const CITE_MARK = /\s*(?:\[\d+(?:\s*,\s*\d+)*\]|\((?:claims?|sources?)\s*\d+(?:\s*(?:,|and|&)\s*\d+)*\))/gi;
|
|
56
|
+
const stripCiteMarks = (s) => s
|
|
57
|
+
.replace(CITE_MARK, "")
|
|
58
|
+
.replace(/[ \t]+([.,;:!?])/g, "$1")
|
|
59
|
+
.trim();
|
|
60
|
+
/** Every item through the gate: kept (citation marks stripped), or dropped with why. */
|
|
61
|
+
export function gateDrafts(proposals, spec, claims) {
|
|
62
|
+
const byN = new Map(claims.map((c) => [c.n, c]));
|
|
63
|
+
const fromTask = new Set(numbers(spec.ask));
|
|
64
|
+
const kept = [];
|
|
65
|
+
const dropped = [];
|
|
66
|
+
for (const p of proposals.items.slice(0, DRAFT_ITEMS_MAX)) {
|
|
67
|
+
const title = stripCiteMarks(p.title ?? "");
|
|
68
|
+
const body = stripCiteMarks(p.body ?? "");
|
|
69
|
+
const cites = [...new Set(p.cites ?? [])];
|
|
70
|
+
const why = !body
|
|
71
|
+
? "empty"
|
|
72
|
+
: !cites.length
|
|
73
|
+
? "no_cites"
|
|
74
|
+
: cites.some((n) => !byN.has(n))
|
|
75
|
+
? "unknown_cite"
|
|
76
|
+
: null;
|
|
77
|
+
if (why) {
|
|
78
|
+
dropped.push({ title, why });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const cited = new Set(cites.flatMap((n) => {
|
|
82
|
+
const c = byN.get(n);
|
|
83
|
+
return c ? numbers(`${c.claim} ${c.quote}`) : [];
|
|
84
|
+
}));
|
|
85
|
+
if (numbers(`${title} ${body}`).some((n) => !cited.has(n) && !fromTask.has(n))) {
|
|
86
|
+
dropped.push({ title, why: "number_not_cited" });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
kept.push({ title, body, cites: cites.sort((a, b) => a - b) });
|
|
90
|
+
}
|
|
91
|
+
return { kept, dropped };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=drafts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drafts.js","sourceRoot":"","sources":["../../src/study/drafts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the model is shown of a page: the passages that match the question's terms, within a
|
|
3
|
+
* budget. Pure. Which pages to read is the agent's call.
|
|
4
|
+
*/
|
|
5
|
+
import { type EvidencePage } from "./grounding.js";
|
|
6
|
+
/** The words worth matching in a question: four letters or more, not filler. */
|
|
7
|
+
export declare function terms(...texts: string[]): Set<string>;
|
|
8
|
+
/**
|
|
9
|
+
* The parts of a page worth showing for these terms, in page order, within
|
|
10
|
+
* `budget` characters. A chunk scores a point per term it has, and one more
|
|
11
|
+
* when it has a number (a study wants figures). A short page is shown whole.
|
|
12
|
+
*/
|
|
13
|
+
export declare function passages(text: string, want: ReadonlySet<string>, budget: number): string;
|
|
14
|
+
/** How much of one page the model is shown: its best passages for every angle. */
|
|
15
|
+
export declare const PAGE_CHARS = 12000;
|
|
16
|
+
/** The part of one page its claims are drawn from. */
|
|
17
|
+
export declare const pageEvidence: (page: EvidencePage, want: ReadonlySet<string>) => EvidencePage;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the model is shown of a page: the passages that match the question's terms, within a
|
|
3
|
+
* budget. Pure. Which pages to read is the agent's call.
|
|
4
|
+
*/
|
|
5
|
+
import { foldText } from "./grounding.js";
|
|
6
|
+
const STOP = new Set("about after also been being between both could does doing each from have having here into more most much only other over same should some such than that their them then there these they this those through under very what when where which while with would your year years".split(" "));
|
|
7
|
+
/** The words worth matching in a question: four letters or more, not filler. */
|
|
8
|
+
export function terms(...texts) {
|
|
9
|
+
return new Set(texts
|
|
10
|
+
.flatMap((t) => foldText(t).split(/[^\p{L}\d]+/u))
|
|
11
|
+
.filter((w) => w.length >= 4 && !STOP.has(w)));
|
|
12
|
+
}
|
|
13
|
+
const CHUNK = 600;
|
|
14
|
+
/** Lines packed into chunks of about CHUNK characters, in page order. */
|
|
15
|
+
function chunks(text) {
|
|
16
|
+
const out = [];
|
|
17
|
+
let cur = "";
|
|
18
|
+
for (const line of text.split(/\n+/)) {
|
|
19
|
+
const l = line.trim();
|
|
20
|
+
if (!l)
|
|
21
|
+
continue;
|
|
22
|
+
if (cur && cur.length + l.length > CHUNK) {
|
|
23
|
+
out.push(cur);
|
|
24
|
+
cur = "";
|
|
25
|
+
}
|
|
26
|
+
cur = cur ? `${cur}\n${l}` : l;
|
|
27
|
+
}
|
|
28
|
+
if (cur)
|
|
29
|
+
out.push(cur);
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The parts of a page worth showing for these terms, in page order, within
|
|
34
|
+
* `budget` characters. A chunk scores a point per term it has, and one more
|
|
35
|
+
* when it has a number (a study wants figures). A short page is shown whole.
|
|
36
|
+
*/
|
|
37
|
+
export function passages(text, want, budget) {
|
|
38
|
+
if (text.length <= budget)
|
|
39
|
+
return text;
|
|
40
|
+
const parts = chunks(text).map((c, i) => {
|
|
41
|
+
const words = new Set(foldText(c).split(/[^\p{L}\d]+/u));
|
|
42
|
+
let score = /\d/.test(c) ? 1 : 0;
|
|
43
|
+
for (const t of want)
|
|
44
|
+
if (words.has(t))
|
|
45
|
+
score++;
|
|
46
|
+
return { i, c, score };
|
|
47
|
+
});
|
|
48
|
+
const picked = [];
|
|
49
|
+
let used = 0;
|
|
50
|
+
for (const p of [...parts].sort((a, b) => b.score - a.score || a.i - b.i)) {
|
|
51
|
+
if (p.score === 0)
|
|
52
|
+
break;
|
|
53
|
+
if (used + p.c.length > budget)
|
|
54
|
+
continue;
|
|
55
|
+
picked.push(p);
|
|
56
|
+
used += p.c.length + 2;
|
|
57
|
+
}
|
|
58
|
+
if (!picked.length)
|
|
59
|
+
return text.slice(0, budget);
|
|
60
|
+
return picked
|
|
61
|
+
.sort((a, b) => a.i - b.i)
|
|
62
|
+
.map((p) => p.c)
|
|
63
|
+
.join("\n\n");
|
|
64
|
+
}
|
|
65
|
+
/** How much of one page the model is shown: its best passages for every angle. */
|
|
66
|
+
export const PAGE_CHARS = 12_000;
|
|
67
|
+
/** The part of one page its claims are drawn from. */
|
|
68
|
+
export const pageEvidence = (page, want) => ({
|
|
69
|
+
url: page.url,
|
|
70
|
+
text: passages(page.text, want, PAGE_CHARS),
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/study/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cite or drop: a model's claim stands only on words we can find on a page we
|
|
3
|
+
* read. Openers and studies share these checks, so "grounded" means one thing.
|
|
4
|
+
*/
|
|
5
|
+
/** A page as the model was shown it: its URL and the text it saw (already cut). */
|
|
6
|
+
export interface EvidencePage {
|
|
7
|
+
url: string;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Case, whitespace, curly quotes/dashes and markdown folded, so a faithful copy
|
|
12
|
+
* matches: pages arrive as markdown ("**over 60%**", "[report](https://…)") and
|
|
13
|
+
* models quote the words without the marks.
|
|
14
|
+
*/
|
|
15
|
+
export declare function foldText(s: string): string;
|
|
16
|
+
/** The numbers in a text, thousands commas and trailing stops dropped: "1,200." → "1200". */
|
|
17
|
+
export declare const numbers: (s: string) => string[];
|
|
18
|
+
export declare const words: (s: string) => number;
|
|
19
|
+
/**
|
|
20
|
+
* The page a quote is on: the one the model named first, then any page shown
|
|
21
|
+
* (a wrong URL is fixed, not fatal). Null when no page has it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function pageQuoted(quote: string, pages: readonly EvidencePage[], namedUrl: string | null): EvidencePage | null;
|
|
24
|
+
/**
|
|
25
|
+
* Every number in `claim` is in `quote`. A year may instead come from the page
|
|
26
|
+
* the quote is on, its URL or text: "Level CFO's 2026 benchmarks" names the
|
|
27
|
+
* report, and the quote rarely repeats it.
|
|
28
|
+
*/
|
|
29
|
+
export declare const numbersQuoted: (claim: string, quote: string, page?: EvidencePage) => boolean;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cite or drop: a model's claim stands only on words we can find on a page we
|
|
3
|
+
* read. Openers and studies share these checks, so "grounded" means one thing.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Case, whitespace, curly quotes/dashes and markdown folded, so a faithful copy
|
|
7
|
+
* matches: pages arrive as markdown ("**over 60%**", "[report](https://…)") and
|
|
8
|
+
* models quote the words without the marks.
|
|
9
|
+
*/
|
|
10
|
+
export function foldText(s) {
|
|
11
|
+
return s
|
|
12
|
+
.normalize("NFKC")
|
|
13
|
+
.replace(/!?\[([^\]]*)\]\([^)\s]*\)/g, "$1")
|
|
14
|
+
.replace(/[*_`]+/g, "")
|
|
15
|
+
.replace(/[‘’ʼ]/g, "'")
|
|
16
|
+
.replace(/[“”]/g, '"')
|
|
17
|
+
.replace(/[‐-―]/g, "-")
|
|
18
|
+
.replace(/\s+/g, " ")
|
|
19
|
+
.trim()
|
|
20
|
+
.toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
/** The numbers in a text, thousands commas and trailing stops dropped: "1,200." → "1200". */
|
|
23
|
+
export const numbers = (s) => (s.match(/\d[\d,.]*/g) ?? []).map((n) => n.replace(/[,.]+$/, "").replace(/,/g, ""));
|
|
24
|
+
export const words = (s) => s.trim().split(/\s+/).filter(Boolean).length;
|
|
25
|
+
/**
|
|
26
|
+
* The folded quote is on the folded page word for word, or each of its sentences
|
|
27
|
+
* is: the model often joins two sentences from one page into one quote.
|
|
28
|
+
*/
|
|
29
|
+
function quotedFrom(quote, page) {
|
|
30
|
+
if (page.includes(quote))
|
|
31
|
+
return true;
|
|
32
|
+
const sentences = quote
|
|
33
|
+
.split(/(?<=[.;·])\s+/)
|
|
34
|
+
.map((f) => f.replace(/[\s.;·]+$/, ""))
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
return sentences.length > 1 && sentences.every((f) => page.includes(f));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The page a quote is on: the one the model named first, then any page shown
|
|
40
|
+
* (a wrong URL is fixed, not fatal). Null when no page has it.
|
|
41
|
+
*/
|
|
42
|
+
export function pageQuoted(quote, pages, namedUrl) {
|
|
43
|
+
const folded = foldText(quote);
|
|
44
|
+
const named = pages.find((p) => p.url === namedUrl);
|
|
45
|
+
return ([...(named ? [named] : []), ...pages].find((p) => quotedFrom(folded, foldText(p.text))) ?? null);
|
|
46
|
+
}
|
|
47
|
+
const YEAR = /^(19|20)\d\d$/;
|
|
48
|
+
/**
|
|
49
|
+
* Every number in `claim` is in `quote`. A year may instead come from the page
|
|
50
|
+
* the quote is on, its URL or text: "Level CFO's 2026 benchmarks" names the
|
|
51
|
+
* report, and the quote rarely repeats it.
|
|
52
|
+
*/
|
|
53
|
+
export const numbersQuoted = (claim, quote, page) => {
|
|
54
|
+
const inQuote = new Set(numbers(quote));
|
|
55
|
+
const onPage = new Set(page ? numbers(`${page.url} ${page.text}`) : []);
|
|
56
|
+
return numbers(claim).every((n) => inQuote.has(n) || (YEAR.test(n) && onPage.has(n)));
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=grounding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding.js","sourceRoot":"","sources":["../../src/study/grounding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A study's synthesis reader: the quote gate (`grounding`), the passages a page is shown by
|
|
3
|
+
* (`evidence`), drafts that cite claims, and the report. Pure: the caller searches, stores and
|
|
4
|
+
* calls the model. The planner (queries per angle, which pages to read, the claim gate per angle)
|
|
5
|
+
* went in 0.5.0: birdwatch plans and runs; its `claimExtractor` is the one gate.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./claims.js";
|
|
8
|
+
export * from "./drafts.js";
|
|
9
|
+
export * from "./evidence.js";
|
|
10
|
+
export * from "./grounding.js";
|
|
11
|
+
export * from "./report.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A study's synthesis reader: the quote gate (`grounding`), the passages a page is shown by
|
|
3
|
+
* (`evidence`), drafts that cite claims, and the report. Pure: the caller searches, stores and
|
|
4
|
+
* calls the model. The planner (queries per angle, which pages to read, the claim gate per angle)
|
|
5
|
+
* went in 0.5.0: birdwatch plans and runs; its `claimExtractor` is the one gate.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./claims.js";
|
|
8
|
+
export * from "./drafts.js";
|
|
9
|
+
export * from "./evidence.js";
|
|
10
|
+
export * from "./grounding.js";
|
|
11
|
+
export * from "./report.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/study/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A study as markdown: each angle's claims with [n] marks and the reader's calls, the
|
|
3
|
+
* drafts that cite them, then every source once. Pure: the caller reads, this writes.
|
|
4
|
+
*/
|
|
5
|
+
import type { DraftItem, DroppedDraft, NumberedClaim, StudyDraftSpec } from "./drafts.js";
|
|
6
|
+
/** A claim as the report sees it: numbered, with the reader's call on it. */
|
|
7
|
+
export interface ReportClaim extends NumberedClaim {
|
|
8
|
+
/** keep, tune or drop; null when nobody has called it. */
|
|
9
|
+
call: string | null;
|
|
10
|
+
comment: string | null;
|
|
11
|
+
}
|
|
12
|
+
export interface ReportAngle {
|
|
13
|
+
angle: string;
|
|
14
|
+
claims: readonly ReportClaim[];
|
|
15
|
+
}
|
|
16
|
+
export interface ReportDraft {
|
|
17
|
+
spec: StudyDraftSpec;
|
|
18
|
+
done: boolean;
|
|
19
|
+
/** Calls or new claims changed its claims since it was written. */
|
|
20
|
+
stale: boolean;
|
|
21
|
+
kept: readonly DraftItem[];
|
|
22
|
+
dropped: readonly DroppedDraft[];
|
|
23
|
+
}
|
|
24
|
+
/** A page someone added by hand, not found by a search. */
|
|
25
|
+
export interface ReportManualPage {
|
|
26
|
+
by: string;
|
|
27
|
+
at: string;
|
|
28
|
+
/** YYYY-MM-DD, when given. */
|
|
29
|
+
published: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** What the report needs of a study; a richer view satisfies it. */
|
|
32
|
+
export interface StudyReportView {
|
|
33
|
+
study: {
|
|
34
|
+
question: string;
|
|
35
|
+
slug: string;
|
|
36
|
+
niche?: string | null;
|
|
37
|
+
};
|
|
38
|
+
angles: readonly ReportAngle[];
|
|
39
|
+
drafts: readonly ReportDraft[];
|
|
40
|
+
/** Page title by source url. */
|
|
41
|
+
titles: ReadonlyMap<string, string>;
|
|
42
|
+
manual?: ReadonlyMap<string, ReportManualPage> | null;
|
|
43
|
+
counts: {
|
|
44
|
+
queries: number;
|
|
45
|
+
pages: number;
|
|
46
|
+
unread: number;
|
|
47
|
+
gated: number;
|
|
48
|
+
waiting: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface ReportOptions {
|
|
52
|
+
/** The command that runs the open parts, shown when some are still to run. */
|
|
53
|
+
rerun?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function studyReport(view: StudyReportView, opts?: ReportOptions): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
2
|
+
export function studyReport(view, opts = {}) {
|
|
3
|
+
const { study, angles, drafts, titles, manual, counts } = view;
|
|
4
|
+
const rerun = opts.rerun ? `: \`${opts.rerun}\`` : "";
|
|
5
|
+
const claims = angles.flatMap((a) => a.claims);
|
|
6
|
+
const dropped = claims.filter((c) => c.call === "drop").length;
|
|
7
|
+
const open = counts.waiting + drafts.filter((d) => !d.done || d.stale).length;
|
|
8
|
+
const out = [
|
|
9
|
+
`# ${study.question}`,
|
|
10
|
+
"",
|
|
11
|
+
`Study \`${study.slug}\`${study.niche ? ` for ${study.niche}` : ""}. ${plural(counts.queries, "search", "searches")}, ${plural(counts.pages, "page")} read (${counts.unread} unreadable), ${plural(claims.length, "claim")} passed the quote check (${counts.gated} did not), ${dropped} dropped by you.${open ? ` ${plural(open, "part")} still to run${rerun}.` : ""}`,
|
|
12
|
+
];
|
|
13
|
+
for (const a of angles) {
|
|
14
|
+
out.push("", `## ${a.angle}`, "");
|
|
15
|
+
if (!a.claims.length)
|
|
16
|
+
out.push(counts.waiting || !counts.pages
|
|
17
|
+
? "_Not run yet._"
|
|
18
|
+
: "_Nothing on the pages read answered this._");
|
|
19
|
+
for (const c of a.claims) {
|
|
20
|
+
const call = c.call ? ` (${c.call}${c.comment ? `: ${c.comment}` : ""})` : "";
|
|
21
|
+
const line = `${c.claim} [${c.n}]${call}`;
|
|
22
|
+
out.push(c.call === "drop" ? `- ~~${line}~~` : `- ${line}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const d of drafts) {
|
|
26
|
+
out.push("", `## Draft: ${d.spec.key}`, "");
|
|
27
|
+
if (!d.done)
|
|
28
|
+
out.push("_Not run yet._");
|
|
29
|
+
else if (d.stale)
|
|
30
|
+
out.push(`_Your calls or new claims changed its claims${rerun}._`, "");
|
|
31
|
+
if (d.done && !d.kept.length)
|
|
32
|
+
out.push("_Nothing survived the citation check._");
|
|
33
|
+
for (const item of d.kept) {
|
|
34
|
+
out.push(`### ${item.title || "(untitled)"}`, "", item.body, "", `Cites ${item.cites.map((n) => `[${n}]`).join(" ")}`, "");
|
|
35
|
+
}
|
|
36
|
+
if (d.dropped.length) {
|
|
37
|
+
out.push(`_Dropped: ${d.dropped.map((x) => `${x.title || "(untitled)"} (${x.why.replace(/_/g, " ")})`).join("; ")}._`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (claims.length) {
|
|
41
|
+
out.push("", "## Sources", "");
|
|
42
|
+
for (const c of claims) {
|
|
43
|
+
const title = titles.get(c.source_url);
|
|
44
|
+
const m = manual?.get(c.source_url);
|
|
45
|
+
const held = m
|
|
46
|
+
? ` (added by hand by ${m.by} on ${m.at.slice(0, 10)}${m.published ? `, published ${m.published}` : ""})`
|
|
47
|
+
: "";
|
|
48
|
+
out.push(`${c.n}. ${title ? `${title}, ` : ""}<${c.source_url}>${held}: "${c.quote}"`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return `${out
|
|
52
|
+
.join("\n")
|
|
53
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
54
|
+
.trim()}\n`;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/study/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/text/entities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAS,EAAU,EAAE,CAC9C,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny HTML reading: visible text, links, title, mailto/tel hrefs in one pass.
|
|
3
|
+
* Deliberately not a DOM: enrichment needs the words and the links, not the tree.
|
|
4
|
+
* htmlparser2's streaming parser feeds the Extractor; it is tolerant of the malformed
|
|
5
|
+
* markup small-business sites serve and closes what HTML implies closed. Never throws:
|
|
6
|
+
* a pathological page degrades to whatever was read so far, never sinks a crawl.
|
|
7
|
+
*/
|
|
8
|
+
interface PageLink {
|
|
9
|
+
url: string;
|
|
10
|
+
anchor: string;
|
|
11
|
+
}
|
|
12
|
+
interface PageContent {
|
|
13
|
+
title: string;
|
|
14
|
+
text: string;
|
|
15
|
+
/** Absolute url + anchor text; mailto/tel/javascript/fragment hrefs excluded. */
|
|
16
|
+
links: PageLink[];
|
|
17
|
+
/** Addresses declared in mailto: hrefs, decoded, query-stripped, order kept, duplicates kept. */
|
|
18
|
+
mailtos: string[];
|
|
19
|
+
tels: string[];
|
|
20
|
+
}
|
|
21
|
+
/** HTML character references to text (the WHATWG table). */
|
|
22
|
+
export declare const decodeEntities: (s: string) => string;
|
|
23
|
+
export declare function readPage(html: string, baseUrl?: string): PageContent;
|
|
24
|
+
/**
|
|
25
|
+
* Is this page a JS mount point whose words live client-side? Thin text alone must
|
|
26
|
+
* NOT fire (a tiny server-rendered contact page is real content): a shell is thin
|
|
27
|
+
* text plus substantial, script-dominated markup or a framework mount marker.
|
|
28
|
+
*/
|
|
29
|
+
export declare function looksLikeJsShell(html: string, text: string): boolean;
|
|
30
|
+
export {};
|