@companion-ai/feynman 0.5.1 → 0.5.2

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.
@@ -5,6 +5,7 @@ thinking: high
5
5
  tools: read, write, edit, bash, grep, find, ls, web_search, fetch_content, get_search_content, feynman_science_database_search, hf_dataset_info, hf_repo_files, hf_repo_read_file
6
6
  output: research.md
7
7
  defaultProgress: true
8
+ async: true
8
9
  ---
9
10
 
10
11
  You are Feynman's evidence-gathering subagent.
@@ -5,6 +5,7 @@ thinking: medium
5
5
  tools: read, bash, grep, find, ls, write, edit, web_search, fetch_content, get_search_content, feynman_science_database_search
6
6
  output: cited.md
7
7
  defaultProgress: true
8
+ async: true
8
9
  ---
9
10
 
10
11
  You are Feynman's verifier agent.
package/RELEASES.md CHANGED
@@ -6,6 +6,14 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v0.5.2 - 2026-09-23
10
+
11
+ ### Faster, cheaper research runs
12
+
13
+ - OpenAlex exact searches (`openalex_search_works`, citations, references, authors, venues) now return at most the tool's `limit` (default 5, max 20) instead of 50-100 full records; an explicit `max_records` is capped at 50. In live `/lit` runs these results were 100-240 KB per call and drove most of the multi-million-token usage.
14
+ - The `researcher` and `verifier` subagents run in the background by default, where they get Feynman's web and literature tools. Foreground launches of these agents failed and had to be retried.
15
+ - `alpha_ask_paper` now states it takes arXiv and alphaXiv papers only, and points DOI-only papers to `fetch_content` or Europe PMC full text.
16
+
9
17
  ## v0.5.1 - 2026-09-23
10
18
 
11
19
  ### Telemetry
@@ -86,7 +86,7 @@ export function registerAlphaTools(pi: ExtensionAPI): void {
86
86
  pi.registerTool({
87
87
  name: "alpha_ask_paper",
88
88
  label: "Alpha Ask Paper",
89
- description: "Ask a targeted question about a paper. Uses AI to analyze the PDF and answer.",
89
+ description: "Ask a targeted question about an arXiv or alphaXiv paper. Uses AI to analyze the PDF and answer. DOI-only papers are not supported; read those with fetch_content on the open-access PDF or Europe PMC full text.",
90
90
  parameters: Type.Object({
91
91
  paper: Type.String({ description: "arXiv ID, arXiv URL, or alphaXiv URL." }),
92
92
  question: Type.String({ description: "Question about the paper." }),
@@ -1,4 +1,4 @@
1
- const MAX_EXACT_RECORDS = 500;
1
+ const MAX_EXACT_RECORDS = 50;
2
2
  const REQUEST_TIMEOUT_MS = 25_000;
3
3
  const OPENALEX_BASE = "https://api.openalex.org";
4
4
  const OPEN_ABSTRACT_LICENSES = new Set(["cc-by", "cc-by-sa", "cc0", "public-domain"]);
@@ -461,9 +461,9 @@ async function fetchWorkById(workId: string): Promise<{
461
461
  };
462
462
  }
463
463
 
464
- async function exactWorkSearch(query: string, commandQuery: string): Promise<Record<string, unknown>> {
464
+ async function exactWorkSearch(query: string, commandQuery: string, defaultLimit?: number): Promise<Record<string, unknown>> {
465
465
  const parsed = parseKeyValueQuery(commandQuery);
466
- const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), 50);
466
+ const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), defaultLimit ?? 50);
467
467
  const url = endpointPath("/works");
468
468
  if (parsed.text) url.searchParams.set("search", parsed.text);
469
469
  const filters: string[] = [];
@@ -518,12 +518,12 @@ async function exactGetWork(query: string, workId: string): Promise<Record<strin
518
518
  }, "openalex_get_work", query, resolved.endpoints, resolved.credentialStatus);
519
519
  }
520
520
 
521
- async function exactCitations(query: string, commandQuery: string): Promise<Record<string, unknown>> {
521
+ async function exactCitations(query: string, commandQuery: string, defaultLimit?: number): Promise<Record<string, unknown>> {
522
522
  const parsed = parseKeyValueQuery(commandQuery);
523
523
  const workInput = parsed.text;
524
524
  if (!workInput) throw new Error("openalex_citations requires an OpenAlex work id or DOI.");
525
525
  const resolved = await fetchWorkById(workInput);
526
- const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), 50);
526
+ const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), defaultLimit ?? 50);
527
527
  const url = endpointPath("/works");
528
528
  url.searchParams.set("filter", `cites:${resolved.workId}`);
529
529
  const sort = exactSearchSort(parsed.flags.sort ?? "cited_by_count", false);
@@ -545,11 +545,11 @@ async function exactCitations(query: string, commandQuery: string): Promise<Reco
545
545
  }, "openalex_citations", query, [...resolved.endpoints, result.endpoint], result.credentialStatus);
546
546
  }
547
547
 
