@wrenautomation/birdwatch 0.0.0-stage → 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.
Files changed (141) hide show
  1. package/LICENSE.md +61 -0
  2. package/README.md +320 -2
  3. package/dist/app/cli.d.ts +4 -0
  4. package/dist/app/cli.js +149 -0
  5. package/dist/app/cli.js.map +1 -0
  6. package/dist/app/engine-cli.d.ts +44 -0
  7. package/dist/app/engine-cli.js +596 -0
  8. package/dist/app/engine-cli.js.map +1 -0
  9. package/dist/app/index.d.ts +6 -0
  10. package/dist/app/index.js +7 -0
  11. package/dist/app/index.js.map +1 -0
  12. package/dist/app/serve.d.ts +14 -0
  13. package/dist/app/serve.js +102 -0
  14. package/dist/app/serve.js.map +1 -0
  15. package/dist/document/body.d.ts +6 -0
  16. package/dist/document/body.js +18 -0
  17. package/dist/document/body.js.map +1 -0
  18. package/dist/document/check.d.ts +35 -0
  19. package/dist/document/check.js +68 -0
  20. package/dist/document/check.js.map +1 -0
  21. package/dist/document/extract.d.ts +81 -0
  22. package/dist/document/extract.js +226 -0
  23. package/dist/document/extract.js.map +1 -0
  24. package/dist/document/index.d.ts +11 -0
  25. package/dist/document/index.js +12 -0
  26. package/dist/document/index.js.map +1 -0
  27. package/dist/document/model.d.ts +10 -0
  28. package/dist/document/model.js +2 -0
  29. package/dist/document/model.js.map +1 -0
  30. package/dist/document/readers.d.ts +24 -0
  31. package/dist/document/readers.js +87 -0
  32. package/dist/document/readers.js.map +1 -0
  33. package/dist/document/types.d.ts +99 -0
  34. package/dist/document/types.js +93 -0
  35. package/dist/document/types.js.map +1 -0
  36. package/dist/engine/agent.d.ts +475 -0
  37. package/dist/engine/agent.js +990 -0
  38. package/dist/engine/agent.js.map +1 -0
  39. package/dist/engine/canonical.d.ts +4 -0
  40. package/dist/engine/canonical.js +55 -0
  41. package/dist/engine/canonical.js.map +1 -0
  42. package/dist/engine/context.d.ts +9 -0
  43. package/dist/engine/context.js +141 -0
  44. package/dist/engine/context.js.map +1 -0
  45. package/dist/engine/cost.d.ts +13 -0
  46. package/dist/engine/cost.js +31 -0
  47. package/dist/engine/cost.js.map +1 -0
  48. package/dist/engine/index.d.ts +16 -0
  49. package/dist/engine/index.js +17 -0
  50. package/dist/engine/index.js.map +1 -0
  51. package/dist/engine/judge.d.ts +158 -0
  52. package/dist/engine/judge.js +323 -0
  53. package/dist/engine/judge.js.map +1 -0
  54. package/dist/engine/people.d.ts +34 -0
  55. package/dist/engine/people.js +125 -0
  56. package/dist/engine/people.js.map +1 -0
  57. package/dist/engine/plan.d.ts +6 -0
  58. package/dist/engine/plan.js +7 -0
  59. package/dist/engine/plan.js.map +1 -0
  60. package/dist/engine/plans/index.d.ts +19 -0
  61. package/dist/engine/plans/index.js +104 -0
  62. package/dist/engine/plans/index.js.map +1 -0
  63. package/dist/engine/ports.d.ts +76 -0
  64. package/dist/engine/ports.js +28 -0
  65. package/dist/engine/ports.js.map +1 -0
  66. package/dist/engine/recall.d.ts +42 -0
  67. package/dist/engine/recall.js +71 -0
  68. package/dist/engine/recall.js.map +1 -0
  69. package/dist/engine/state.d.ts +96 -0
  70. package/dist/engine/state.js +162 -0
  71. package/dist/engine/state.js.map +1 -0
  72. package/dist/engine/steps.d.ts +98 -0
  73. package/dist/engine/steps.js +435 -0
  74. package/dist/engine/steps.js.map +1 -0
  75. package/dist/engine/tree.d.ts +76 -0
  76. package/dist/engine/tree.js +148 -0
  77. package/dist/engine/tree.js.map +1 -0
  78. package/dist/engine/types.d.ts +250 -0
  79. package/dist/engine/types.js +168 -0
  80. package/dist/engine/types.js.map +1 -0
  81. package/dist/engine/view.d.ts +138 -0
  82. package/dist/engine/view.js +188 -0
  83. package/dist/engine/view.js.map +1 -0
  84. package/dist/index.d.ts +15 -0
  85. package/dist/index.js +16 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/ledger/files.d.ts +39 -0
  88. package/dist/ledger/files.js +113 -0
  89. package/dist/ledger/files.js.map +1 -0
  90. package/dist/ledger/ids.d.ts +4 -0
  91. package/dist/ledger/ids.js +9 -0
  92. package/dist/ledger/ids.js.map +1 -0
  93. package/dist/ledger/index.d.ts +11 -0
  94. package/dist/ledger/index.js +12 -0
  95. package/dist/ledger/index.js.map +1 -0
  96. package/dist/ledger/ledger.d.ts +46 -0
  97. package/dist/ledger/ledger.js +105 -0
  98. package/dist/ledger/ledger.js.map +1 -0
  99. package/dist/ledger/memory.d.ts +19 -0
  100. package/dist/ledger/memory.js +19 -0
  101. package/dist/ledger/memory.js.map +1 -0
  102. package/dist/ledger/records.d.ts +1424 -0
  103. package/dist/ledger/records.js +292 -0
  104. package/dist/ledger/records.js.map +1 -0
  105. package/dist/sources/arxiv.d.ts +105 -0
  106. package/dist/sources/arxiv.js +321 -0
  107. package/dist/sources/arxiv.js.map +1 -0
  108. package/dist/sources/exa.d.ts +19 -0
  109. package/dist/sources/exa.js +59 -0
  110. package/dist/sources/exa.js.map +1 -0
  111. package/dist/sources/fetcher.d.ts +35 -0
  112. package/dist/sources/fetcher.js +67 -0
  113. package/dist/sources/fetcher.js.map +1 -0
  114. package/dist/sources/google.d.ts +16 -0
  115. package/dist/sources/google.js +64 -0
  116. package/dist/sources/google.js.map +1 -0
  117. package/dist/sources/index.d.ts +17 -0
  118. package/dist/sources/index.js +18 -0
  119. package/dist/sources/index.js.map +1 -0
  120. package/dist/sources/junk.d.ts +10 -0
  121. package/dist/sources/junk.js +16 -0
  122. package/dist/sources/junk.js.map +1 -0
  123. package/dist/sources/ladder.d.ts +47 -0
  124. package/dist/sources/ladder.js +158 -0
  125. package/dist/sources/ladder.js.map +1 -0
  126. package/dist/sources/ledger.d.ts +7 -0
  127. package/dist/sources/ledger.js +35 -0
  128. package/dist/sources/ledger.js.map +1 -0
  129. package/dist/sources/paste.d.ts +8 -0
  130. package/dist/sources/paste.js +22 -0
  131. package/dist/sources/paste.js.map +1 -0
  132. package/dist/sources/searxng.d.ts +11 -0
  133. package/dist/sources/searxng.js +38 -0
  134. package/dist/sources/searxng.js.map +1 -0
  135. package/dist/sources/sites.d.ts +53 -0
  136. package/dist/sources/sites.js +285 -0
  137. package/dist/sources/sites.js.map +1 -0
  138. package/dist/sources/ytdlp.d.ts +43 -0
  139. package/dist/sources/ytdlp.js +117 -0
  140. package/dist/sources/ytdlp.js.map +1 -0
  141. package/package.json +61 -3
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The default plan file per ask kind. A copy of one, tuned and stored beside a ledger, is how
3
+ * a run learns: the recipe changes, the agent reads it. `birdwatch plans <kind>` prints one to start from.
4
+ */
5
+ import { PlanFile as PlanFileShape, } from "../types.js";
6
+ /**
7
+ * The five sides a researcher covers before the reads start. One tree per round; the agent
8
+ * fills each facet with tries, in order of what it expects to work.
9
+ */
10
+ export const TREE = {
11
+ facets: [
12
+ { name: "number", why: "a figure: a rate, a price, a count, a date" },
13
+ { name: "how", why: "how it is done: a method, a step list, a tool" },
14
+ { name: "who", why: "who does it or buys it, in their own words" },
15
+ { name: "counter", why: "the case against: what fails, who says no" },
16
+ {
17
+ name: "primary",
18
+ why: "the source itself: a study, a filing, a transcript, the maker's own page",
19
+ },
20
+ ],
21
+ thin: 3,
22
+ };
23
+ const web = {
24
+ id: "birdwatch/web",
25
+ kind: "web",
26
+ version: 6,
27
+ sources: ["searxng", "exa", "google"],
28
+ budget: { queries: 3, reads: 6, rounds: 3, checks: 3 },
29
+ tree: TREE,
30
+ age: "Prices, tools and numbers that move: prefer the last two years. A principle or a method can be old.",
31
+ rules: [
32
+ "Each facet's tries reach different kinds of source: a survey or benchmark with numbers, a trade publication or association, people in the field (forums, Reddit, reviews), a government statistic.",
33
+ "Put a year in a query only when fresh numbers matter.",
34
+ "Keep primary sources and people in the field over listicles and aggregators.",
35
+ ],
36
+ };
37
+ const video = {
38
+ id: "birdwatch/video",
39
+ kind: "video",
40
+ version: 6,
41
+ sources: ["youtube", "podcasts"],
42
+ budget: { queries: 3, reads: 4, rounds: 2, checks: 3 },
43
+ tree: TREE,
44
+ age: "Tools and platforms: prefer the last two years. A talk on a principle can be old.",
45
+ rules: [
46
+ "Each facet's tries reach different kinds of speaker: a practitioner who shows the work, a conference talk, an interview or podcast, a critic or reviewer.",
47
+ "Drop a clip under 60 seconds unless the ask is about short-form video. Videos of 5 to 60 minutes hold the most.",
48
+ "A channel that yielded nothing before is a reason to drop.",
49
+ ],
50
+ };
51
+ const channel = {
52
+ id: "birdwatch/channel",
53
+ kind: "channel",
54
+ version: 6,
55
+ sources: ["searxng", "youtube", "exa"],
56
+ budget: { queries: 3, reads: 6, rounds: 2, checks: 3 },
57
+ tree: TREE,
58
+ age: "A channel that has not posted in the last year counts for little.",
59
+ rules: [
60
+ "Each facet's tries reach different kinds of channel: a creator on video, a writer or newsletter, a podcast, a community.",
61
+ "Keep the channel's own page over a page about it.",
62
+ ],
63
+ };
64
+ const person = {
65
+ id: "birdwatch/person",
66
+ kind: "person",
67
+ version: 6,
68
+ sources: ["searxng", "exa", "linkedin", "google"],
69
+ budget: { queries: 4, reads: 6, rounds: 2, checks: 3 },
70
+ tree: TREE,
71
+ age: "What they do now: prefer the latest pages. Their history can be old.",
72
+ rules: [
73
+ "Name the person or company in each query, as the ask writes it.",
74
+ "Each facet's tries reach different kinds of page: their own site or profile, an interview or talk, press or reviews, a filing or directory.",
75
+ "Drop a page about a different person or company with the same name.",
76
+ "Keep what they wrote or said over what a directory copied.",
77
+ ],
78
+ };
79
+ const site = {
80
+ id: "birdwatch/site",
81
+ kind: "site",
82
+ version: 6,
83
+ sources: ["sitemap", "searxng", "fetch"],
84
+ budget: { queries: 4, reads: 8, rounds: 2, checks: 3 },
85
+ tree: TREE,
86
+ age: "When two pages of the site disagree, the later one counts.",
87
+ rules: [
88
+ "Each facet's tries reach different parts of the site: its product or service pages, its pricing, its blog or help, its people.",
89
+ "Drop a URL off the site.",
90
+ ],
91
+ };
92
+ const DEFAULTS = { web, video, channel, person, site };
93
+ /** The default plan file for a kind, validated, as a fresh copy. */
94
+ export function defaultPlanFile(kind) {
95
+ return PlanFileShape.parse(structuredClone(DEFAULTS[kind]));
96
+ }
97
+ /** The plan file's word on age; a stored file from before version 4 takes its kind's default. Version 5: the review no longer says "at most". Version 6: `tree`, the query tree's guide. 0.30.0 dropped the prompts with no bump: the agent never read them. */
98
+ export const ageOf = (planFile) => planFile.age ?? DEFAULTS[planFile.kind].age;
99
+ /** The plan file's rules for the agent; a stored file from before 0.30.0 takes its kind's default. */
100
+ export const rulesOf = (planFile) => planFile.rules ?? DEFAULTS[planFile.kind].rules ?? [];
101
+ /** The plan file's guide for a query tree; a stored file from before version 6 takes its kind's default. */
102
+ export const treeOf = (planFile) => planFile.tree ?? DEFAULTS[planFile.kind].tree;
103
+ export const PLAN_KINDS = Object.keys(DEFAULTS);
104
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/plans/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAGL,QAAQ,IAAI,aAAa,GAE1B,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAc;IAC7B,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,4CAA4C,EAAE;QACrE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,+CAA+C,EAAE;QACrE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,4CAA4C,EAAE;QAClE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,2CAA2C,EAAE;QACrE;YACE,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,0EAA0E;SAChF;KACF;IACD,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,GAAG,GAAa;IACpB,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC;IACrC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,qGAAqG;IAC1G,KAAK,EAAE;QACL,oMAAoM;QACpM,uDAAuD;QACvD,8EAA8E;KAC/E;CACF,CAAC;AAEF,MAAM,KAAK,GAAa;IACtB,EAAE,EAAE,iBAAiB;IACrB,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAChC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,mFAAmF;IACxF,KAAK,EAAE;QACL,2JAA2J;QAC3J,iHAAiH;QACjH,4DAA4D;KAC7D;CACF,CAAC;AAEF,MAAM,OAAO,GAAa;IACxB,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC;IACtC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,mEAAmE;IACxE,KAAK,EAAE;QACL,0HAA0H;QAC1H,mDAAmD;KACpD;CACF,CAAC;AAEF,MAAM,MAAM,GAAa;IACvB,EAAE,EAAE,kBAAkB;IACtB,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC;IACjD,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,sEAAsE;IAC3E,KAAK,EAAE;QACL,iEAAiE;QACjE,6IAA6I;QAC7I,qEAAqE;QACrE,4DAA4D;KAC7D;CACF,CAAC;AAEF,MAAM,IAAI,GAAa;IACrB,EAAE,EAAE,gBAAgB;IACpB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC;IACxC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,4DAA4D;IACjE,KAAK,EAAE;QACL,gIAAgI;QAChI,0BAA0B;KAC3B;CACF,CAAC;AAEF,MAAM,QAAQ,GAA8B,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAElF,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,IAAa;IAC3C,OAAO,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,gQAAgQ;AAChQ,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,QAAkB,EAAsB,EAAE,CAC9D,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;AAE9C,sGAAsG;AACtG,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,QAAkB,EAAY,EAAE,CACtD,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;AAExD,4GAA4G;AAC5G,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAkB,EAAa,EAAE,CACtD,QAAQ,CAAC,IAAI,IAAK,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAkB,CAAC;AAE/D,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAc,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The ports each agent command is given, as one plain `deps` object. A source is one file that
3
+ * answers `search` (hits for a query) or `open` (a hit read whole) or both; the registry holds
4
+ * them by name and the plan's allow-list picks per run. The model is the bulk extract and check's, the extractor
5
+ * `./document`'s, the ledger `./ledger`'s. Nothing here reaches the network: the adapters in `src/sources` do.
6
+ */
7
+ import type { Model } from "@wrenautomation/models";
8
+ import type { Judge } from "@wrenautomation/models/judge";
9
+ import type { Document, Extractor } from "../document/index.js";
10
+ import type { Ids } from "../ledger/ids.js";
11
+ import type { Ledger } from "../ledger/ledger.js";
12
+ import type { CostKind, FollowAs, From, HitRecord, QueryRecord } from "../ledger/records.js";
13
+ import type { HitViewIn } from "./types.js";
14
+ export interface Source {
15
+ name: string;
16
+ kind: "search" | "open" | "both";
17
+ cost: CostKind;
18
+ /** Who the records it writes are from. A person pasting URLs is `human`; the default is `tool`. */
19
+ from?: From;
20
+ /** Who its records are by: the person behind a paste. The default is the source's name. */
21
+ by?: string;
22
+ /**
23
+ * Whether its search keeps to that site (a query's `site`). A site-scoped query skips a
24
+ * searcher that would answer from everywhere. Absent: it keeps to any site.
25
+ */
26
+ keeps?(site: string): boolean;
27
+ /** Hits as the source shows them for free. Throw on failure: the engine writes the spend. */
28
+ search?(query: QueryRecord): Promise<HitViewIn[]>;
29
+ /** Whether it lists that page's own posts (`posts`) or pages like it (`similar`). */
30
+ follows?(as: FollowAs, url: string): boolean;
31
+ /** The follow's hits, newest or closest first, `n` at most. Throw on failure, like search. */
32
+ follow?(as: FollowAs, url: string, n: number): Promise<HitViewIn[]>;
33
+ /**
34
+ * Whether this source opens that URL at all (a YouTube page, an http(s) page). The read step
35
+ * asks before a call, so a source that was never going to open it writes no spend. Absent: it
36
+ * opens everything.
37
+ */
38
+ opens?(url: string): boolean;
39
+ /**
40
+ * Opens only its own site's URLs, better than any general opener (a thread as data, not a
41
+ * fetched wall): it gets its turn before the rest, whatever the allow-list's order.
42
+ */
43
+ first?: true;
44
+ /**
45
+ * The hit read whole. Throw with the reason when it was fetched and nothing usable came back
46
+ * (a 403, a block, no reader for the media): the engine writes the spend `ok: false` with the
47
+ * reason and the next opener gets its turn.
48
+ */
49
+ open?(hit: HitRecord): Promise<Document>;
50
+ }
51
+ export type Sources = Map<string, Source>;
52
+ export declare const registry: (...sources: Source[]) => Sources;
53
+ /** The allow-list's sources, in its order. A name no source answers to fails the run at its start. */
54
+ export declare function pick(sources: Sources, names: readonly string[]): Source[];
55
+ export interface Deps {
56
+ sources: Sources;
57
+ ledger: Ledger;
58
+ /** The bulk extract and check's model; null: none, and those two commands fail. */
59
+ model: Model | null;
60
+ extractor: Extractor;
61
+ /** Cheap typed answers on every hit, page and finding: a hint and a router. Absent or null: none. */
62
+ judge?: Judge | null;
63
+ clock: () => Date;
64
+ ids: Ids;
65
+ /** `by` on the records the engine itself writes: this terminal's writer. */
66
+ writer: string;
67
+ /** The pause between tries of a call that failed with a Transient; `setTimeout` by default. A test passes a stub. */
68
+ sleep?: (ms: number) => Promise<void>;
69
+ }
70
+ /**
71
+ * The searchers a query goes to: all of them, or for a site-scoped query those that keep to it.
72
+ * A follow goes to the sources that follow its URL that way.
73
+ */
74
+ export declare const searchersFor: (sources: readonly Source[], q: Pick<QueryRecord, "site" | "follow">) => Source[];
75
+ /** How many hits a follow lists. */
76
+ export declare const FOLLOW_N = 30;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The ports each agent command is given, as one plain `deps` object. A source is one file that
3
+ * answers `search` (hits for a query) or `open` (a hit read whole) or both; the registry holds
4
+ * them by name and the plan's allow-list picks per run. The model is the bulk extract and check's, the extractor
5
+ * `./document`'s, the ledger `./ledger`'s. Nothing here reaches the network: the adapters in `src/sources` do.
6
+ */
7
+ export const registry = (...sources) => new Map(sources.map((s) => [s.name, s]));
8
+ /** The allow-list's sources, in its order. A name no source answers to fails the run at its start. */
9
+ export function pick(sources, names) {
10
+ const missing = names.filter((n) => !sources.has(n));
11
+ if (missing.length)
12
+ throw new Error(`no source named ${missing.join(", ")}`);
13
+ return names.map((n) => sources.get(n));
14
+ }
15
+ /**
16
+ * The searchers a query goes to: all of them, or for a site-scoped query those that keep to it.
17
+ * A follow goes to the sources that follow its URL that way.
18
+ */
19
+ export const searchersFor = (sources, q) => {
20
+ const f = q.follow;
21
+ if (f)
22
+ return sources.filter((s) => s.follow && s.follows?.(f.as, f.url));
23
+ const searchers = sources.filter((s) => s.search);
24
+ return q.site ? searchers.filter((s) => s.keeps?.(q.site) ?? true) : searchers;
25
+ };
26
+ /** How many hits a follow lists. */
27
+ export const FOLLOW_N = 30;
28
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/engine/ports.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkDH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAiB,EAAW,EAAE,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpG,sGAAsG;AACtG,MAAM,UAAU,IAAI,CAAC,OAAgB,EAAE,KAAwB;IAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAW,CAAC,CAAC;AACpD,CAAC;AAkBD;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,OAA0B,EAC1B,CAAuC,EAC7B,EAAE;IACZ,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAClD,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAc,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3F,CAAC,CAAC;AAEF,oCAAoC;AACpC,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * What earlier runs proved, found again: the findings of every run in the ledger, ranked by
3
+ * MiniSearch (BM25 over the finding's text and quote, prefix and a little fuzz). Each carries its
4
+ * quote, its source's date and its run, so a later run sees how old a fact is and where it came
5
+ * from. A finding a call or a failed check dropped is left out. Read only: the agent decides what
6
+ * still holds, puts it in `known` or reads the source again (WA-934, option A).
7
+ */
8
+ import { type Ledger } from "../ledger/ledger.js";
9
+ import type { AskKind } from "./plan.js";
10
+ export interface Recalled {
11
+ finding: string;
12
+ run: string;
13
+ /** The ask of the run that found it. */
14
+ ask: string;
15
+ text: string;
16
+ quote: string;
17
+ url: string;
18
+ /** The source's own date, when its document had one. */
19
+ date: string | null;
20
+ /** When the run found it. */
21
+ found: string;
22
+ /** Who wrote it: the agent's id for a finding it read and added, a model's id for `extract`. */
23
+ by: string;
24
+ /** `kept`: a call kept it; `open`: no call on it yet. Ranking ignores it. */
25
+ state: "kept" | "open";
26
+ /** Other runs that found the same quote on the same page, newest first. */
27
+ alsoIn: string[];
28
+ score: number;
29
+ }
30
+ export interface RecallOptions {
31
+ /** At most this many; 20 by default. */
32
+ n?: number;
33
+ /** Leave this run out: the one asking. */
34
+ not?: string;
35
+ /** Only runs of this ask kind. */
36
+ kind?: AskKind;
37
+ /** Only these runs: a child's siblings and parent. */
38
+ runs?: readonly string[];
39
+ }
40
+ /** Several ledgers read as one (a folder per day): their records merged in time order. */
41
+ export declare const joined: (ledgers: readonly Pick<Ledger, "scan">[]) => Pick<Ledger, "scan">;
42
+ export declare function recall(ledger: Pick<Ledger, "scan">, terms: string, o?: RecallOptions): Promise<Recalled[]>;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * What earlier runs proved, found again: the findings of every run in the ledger, ranked by
3
+ * MiniSearch (BM25 over the finding's text and quote, prefix and a little fuzz). Each carries its
4
+ * quote, its source's date and its run, so a later run sees how old a fact is and where it came
5
+ * from. A finding a call or a failed check dropped is left out. Read only: the agent decides what
6
+ * still holds, puts it in `known` or reads the source again (WA-934, option A).
7
+ */
8
+ import MiniSearch from "minisearch";
9
+ import { latestCalls, ofType } from "../ledger/ledger.js";
10
+ import { byTime } from "../ledger/records.js";
11
+ /** Several ledgers read as one (a folder per day): their records merged in time order. */
12
+ export const joined = (ledgers) => ({
13
+ scan: async (f) => (await Promise.all(ledgers.map((l) => l.scan(f)))).flat().sort(byTime),
14
+ });
15
+ const fold = (s) => s.replace(/\s+/g, " ").trim().toLowerCase();
16
+ export async function recall(ledger, terms, o = {}) {
17
+ if (!terms.trim())
18
+ throw new Error("recall needs terms");
19
+ const records = await ledger.scan({ type: ["snapshot", "finding", "call"] });
20
+ const asks = new Map();
21
+ for (const s of ofType(records, "snapshot"))
22
+ if (!asks.has(s.run))
23
+ asks.set(s.run, { text: s.ask.text, kind: s.ask.kind });
24
+ const calls = latestCalls(records);
25
+ // Newest first, so the first of a quote seen twice is the latest proof of it.
26
+ const findings = ofType(records, "finding")
27
+ .filter((f) => f.run !== o.not && calls.get(f.id) !== "drop")
28
+ .filter((f) => !o.kind || asks.get(f.run)?.kind === o.kind)
29
+ .filter((f) => !o.runs || o.runs.includes(f.run))
30
+ .reverse();
31
+ const byKey = new Map();
32
+ for (const f of findings) {
33
+ const quote = f.proof.quote ?? "";
34
+ const key = `${f.proof.url}\u0000${fold(quote)}`;
35
+ const seen = byKey.get(key);
36
+ if (seen) {
37
+ if (f.run !== seen.run && !seen.alsoIn.includes(f.run))
38
+ seen.alsoIn.push(f.run);
39
+ continue;
40
+ }
41
+ byKey.set(key, {
42
+ finding: f.id,
43
+ run: f.run,
44
+ ask: asks.get(f.run)?.text ?? "",
45
+ text: f.text,
46
+ quote,
47
+ url: f.proof.url,
48
+ date: f.date ?? null,
49
+ found: f.at,
50
+ by: f.by,
51
+ state: calls.get(f.id) === "keep" ? "kept" : "open",
52
+ alsoIn: [],
53
+ score: 0,
54
+ });
55
+ }
56
+ const index = new MiniSearch({
57
+ idField: "finding",
58
+ fields: ["text", "quote"],
59
+ searchOptions: { boost: { text: 2 }, prefix: true, fuzzy: 0.2 },
60
+ });
61
+ index.addAll([...byKey.values()]);
62
+ const of = new Map([...byKey.values()].map((r) => [r.finding, r]));
63
+ return index
64
+ .search(terms)
65
+ .slice(0, o.n ?? 20)
66
+ .flatMap((hit) => {
67
+ const r = of.get(hit.id);
68
+ return r ? [{ ...r, score: Math.round(hit.score * 100) / 100 }] : [];
69
+ });
70
+ }
71
+ //# sourceMappingURL=recall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall.js","sourceRoot":"","sources":["../../src/engine/recall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { type CallRecord, type CheckRecord, type ClassRecord, type DeadRecord, type DocumentRecord, type DraftRecord, type EdgeRecord, type FindingRecord, type FoundRecord, type GapRecord, type HitRecord, type JudgeRecord, type LedgerRecord, type PatternRecord, type PersonRecord, type QueryRecord, type SnapshotRecord, type SourceClass, type SpendRecord, type SteerRecord, type Step, type VerdictRecord } from "../ledger/records.js";
2
+ export interface Pointer {
3
+ round: number;
4
+ step: Step;
5
+ }
6
+ export interface RunState {
7
+ run: string;
8
+ records: LedgerRecord[];
9
+ /** The latest snapshot: the ask and the allow-list. Absent before round 1's plan. */
10
+ snapshot: SnapshotRecord | null;
11
+ /** The round the pointer is in. */
12
+ round: number;
13
+ /** Where to go next; null when the run stopped. */
14
+ next: Pointer | null;
15
+ /** Why the run stopped or the last step gave up. */
16
+ note: string | null;
17
+ queries: QueryRecord[];
18
+ hits: HitRecord[];
19
+ documents: DocumentRecord[];
20
+ findings: FindingRecord[];
21
+ /** The extractor's word on each document it handled. */
22
+ verdicts: VerdictRecord[];
23
+ calls: CallRecord[];
24
+ spends: SpendRecord[];
25
+ edges: EdgeRecord[];
26
+ /** What the person said along the way, oldest first. */
27
+ steers: SteerRecord[];
28
+ /** What a step or a person said a round lacks. */
29
+ gaps: GapRecord[];
30
+ /** The sample check's word on each finding it checked. */
31
+ checks: CheckRecord[];
32
+ /** What the agent says several findings say together. */
33
+ patterns: PatternRecord[];
34
+ drafts: DraftRecord[];
35
+ classes: ClassRecord[];
36
+ /** Facets the agent gave up on, for the rest of the run. */
37
+ dead: DeadRecord[];
38
+ /** People the agent named across platforms, in time order. */
39
+ people: PersonRecord[];
40
+ /** The latest class on each document and finding. */
41
+ classOf: Map<string, SourceClass>;
42
+ /** The judge's latest answers on each hit, document and finding. */
43
+ judged: Map<string, JudgeRecord["answers"]>;
44
+ judges: JudgeRecord[];
45
+ /** A known hit found again by another query. */
46
+ founds: FoundRecord[];
47
+ /** The hit record for each canonical URL: a hit seen again, in any round, is a `found`, not a new record. */
48
+ hitByKey: Map<string, string>;
49
+ /** The latest call on each hit and finding. */
50
+ called: Map<string, "keep" | "drop">;
51
+ }
52
+ /** Records must be in time order. */
53
+ export declare function fold(run: string, records: readonly LedgerRecord[]): RunState;
54
+ /** A finding stays kept unless its latest call is a drop. */
55
+ export declare const kept: (state: RunState, f: FindingRecord) => boolean;
56
+ /** What the run has found so far: the kept findings' sentences with their URLs, oldest first. */
57
+ export declare const found: (state: RunState, round?: number) => string[];
58
+ /**
59
+ * The round a person's gap or back edge is about. Between rounds (a pause, a stop) the pointer
60
+ * is one ahead of the last review, so it is the round that review closed; mid-round, the current one.
61
+ */
62
+ export declare const personRound: (state: RunState) => number;
63
+ /** The queries that found a hit: the one that wrote it, then every one it came back for. */
64
+ export declare const queriesOf: (state: RunState) => Map<string, Set<string>>;
65
+ /** The hits of a round, the ones no call has named yet. */
66
+ export declare const uncalledHits: (state: RunState, round: number) => HitRecord[];
67
+ /**
68
+ * The hits to read: kept by triage in this round, no document yet. A failed open is tried again
69
+ * on a rerun (`birdwatch back <run> read`).
70
+ */
71
+ export declare const toRead: (state: RunState, round: number) => HitRecord[];
72
+ /**
73
+ * The kept findings to check, at most `n`, spread across documents: each document's first
74
+ * unchecked finding, then each one's second, and so on. A finding with no quote, or one checked
75
+ * already, is not in it. Findings come in record order, so the sample is the same on a rerun.
76
+ */
77
+ export declare function spreadSample(state: RunState, n: number, only?: (f: FindingRecord) => boolean): FindingRecord[];
78
+ /** Share of kept findings a check reads at least: the budget's `checks` is the floor under it. */
79
+ export declare const CHECK_SHARE = 0.1;
80
+ /** The sample's size: the budget's `checks`, or a tenth of the kept findings in reach, whichever is more; 0 checks is none. */
81
+ export declare const checkCount: (inReach: number, budget: number) => number;
82
+ /** A round's sample for the check step, less what the round checked already. */
83
+ export declare const toCheck: (state: RunState, round: number, budget: number) => FindingRecord[];
84
+ /** The documents of a round without a verdict: the extract step has not finished them. */
85
+ export declare const toExtract: (state: RunState, round: number) => DocumentRecord[];
86
+ /** A finding's class: its own, else its document's; undefined when neither has one. */
87
+ export declare const classOfFinding: (state: RunState, f: FindingRecord) => SourceClass | undefined;
88
+ /**
89
+ * Sources per class over findings still kept: distinct sites, but each forum thread (a
90
+ * document) counts on its own, so 31 threads on one forum are 31 voices, not one site.
91
+ */
92
+ export declare function classesOf(state: RunState, ids: readonly string[]): Partial<Record<SourceClass | "unclassed", number>>;
93
+ /** The sources a set of findings rests on: the sum over `classesOf`. */
94
+ export declare const sourcesOf: (classes: ReturnType<typeof classesOf>) => number;
95
+ /** Vendor echo: every classed source is a vendor, and vendors cite each other. */
96
+ export declare const echoOf: (classes: ReturnType<typeof classesOf>) => boolean;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * A run's state is the fold of its records, never held between steps: every step reads the
3
+ * trace again. The fold gives the ask, the round, the plan, what each step
4
+ * has handled, and the pointer: the next step to run, from the `done` and `edge` records.
5
+ */
6
+ import { hostOf } from "../document/index.js";
7
+ import { latestCalls, ofType } from "../ledger/ledger.js";
8
+ import { STEPS, } from "../ledger/records.js";
9
+ const after = (step) => STEPS[STEPS.indexOf(step) + 1] ?? null;
10
+ /** Records must be in time order. */
11
+ export function fold(run, records) {
12
+ let next = { round: 1, step: "plan" };
13
+ let note = null;
14
+ /** The round whose edge set the pointer: the review's own `done` then leaves it. */
15
+ let edged = 0;
16
+ for (const r of records) {
17
+ if (r.type === "done") {
18
+ if (!r.ok) {
19
+ next = { round: r.round, step: r.step };
20
+ note = r.note ?? null;
21
+ continue;
22
+ }
23
+ if (r.step === "review" && edged === r.round)
24
+ continue;
25
+ const to = after(r.step);
26
+ next = to ? { round: r.round, step: to } : null;
27
+ note = null;
28
+ }
29
+ else if (r.type === "edge") {
30
+ edged = r.round;
31
+ if (r.next === "stop")
32
+ next = null;
33
+ else if (r.next === "again")
34
+ next = { round: r.round + 1, step: "plan" };
35
+ else if (r.to)
36
+ next = { round: r.round, step: r.to };
37
+ note = r.why;
38
+ }
39
+ }
40
+ const snapshots = ofType(records, "snapshot");
41
+ const hits = ofType(records, "hit");
42
+ return {
43
+ run,
44
+ records: [...records],
45
+ snapshot: snapshots.at(-1) ?? null,
46
+ round: next?.round ?? snapshots.at(-1)?.round ?? 1,
47
+ next,
48
+ note,
49
+ queries: ofType(records, "query"),
50
+ hits,
51
+ documents: ofType(records, "document"),
52
+ findings: ofType(records, "finding"),
53
+ verdicts: ofType(records, "verdict"),
54
+ calls: ofType(records, "call"),
55
+ spends: ofType(records, "spend"),
56
+ edges: ofType(records, "edge"),
57
+ steers: ofType(records, "steer"),
58
+ gaps: ofType(records, "gap"),
59
+ checks: ofType(records, "check"),
60
+ patterns: ofType(records, "pattern"),
61
+ drafts: ofType(records, "draft"),
62
+ classes: ofType(records, "class"),
63
+ dead: ofType(records, "dead"),
64
+ people: ofType(records, "person"),
65
+ classOf: new Map(ofType(records, "class").map((c) => [c.on, c.class])),
66
+ judged: new Map(ofType(records, "judge").map((j) => [j.on, j.answers])),
67
+ judges: ofType(records, "judge"),
68
+ founds: ofType(records, "found"),
69
+ hitByKey: new Map(hits.map((h) => [h.key, h.id])),
70
+ called: latestCalls(records),
71
+ };
72
+ }
73
+ /** A finding stays kept unless its latest call is a drop. */
74
+ export const kept = (state, f) => state.called.get(f.id) !== "drop";
75
+ /** What the run has found so far: the kept findings' sentences with their URLs, oldest first. */
76
+ export const found = (state, round) => state.findings
77
+ .filter((f) => (round === undefined || f.round === round) && kept(state, f))
78
+ .map((f) => `${f.text} (${f.proof.url})`);
79
+ /**
80
+ * The round a person's gap or back edge is about. Between rounds (a pause, a stop) the pointer
81
+ * is one ahead of the last review, so it is the round that review closed; mid-round, the current one.
82
+ */
83
+ export const personRound = (state) => {
84
+ const last = state.edges.at(-1);
85
+ return last && (!state.next || state.next.step === "plan") ? last.round : state.round;
86
+ };
87
+ /** The queries that found a hit: the one that wrote it, then every one it came back for. */
88
+ export const queriesOf = (state) => {
89
+ const out = new Map(state.hits.map((h) => [h.id, new Set(h.queries)]));
90
+ for (const f of state.founds)
91
+ out.get(f.hit)?.add(f.query);
92
+ return out;
93
+ };
94
+ /** The hits of a round, the ones no call has named yet. */
95
+ export const uncalledHits = (state, round) => state.hits.filter((h) => h.round === round && !state.called.has(h.id));
96
+ /**
97
+ * The hits to read: kept by triage in this round, no document yet. A failed open is tried again
98
+ * on a rerun (`birdwatch back <run> read`).
99
+ */
100
+ export const toRead = (state, round) => {
101
+ const read = new Set(state.documents.map((d) => d.hit));
102
+ return state.hits.filter((h) => h.round === round && state.called.get(h.id) === "keep" && !read.has(h.id));
103
+ };
104
+ /**
105
+ * The kept findings to check, at most `n`, spread across documents: each document's first
106
+ * unchecked finding, then each one's second, and so on. A finding with no quote, or one checked
107
+ * already, is not in it. Findings come in record order, so the sample is the same on a rerun.
108
+ */
109
+ export function spreadSample(state, n, only = () => true) {
110
+ const checked = new Set(state.checks.map((c) => c.on));
111
+ const byDoc = new Map();
112
+ for (const f of state.findings)
113
+ if (only(f) && f.proof.quote && !checked.has(f.id) && kept(state, f))
114
+ byDoc.set(f.document, [...(byDoc.get(f.document) ?? []), f]);
115
+ const out = [];
116
+ const lists = [...byDoc.values()];
117
+ for (let i = 0; out.length < n && lists.some((l) => i < l.length); i++)
118
+ for (const l of lists) {
119
+ const f = l[i];
120
+ if (f && out.length < n)
121
+ out.push(f);
122
+ }
123
+ return out;
124
+ }
125
+ /** Share of kept findings a check reads at least: the budget's `checks` is the floor under it. */
126
+ export const CHECK_SHARE = 0.1;
127
+ /** The sample's size: the budget's `checks`, or a tenth of the kept findings in reach, whichever is more; 0 checks is none. */
128
+ export const checkCount = (inReach, budget) => budget ? Math.max(budget, Math.ceil(inReach * CHECK_SHARE)) : 0;
129
+ /** A round's sample for the check step, less what the round checked already. */
130
+ export const toCheck = (state, round, budget) => {
131
+ const inRound = (f) => f.round === round;
132
+ const done = state.checks.filter((c) => c.round === round).length;
133
+ const reach = state.findings.filter((f) => inRound(f) && f.proof.quote && kept(state, f)).length;
134
+ return spreadSample(state, checkCount(reach, budget) - done, inRound);
135
+ };
136
+ /** The documents of a round without a verdict: the extract step has not finished them. */
137
+ export const toExtract = (state, round) => {
138
+ const judged = new Set(state.verdicts.map((v) => v.document));
139
+ return state.documents.filter((d) => d.round === round && !judged.has(d.id));
140
+ };
141
+ /** A finding's class: its own, else its document's; undefined when neither has one. */
142
+ export const classOfFinding = (state, f) => state.classOf.get(f.id) ?? state.classOf.get(f.document);
143
+ /**
144
+ * Sources per class over findings still kept: distinct sites, but each forum thread (a
145
+ * document) counts on its own, so 31 threads on one forum are 31 voices, not one site.
146
+ */
147
+ export function classesOf(state, ids) {
148
+ const sources = new Map();
149
+ for (const f of state.findings) {
150
+ if (!ids.includes(f.id) || !kept(state, f))
151
+ continue;
152
+ const c = classOfFinding(state, f) ?? "unclassed";
153
+ const key = c === "forum" ? f.document : hostOf(f.proof.url);
154
+ sources.set(c, (sources.get(c) ?? new Set()).add(key));
155
+ }
156
+ return Object.fromEntries([...sources].map(([c, s]) => [c, s.size]));
157
+ }
158
+ /** The sources a set of findings rests on: the sum over `classesOf`. */
159
+ export const sourcesOf = (classes) => Object.values(classes).reduce((a, b) => a + (b ?? 0), 0);
160
+ /** Vendor echo: every classed source is a vendor, and vendors cite each other. */
161
+ export const echoOf = (classes) => !!classes.vendor && Object.keys(classes).every((c) => c === "vendor" || c === "unclassed");
162
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/engine/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAoBL,KAAK,GAIN,MAAM,sBAAsB,CAAC;AAsD9B,MAAM,KAAK,GAAG,CAAC,IAAU,EAAe,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;AAElF,qCAAqC;AACrC,MAAM,UAAU,IAAI,CAAC,GAAW,EAAE,OAAgC;IAChE,IAAI,IAAI,GAAmB,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACtD,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,oFAAoF;IACpF,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gBACV,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACxC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC;gBACtB,SAAS;YACX,CAAC;YACD,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,KAAK;gBAAE,SAAS;YACvD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAChD,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7B,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;YAChB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;gBAAE,IAAI,GAAG,IAAI,CAAC;iBAC9B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;gBAAE,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iBACpE,IAAI,CAAC,CAAC,EAAE;gBAAE,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YACrD,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC;QACf,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO;QACL,GAAG;QACH,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;QACrB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI;QAClC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;QAClD,IAAI;QACJ,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QACjC,IAAI;QACJ,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC;QACpC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QACjC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;QAC7B,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;QACjC,OAAO,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,MAAM,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,QAAQ,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC;KAC7B,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAe,EAAE,CAAgB,EAAW,EAAE,CACjE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAEpC,iGAAiG;AACjG,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAe,EAAE,KAAc,EAAY,EAAE,CACjE,KAAK,CAAC,QAAQ;KACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;KAC3E,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAe,EAAU,EAAE;IACrD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;AACxF,CAAC,CAAC;AAEF,4FAA4F;AAC5F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAe,EAA4B,EAAE;IACrE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC3D,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAe,EAAE,KAAa,EAAe,EAAE,CAC1E,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEzE;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAe,EAAE,KAAa,EAAe,EAAE;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACjF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAe,EACf,CAAS,EACT,OAAsC,GAAG,EAAE,CAAC,IAAI;IAEhD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ;QAC5B,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAClE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;QACpE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACf,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kGAAkG;AAClG,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B,+HAA+H;AAC/H,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,MAAc,EAAU,EAAE,CACpE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAElE,gFAAgF;AAChF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAe,EAAE,KAAa,EAAE,MAAc,EAAmB,EAAE;IACzF,MAAM,OAAO,GAAG,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjG,OAAO,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,KAAa,EAAoB,EAAE;IAC5E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/E,CAAC,CAAC;AAEF,uFAAuF;AACvF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAe,EAAE,CAAgB,EAA2B,EAAE,CAC3F,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,KAAe,EACf,GAAsB;IAEtB,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0C,CAAC;IAClE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAAE,SAAS;QACrD,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,WAAW,CAAC;QAClD,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAqC,EAAU,EAAE,CACzE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE3D,kFAAkF;AAClF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAqC,EAAW,EAAE,CACvE,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,WAAW,CAAC,CAAC"}