@companion-ai/feynman 0.5.24 → 0.5.26
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/RELEASES.md +13 -0
- package/dist/cli.js +13 -4
- package/dist/model/catalog.js +16 -3
- package/extensions/research-tools/alpha.ts +5 -1
- package/extensions/research-tools/science-database-arxiv.ts +6 -3
- package/extensions/research-tools/science-databases.ts +14 -1
- package/metadata/commands.mjs +1 -1
- package/package.json +5 -5
package/RELEASES.md
CHANGED
|
@@ -6,6 +6,19 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## v0.5.26 - 2026-10-05
|
|
10
|
+
|
|
11
|
+
- **GitHub Copilot users get a research model by default.** Copilot names Claude models with dots (`claude-opus-5.5`), which Feynman did not recognize, so a new Copilot login fell back to alphabetical order and set `claude-haiku-4.5` as the default; Copilot then rejected it with "The requested model is not supported" for several users. Feynman now picks Copilot's newest Claude Opus, then Sonnet, then GPT. If `feynman status` shows `github-copilot/claude-haiku-4.5` and you did not choose it, run `feynman model set` to pick another.
|
|
12
|
+
- `alpha_ask_paper` answers with the arXiv metadata and abstract, marked as a fallback, when alphaXiv does not answer (for example after the login expires), like `alpha_search` and `alpha_get_paper` already did.
|
|
13
|
+
- `-p` is short for `--prompt`, as in Pi. `--setup`, `--list-models`, `--new`, `feynman setup model`, and `feynman model <provider/model>` now say which command was meant.
|
|
14
|
+
- pi-web-access 0.36.0: `web_search` accepts lists that a model sent as a JSON string (nicobailon/pi-web-access#491), `fetch_content` asks docs sites for markdown first, Brave prepaid keys keep working, and a `fetch_content` call whose only URL failed is reported as an error.
|
|
15
|
+
|
|
16
|
+
## v0.5.25 - 2026-10-05
|
|
17
|
+
|
|
18
|
+
- **Pi 1.0.2 and pi-subagents 0.76.0.** Pi now uses the patched brace-expansion 5.0.12 (GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p), so Feynman's npm audit has no exceptions left, and an npm install pulls about 120 fewer packages. Pi also retries "model at capacity" errors instead of ending the turn, and Sign in with ChatGPT reports a busy callback port instead of "OAuth state mismatch". pi-subagents frees async slots held by abandoned workflows.
|
|
19
|
+
- **A timed-out paper search no longer ends a research step.** A Semantic Scholar or OpenAlex topic search that hits the 25 s timeout is answered from the other index, and other sources say which one did not respond instead of "This operation was aborted".
|
|
20
|
+
- **arXiv batch versions.** Looking up a bare arXiv ID alongside an older version now returns the latest version for the bare ID regardless of feed order. An unavailable explicit version is reported in `not_found` instead of resolving to a different version. Thanks to @Kunyanli230 (#322).
|
|
21
|
+
|
|
9
22
|
## v0.5.24 - 2026-10-03
|
|
10
23
|
|
|
11
24
|
- **Pi 1.0.** Feynman now runs on Pi 1.0.0, with pi-subagents 0.75.0 (background subagents start on Pi 1.0) and pi-btw 0.7.1 (allows Pi 1.x). The terminal UI is fullscreen by default and keeps its own scrollback; run `feynman --tui-mode regular`, or set `"tuiMode": "regular"` in `~/.feynman/agent/settings.json`, to use the terminal's normal scrollback. Launch warnings show inside the fullscreen UI. Pi 1.0 also adds MCP OAuth hardening, a copy-code Anthropic login for headless machines, and lower codemode token use.
|
package/dist/cli.js
CHANGED
|
@@ -34,6 +34,12 @@ import { ASH, printAsciiHeader, printInfo, printPanel, printSection, RESET, SAGE
|
|
|
34
34
|
import { createModelRuntime } from "./model/registry.js";
|
|
35
35
|
import { cliCommandSections, formatCliWorkflowUsage, legacyFlags, readPromptSpecs, topLevelCommandNames, } from "../metadata/commands.mjs";
|
|
36
36
|
const TOP_LEVEL_COMMANDS = new Set(topLevelCommandNames);
|
|
37
|
+
// Flags users reach for that Feynman spells differently.
|
|
38
|
+
const UNKNOWN_OPTION_HINTS = {
|
|
39
|
+
"--setup": "feynman setup",
|
|
40
|
+
"--list-models": "feynman model list",
|
|
41
|
+
"--new": "feynman --new-session",
|
|
42
|
+
};
|
|
37
43
|
// Removed commands fail instead of falling through to a chat prompt.
|
|
38
44
|
const REMOVED_COMMANDS = new Set(["jobs", "paper", "rank", "serve", "watch"]);
|
|
39
45
|
const ALPHA_HUB_PACKAGE_PATH = ["@companion-ai", "alpha-hub"];
|
|
@@ -226,7 +232,8 @@ async function handleModelCommand(subcommand, args, feynmanSettingsPath, feynman
|
|
|
226
232
|
}
|
|
227
233
|
return;
|
|
228
234
|
}
|
|
229
|
-
|
|
235
|
+
const setHint = /[/:]/.test(subcommand) ? `Did you mean \`feynman model set ${subcommand}\`? ` : "";
|
|
236
|
+
throw new Error(`Unknown model command: ${subcommand}\n${setHint}Run \`feynman model help\` to see model commands.`);
|
|
230
237
|
}
|
|
231
238
|
async function handleUpdateCommand(piOptions, feynmanVersion, source) {
|
|
232
239
|
const latestFeynmanVersionPromise = fetchLatestFeynmanVersion();
|
|
@@ -468,7 +475,7 @@ async function runMain(input) {
|
|
|
468
475
|
"no-session": { type: "boolean" },
|
|
469
476
|
"no-themes": { type: "boolean" },
|
|
470
477
|
"tui-mode": { type: "string" },
|
|
471
|
-
prompt: { type: "string" },
|
|
478
|
+
prompt: { type: "string", short: "p" },
|
|
472
479
|
resume: { type: "boolean", short: "r" },
|
|
473
480
|
"service-tier": { type: "string" },
|
|
474
481
|
session: { type: "string" },
|
|
@@ -488,7 +495,9 @@ async function runMain(input) {
|
|
|
488
495
|
catch (error) {
|
|
489
496
|
if (error && typeof error === "object" && "code" in error && error.code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
|
|
490
497
|
const message = error instanceof Error ? error.message : String(error);
|
|
491
|
-
|
|
498
|
+
const option = /Unknown option '([^']+)'/.exec(message)?.[1];
|
|
499
|
+
const hint = option ? UNKNOWN_OPTION_HINTS[option] : undefined;
|
|
500
|
+
throw new Error(`${message}\n${hint ? `Did you mean \`${hint}\`? ` : ""}Run \`feynman help\` to see available commands and flags.`);
|
|
492
501
|
}
|
|
493
502
|
throw error;
|
|
494
503
|
}
|
|
@@ -559,7 +568,7 @@ async function runMain(input) {
|
|
|
559
568
|
return;
|
|
560
569
|
}
|
|
561
570
|
if (rest[0]) {
|
|
562
|
-
throw new Error(`Unknown setup command: ${rest[0]}`);
|
|
571
|
+
throw new Error(`Unknown setup command: ${rest[0]}${rest[0] === "model" ? "\nTo choose a model, run `feynman model login` or `feynman model set`." : ""}`);
|
|
563
572
|
}
|
|
564
573
|
await runSetup({
|
|
565
574
|
settingsPath: feynmanSettingsPath,
|
package/dist/model/catalog.js
CHANGED
|
@@ -70,6 +70,19 @@ const RESEARCH_MODEL_FAMILY_PREFERENCES = [
|
|
|
70
70
|
matches: (model) => model.provider === "opencode" && /^gpt-\d+(?:\.\d+)*(?:-.+)?$/i.test(model.id),
|
|
71
71
|
reason: "newest OpenCode Zen GPT fallback when direct OpenAI access is unavailable",
|
|
72
72
|
},
|
|
73
|
+
// GitHub Copilot writes Claude versions with dots (claude-opus-5.5).
|
|
74
|
+
{
|
|
75
|
+
matches: (model) => model.provider === "github-copilot" && /^claude-opus-\d+(?:[.-]\d+)*$/i.test(model.id),
|
|
76
|
+
reason: "newest Claude Opus model available through GitHub Copilot",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
matches: (model) => model.provider === "github-copilot" && /^claude-sonnet-\d+(?:[.-]\d+)*$/i.test(model.id),
|
|
80
|
+
reason: "newest Claude Sonnet model available through GitHub Copilot",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
matches: (model) => model.provider === "github-copilot" && /^gpt-\d+(?:\.\d+)*(?:-.+)?$/i.test(model.id),
|
|
84
|
+
reason: "newest GPT model available through GitHub Copilot",
|
|
85
|
+
},
|
|
73
86
|
];
|
|
74
87
|
const RESEARCH_MODEL_FALLBACK_PREFERENCES = [
|
|
75
88
|
exactResearchModel("opencode/kimi-k2.7-code", "good OpenCode Zen fallback for coding and research work"),
|
|
@@ -188,7 +201,7 @@ function compareCurrentModelFamily(left, right) {
|
|
|
188
201
|
return modelSpec(left).localeCompare(modelSpec(right));
|
|
189
202
|
}
|
|
190
203
|
function currentFamilyPreference(model) {
|
|
191
|
-
const anthropic = /^claude-(opus|sonnet)-(\d+(
|
|
204
|
+
const anthropic = /^claude-(opus|sonnet)-(\d+(?:[.-]\d+)*)$/i.exec(model.id);
|
|
192
205
|
if (anthropic) {
|
|
193
206
|
const family = anthropic[1].toLowerCase();
|
|
194
207
|
const parsedVersion = parseClaudeVersion(anthropic[2]);
|
|
@@ -202,7 +215,7 @@ function currentFamilyPreference(model) {
|
|
|
202
215
|
};
|
|
203
216
|
}
|
|
204
217
|
const openAi = /^gpt-(\d+(?:\.\d+)*)(?:-(.+))?$/i.exec(model.id);
|
|
205
|
-
if (openAi &&
|
|
218
|
+
if (openAi && ["openai", "openai-codex", "opencode", "github-copilot"].includes(model.provider)) {
|
|
206
219
|
const suffix = openAi[2]?.toLowerCase();
|
|
207
220
|
return {
|
|
208
221
|
family: `${model.provider}/gpt`,
|
|
@@ -236,7 +249,7 @@ function currentFamilyPreference(model) {
|
|
|
236
249
|
return undefined;
|
|
237
250
|
}
|
|
238
251
|
function parseClaudeVersion(rawVersion) {
|
|
239
|
-
const rawParts = rawVersion.split(
|
|
252
|
+
const rawParts = rawVersion.split(/[.-]/);
|
|
240
253
|
if (rawParts.length >= 2 && /^\d{8}$/.test(rawParts[rawParts.length - 1])) {
|
|
241
254
|
const baseParts = rawParts.slice(0, -1).map(Number);
|
|
242
255
|
return {
|
|
@@ -134,7 +134,11 @@ export function registerAlphaTools(pi: ExtensionAPI, signedIn = isLoggedIn()): v
|
|
|
134
134
|
question: Type.String({ description: "Question about the paper." }),
|
|
135
135
|
}),
|
|
136
136
|
async execute(_toolCallId, params) {
|
|
137
|
-
const
|
|
137
|
+
const arxivId = ARXIV_ID.exec(params.paper.trim())?.[1];
|
|
138
|
+
const result = await withPaperFallback(() => askPaper(params.paper, params.question), arxivId ? {
|
|
139
|
+
source: "arXiv metadata and abstract; the question was not answered, so answer it from the abstract or read the full text with fetch_content on the arXiv PDF",
|
|
140
|
+
run: () => searchArxiv({ query: arxivId }),
|
|
141
|
+
} : undefined);
|
|
138
142
|
return { content: [{ type: "text", text: formatText(result) }], details: result };
|
|
139
143
|
},
|
|
140
144
|
});
|
|
@@ -167,13 +167,16 @@ async function getArxivPapers(query: string): Promise<Record<string, unknown>> {
|
|
|
167
167
|
const feed = recordValue(xmlParser.parse(xml).feed);
|
|
168
168
|
const byId = new Map<string, Record<string, unknown>>();
|
|
169
169
|
for (const row of arxivFeedEntries(feed).map(parseArxivEntry).filter((item): item is Record<string, unknown> => Boolean(item))) {
|
|
170
|
-
if (row.arxiv_id)
|
|
170
|
+
if (row.arxiv_id) {
|
|
171
|
+
const bare = String(row.arxiv_id);
|
|
172
|
+
const latest = byId.get(bare);
|
|
173
|
+
if (!latest || Number(row.version) >= Number(latest.version)) byId.set(bare, row);
|
|
174
|
+
}
|
|
171
175
|
if (row.id_versioned) byId.set(String(row.id_versioned), row);
|
|
172
176
|
}
|
|
173
177
|
const seen = new Map<Record<string, unknown>, string>();
|
|
174
178
|
for (const id of ids) {
|
|
175
|
-
const
|
|
176
|
-
const row = byId.get(id) ?? byId.get(bare);
|
|
179
|
+
const row = byId.get(id);
|
|
177
180
|
if (!row) notFound.push(id);
|
|
178
181
|
else if (seen.has(row)) duplicates.push({ requested: id, resolved_as: seen.get(row) });
|
|
179
182
|
else {
|
|
@@ -385,7 +385,14 @@ async function searchSemanticScholar(params: ScienceDatabaseSearchParams): Promi
|
|
|
385
385
|
const OPENALEX_LOOKUP_QUERY =
|
|
386
386
|
/^(?:openalex_\w+|rate-limit|authors?|author-search|sources?|venues?|citations?|references?|work|detail)(?::|\s|$)|^(?:W\d+|doi:\s*10\.|10\.\S+\/)/i;
|
|
387
387
|
|
|
388
|
+
// Each request aborts after REQUEST_TIMEOUT_MS, which fetch reports as a bare
|
|
389
|
+
// "This operation was aborted".
|
|
390
|
+
function isTimeout(error: unknown): boolean {
|
|
391
|
+
return error instanceof Error && error.name === "AbortError";
|
|
392
|
+
}
|
|
393
|
+
|
|
388
394
|
function isIndexUnavailable(error: unknown): boolean {
|
|
395
|
+
if (isTimeout(error)) return true;
|
|
389
396
|
if (error instanceof ScienceDatabaseRequestError) return error.status === 429 || error.status >= 500;
|
|
390
397
|
return error instanceof Error && /^OpenAlex request failed: (?:429|5\d\d)\b/.test(error.message);
|
|
391
398
|
}
|
|
@@ -469,7 +476,13 @@ export function registerScienceDatabaseTools(pi: ExtensionAPI): void {
|
|
|
469
476
|
], { description: "PubMed sort order. For semanticscholar, prefer the default citation-count sort; use pub_date (newest first) only when recency matters. relevance falls back to the default when the anonymous pool is rate-limited. Ignored for other sources." })),
|
|
470
477
|
}),
|
|
471
478
|
async execute(_toolCallId, params) {
|
|
472
|
-
|
|
479
|
+
let result: Record<string, unknown>;
|
|
480
|
+
try {
|
|
481
|
+
result = await scienceDatabaseSearch(params as ScienceDatabaseSearchParams);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
if (!isTimeout(error)) throw error;
|
|
484
|
+
throw new Error(`${params.source} did not respond within ${REQUEST_TIMEOUT_MS / 1000} s. Retry, or search another source.`);
|
|
485
|
+
}
|
|
473
486
|
return {
|
|
474
487
|
content: [{ type: "text", text: formatText(result) }],
|
|
475
488
|
details: result,
|
package/metadata/commands.mjs
CHANGED
|
@@ -147,7 +147,7 @@ export const cliCommandSections = [
|
|
|
147
147
|
];
|
|
148
148
|
|
|
149
149
|
export const legacyFlags = [
|
|
150
|
-
{ usage: '--prompt "<text>"', description: "Run one prompt and exit." },
|
|
150
|
+
{ usage: '--prompt, -p "<text>"', description: "Run one prompt and exit." },
|
|
151
151
|
{ usage: "--alpha-login", description: "Sign in to alphaXiv and exit." },
|
|
152
152
|
{ usage: "--alpha-logout", description: "Clear alphaXiv auth and exit." },
|
|
153
153
|
{ usage: "--alpha-status", description: "Show alphaXiv auth status and exit." },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companion-ai/feynman",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.26",
|
|
4
4
|
"description": "Research-first CLI agent built on Pi and alphaXiv",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -82,13 +82,13 @@
|
|
|
82
82
|
"fast-xml-parser": "5.11.1",
|
|
83
83
|
"pi-btw": "0.7.1",
|
|
84
84
|
"pi-docparser": "4.0.0",
|
|
85
|
-
"pi-subagents": "0.
|
|
86
|
-
"pi-web-access": "0.
|
|
85
|
+
"pi-subagents": "0.76.0",
|
|
86
|
+
"pi-web-access": "0.36.0",
|
|
87
87
|
"posthog-node": "^5.53.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
-
"@earendil-works/pi-ai": "1.0.
|
|
91
|
-
"@earendil-works/pi-coding-agent": "1.0.
|
|
90
|
+
"@earendil-works/pi-ai": "1.0.2",
|
|
91
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
92
92
|
"@earendil-works/pi-tui": "*",
|
|
93
93
|
"typebox": "*"
|
|
94
94
|
},
|