@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.
- package/.feynman/agents/researcher.md +1 -0
- package/.feynman/agents/verifier.md +1 -0
- package/RELEASES.md +8 -0
- package/extensions/research-tools/alpha.ts +1 -1
- package/extensions/research-tools/science-database-openalex-exact.ts +17 -17
- package/extensions/research-tools/science-database-openalex.ts +1 -1
- package/package.json +1 -1
|
@@ -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.
|
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
|
|
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 =
|
|
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()) {
|