548
- async function exactReferences(query: string, commandQuery: string): Promise<Record<string, unknown>> {
548
+ async function exactReferences(query: string, commandQuery: string, defaultLimit?: number): Promise<Record<string, unknown>> {
549
549
  const parsed = parseKeyValueQuery(commandQuery);
550
550
  if (!parsed.text) throw new Error("openalex_references requires an OpenAlex work id or DOI.");
551
551
  const resolved = await fetchWorkById(parsed.text);
552
- const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), 100);
552
+ const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), defaultLimit ?? 100);
553
553
  const referenceIds = arrayValue(resolved.payload.referenced_works).map(shortOpenAlexId).filter((id): id is string => Boolean(id));
554
554
  const selected = referenceIds.slice(0, maxRecords);
555
555
  const endpoints = [...resolved.endpoints];
@@ -581,10 +581,10 @@ async function exactReferences(query: string, commandQuery: string): Promise<Rec
581
581
  }, "openalex_references", query, endpoints, credentialStatus);
582
582
  }
583
583
 
584
- async function exactSearchAuthors(query: string, commandQuery: string): Promise<Record<string, unknown>> {
584
+ async function exactSearchAuthors(query: string, commandQuery: string, defaultLimit?: number): Promise<Record<string, unknown>> {
585
585
  const parsed = parseKeyValueQuery(commandQuery);
586
586
  if (!parsed.text) throw new Error("openalex_search_authors requires a name query.");
587
- const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), 25);
587
+ const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), defaultLimit ?? 25);
588
588
  const url = endpointPath("/authors");
589
589
  url.searchParams.set("search", parsed.text);
590
590
  url.searchParams.set("per-page", String(Math.min(maxRecords, 200)));
@@ -625,7 +625,7 @@ async function exactGetAuthor(query: string, commandQuery: string): Promise<Reco
625
625
  }, "openalex_get_author", query, [result.endpoint, works.endpoint], works.credentialStatus);
626
626
  }
627
627
 
628
- async function exactVenueInfo(query: string, commandQuery: string): Promise<Record<string, unknown>> {
628
+ async function exactVenueInfo(query: string, commandQuery: string, defaultLimit?: number): Promise<Record<string, unknown>> {
629
629
  const parsed = parseKeyValueQuery(commandQuery);
630
630
  const venue = parsed.text;
631
631
  if (!venue) throw new Error("openalex_venue_info requires a source id, ISSN, or venue name.");
@@ -640,7 +640,7 @@ async function exactVenueInfo(query: string, commandQuery: string): Promise<Reco
640
640
  records: [row],
641
641
  }, "openalex_venue_info", query, [result.endpoint], result.credentialStatus);
642
642
  }
643
- const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), 10);
643
+ const maxRecords = safeExactLimit(numberValue(parsed.flags.max_records), defaultLimit ?? 10);
644
644
  const url = endpointPath("/sources");
645
645
  url.searchParams.set("search", venue);
646
646
  url.searchParams.set("per-page", String(Math.min(maxRecords, 200)));
@@ -657,15 +657,15 @@ async function exactVenueInfo(query: string, commandQuery: string): Promise<Reco
657
657
  }, "openalex_venue_info", query, [result.endpoint], result.credentialStatus);
658
658
  }
659
659
 
660
- export async function searchExactOpenAlex(query: string): Promise<Record<string, unknown> | undefined> {
660
+ export async function searchExactOpenAlex(query: string, defaultLimit?: number): Promise<Record<string, unknown> | undefined> {
661
661
  const command = exactCommand(query);
662
662
  if (!command) return undefined;
663
- if (command.name === "openalex_search_works") return exactWorkSearch(query, command.rest);
663
+ if (command.name === "openalex_search_works") return exactWorkSearch(query, command.rest, defaultLimit);
664
664
  if (command.name === "openalex_get_work") return exactGetWork(query, command.rest);
665
- if (command.name === "openalex_citations") return exactCitations(query, command.rest);
666
- if (command.name === "openalex_references") return exactReferences(query, command.rest);
667
- if (command.name === "openalex_search_authors") return exactSearchAuthors(query, command.rest);
665
+ if (command.name === "openalex_citations") return exactCitations(query, command.rest, defaultLimit);
666
+ if (command.name === "openalex_references") return exactReferences(query, command.rest, defaultLimit);
667
+ if (command.name === "openalex_search_authors") return exactSearchAuthors(query, command.rest, defaultLimit);
668
668
  if (command.name === "openalex_get_author") return exactGetAuthor(query, command.rest);
669
- if (command.name === "openalex_venue_info") return exactVenueInfo(query, command.rest);
669
+ if (command.name === "openalex_venue_info") return exactVenueInfo(query, command.rest, defaultLimit);
670
670
  return undefined;
671
671
  }
@@ -354,7 +354,7 @@ async function resolveDoiWork(doiWorkId: string): Promise<{
354
354
  export async function searchOpenAlex(params: SearchParams): Promise<Record<string, unknown>> {
355
355
  const query = cleanQuery(params.query);
356
356
  const limit = safeLimit(params.limit);
357
- const exact = await searchExactOpenAlex(query);
357
+ const exact = await searchExactOpenAlex(query, limit);
358
358
  if (exact) return exact;
359
359
  if (/^rate-limit$/i.test(query)) {
360
360
  if (!openAlexApiKey()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "license": "MIT",
6
6
  "type": "module",