@absolutejs/rag 0.22.6 → 0.22.8

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/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.22.8 — 2026-10-28
10
+
11
+ ### Fixed
12
+
13
+ - **Allow bounded research to follow validated links discovered in fetched pages, without treating unread links as evidence or admitting invented URLs.** (`createResearch`)
14
+
15
+ ## 0.22.7 — 2026-10-28
16
+
17
+ ### Fixed
18
+
19
+ - **Separate structured research tasks from literal search queries** (`createResearch`, `researchPlugin`, `ResearchInput`)
20
+ Task mode plans concise initial searches within the existing round budget and does not treat quoted JSON fields as required source phrases. Exact-phrase search mode retains identity filtering.
21
+
9
22
  ## 0.22.6 — 2026-10-22
10
23
 
11
24
  ### Changed
package/changelog.json CHANGED
@@ -2,6 +2,35 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/rag",
4
4
  "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "kind": "fixed",
9
+ "summary": "Allow bounded research to follow validated links discovered in fetched pages, without treating unread links as evidence or admitting invented URLs.",
10
+ "symbols": [
11
+ "createResearch"
12
+ ]
13
+ }
14
+ ],
15
+ "date": "2026-10-28",
16
+ "version": "0.22.8"
17
+ },
18
+ {
19
+ "changes": [
20
+ {
21
+ "detail": "Task mode plans concise initial searches within the existing round budget and does not treat quoted JSON fields as required source phrases. Exact-phrase search mode retains identity filtering.",
22
+ "kind": "fixed",
23
+ "summary": "Separate structured research tasks from literal search queries",
24
+ "symbols": [
25
+ "createResearch",
26
+ "researchPlugin",
27
+ "ResearchInput"
28
+ ]
29
+ }
30
+ ],
31
+ "date": "2026-10-28",
32
+ "version": "0.22.7"
33
+ },
5
34
  {
6
35
  "changes": [
7
36
  {
@@ -121450,9 +121450,10 @@ ${prompt}`,
121450
121450
  return generated.object;
121451
121451
  };
121452
121452
  const normalize = (text) => text.normalize("NFKC").toLowerCase().replace(/\s+/gu, " ").trim();
121453
- const requiredPhrases = input.requiredPhrases ?? [...input.query.matchAll(/"([^"\n]+)"/gu)].map((match) => match[1]);
121453
+ const requiredPhrases = input.requiredPhrases ?? (input.queryMode === "task" ? [] : [...input.query.matchAll(/"([^"\n]+)"/gu)].map((match) => match[1]));
121454
121454
  const urls = new Set;
121455
121455
  const queries = new Set;
121456
+ const discoveredLinks = new Map;
121456
121457
  let evidenceChars = 0;
121457
121458
  const sourceText = new Map;
121458
121459
  const rebalanceEvidence = () => {
@@ -121524,7 +121525,7 @@ ${prompt}`,
121524
121525
  const doRead = async (url) => {
121525
121526
  if (urls.has(url) || urls.size >= limits.reads)
121526
121527
  return;
121527
- if (!result.searches.some((search) => search.sources.some((source) => source.url === url)))
121528
+ if (!discoveredLinks.has(url) && !result.searches.some((search) => search.sources.some((source) => source.url === url)))
121528
121529
  return;
121529
121530
  urls.add(url);
121530
121531
  const page = await operate("read", () => (config.reader ?? readRAGWebpage)({
@@ -121537,6 +121538,16 @@ ${prompt}`,
121537
121538
  if (page.status !== "ok")
121538
121539
  result.limitations.push(`Page read ${page.status}: ${url}`);
121539
121540
  if (page.text && page.status !== "error") {
121541
+ for (const link of page.evidence.links) {
121542
+ if (discoveredLinks.size >= 64)
121543
+ break;
121544
+ try {
121545
+ const target = validatePublicWebUrl(new URL(link.url, page.finalUrl).href);
121546
+ target.hash = "";
121547
+ if (!discoveredLinks.has(target.href))
121548
+ discoveredLinks.set(target.href, { url: target.href, label: link.label.slice(0, 200), discoveredOn: page.finalUrl });
121549
+ } catch {}
121550
+ }
121540
121551
  const prior = result.sources.find((source) => source.url === url);
121541
121552
  if (prior) {
121542
121553
  sourceText.set(prior.url, page.text.slice(0, limits.evidenceChars));
@@ -121557,18 +121568,30 @@ ${prompt}`,
121557
121568
  }
121558
121569
  };
121559
121570
  try {
121560
- for (const query of input.queries ?? [input.query])
121561
- await doSearch(query);
121562
- for (let round = 0;round < limits.rounds; round++) {
121571
+ const needsInitialPlan = !input.queries && (input.queryMode === "task" || boundedQueries(input.query).length > 1) && limits.rounds > 0;
121572
+ if (needsInitialPlan) {
121573
+ const initial = await generate("plan", PlanSchema, "Turn the research task into concise search-engine queries. Preserve the requested company identity and target criteria, but never copy instructions, JSON, internal markers, or the whole brief into a query. Prioritize the most useful query first. Return at most remainingSearches queries. Return no URLs: source URLs must be discovered rather than invented.", {
121574
+ query: input.query,
121575
+ instructions: task.instructions,
121576
+ remainingSearches: limits.searches
121577
+ });
121578
+ for (const query of initial.queries)
121579
+ await doSearch(query);
121580
+ } else {
121581
+ for (const query of input.queries ?? [input.query])
121582
+ await doSearch(query);
121583
+ }
121584
+ for (let round = needsInitialPlan ? 1 : 0;round < limits.rounds; round++) {
121563
121585
  if (!result.sources.length && queries.size >= limits.searches)
121564
121586
  break;
121565
121587
  let plan;
121566
121588
  try {
121567
- plan = await generate("plan", PlanSchema, "Select follow-up searches for missing evidence and primary-source URLs to read. For current people or roles, seek the organization\u2019s own leadership/about/team pages; testimonials, package authors, and job listings are not staff identity evidence. For policies, read the full policy or agreement to preserve section scope and exceptions. URLs must come from the supplied search results. done means available evidence addresses the requested schema, not merely that some results exist. Return no actions if further work is not useful.", {
121589
+ plan = await generate("plan", PlanSchema, "Select follow-up searches for missing evidence and primary-source URLs to read. For current people or roles, seek the organization\u2019s own leadership/about/team pages; testimonials, package authors, and job listings are not staff identity evidence. For policies, read the full policy or agreement to preserve section scope and exceptions. URLs must come from the supplied search results or discoveredLinks. Discovered links are unread navigation hints, not evidence; read relevant product, pricing, checkout or policy details before concluding information is absent. done means available evidence addresses the requested schema, not merely that some results exist. Return no actions if further work is not useful.", {
121568
121590
  query: input.query,
121569
121591
  schema: task.schema,
121570
121592
  instructions: task.instructions,
121571
121593
  sources: result.sources,
121594
+ discoveredLinks: [...discoveredLinks.values()].filter((link) => !urls.has(link.url)),
121572
121595
  searched: [...queries],
121573
121596
  read: [...urls],
121574
121597
  remainingSearches: limits.searches - queries.size,
@@ -121669,6 +121692,7 @@ var renderResearchResult = (result) => `<section aria-label="Research result"><p
121669
121692
  }).join("")}</li>`).join("")}</ul>${result.limitations.map((message) => `<p>${escape5(message)}</p>`).join("")}</section>`;
121670
121693
  var bodySchema = t.Object({
121671
121694
  query: t.String({ minLength: 1, maxLength: 8000 }),
121695
+ queryMode: t.Optional(t.Union([t.Literal("search"), t.Literal("task")])),
121672
121696
  task: t.Optional(t.String({ maxLength: 128 })),
121673
121697
  freshness: t.Optional(t.String({ maxLength: 64 }))
121674
121698
  }, { additionalProperties: false });
@@ -122388,6 +122412,7 @@ var researchManifest = defineManifest()({
122388
122412
  },
122389
122413
  input: Type.Object({
122390
122414
  query: Type.String({ minLength: 1, maxLength: 8000 }),
122415
+ queryMode: Type.Optional(Type.Union([Type.Literal("search"), Type.Literal("task")])),
122391
122416
  task: Type.Optional(Type.String({ maxLength: 128 }))
122392
122417
  }),
122393
122418
  handler: async (input, runtime) => JSON.stringify(await runtime.run(input))
@@ -122486,5 +122511,5 @@ export {
122486
122511
  selectResearchMonitorEvents
122487
122512
  };
122488
122513
 
122489
- //# debugId=BC8292806DC74FDD64756E2164756E21
122514
+ //# debugId=98A81F60666A9C0F64756E2164756E21
122490
122515
  //# sourceMappingURL=index.js.map