@companion-ai/feynman 0.5.7 → 0.5.9

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 CHANGED
@@ -6,6 +6,17 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v0.5.9 - 2026-09-27
10
+
11
+ - **Subagents keep their search tools in the foreground.** A `researcher` or `verifier` launched in the foreground failed at once because foreground children never load the parent's extensions, so `web_search`, `fetch_content`, and `feynman_science_database_search` were missing. Feynman now points pi-subagents' `defaultSubagentOnlyExtensions` setting at its research tools and pi-web-access, so every child has them, in the foreground or background.
12
+ - **No alphaXiv account, no alphaXiv search tools.** Without an account, every `alpha_search`, `alpha_get_paper`, `alpha_ask_paper`, and `alpha_read_code` call failed, and research runs retried them. They are now registered only when you are signed in; otherwise papers come from Semantic Scholar, OpenAlex, and the other literature databases. Start a new session after `feynman alpha login`.
13
+ - The Windows install docs now say Feynman needs Bash from Git for Windows for shell commands.
14
+
15
+ ## v0.5.8 - 2026-09-26
16
+
17
+ - `FEYNMAN_HOME=~` or `~/path` now means your home folder. Unexpanded `~` (common in `.env` files, Docker `ENV`, and Windows shells) created a literal `~` folder in the current directory.
18
+ - Starting two Feynman sessions at the same moment no longer crashes one of them with "Subagent config changed during settings normalization". The second start keeps the subagent config the first one wrote.
19
+
9
20
  ## v0.5.7 - 2026-09-26
10
21
 
11
22
  - When Feynman runs as a package inside another Pi setup, an extension error no longer appears on every turn ("Cannot set properties of undefined (setting 'current_date')") if another extension or the host leaves the system-prompt sections unset (#298, thanks @Oscar-Williams in #300).
@@ -1,8 +1,15 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
+ // Like Pi's own agent-dir variable: a leading ~ in FEYNMAN_HOME (unexpanded in
5
+ // .env files, Docker ENV, or Windows shells) means the home folder.
6
+ export function expandHomePath(path, home = homedir()) {
7
+ if (path === "~")
8
+ return home;
9
+ return /^~[\\/]/.test(path) ? join(home, path.slice(2)) : path;
10
+ }
4
11
  export function getFeynmanHome() {
5
- return resolve(process.env.FEYNMAN_HOME ?? homedir(), ".feynman");
12
+ return resolve(expandHomePath(process.env.FEYNMAN_HOME ?? homedir()), ".feynman");
6
13
  }
7
14
  export function getFeynmanAgentDir(home = getFeynmanHome()) {
8
15
  return resolve(home, "agent");
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { BUNDLED_PI_PACKAGES, getFeynmanPackageSources } from "./runtime.js";
3
+ import { BUNDLED_PI_PACKAGES, getFeynmanPackageSources, resolvePackageRoot } from "./runtime.js";
4
4
  import { choosePreferredModelRecord, getAvailableModelRecords } from "../model/catalog.js";
5
5
  // Written by Feynman <= 0.4.0 next to a subagent extension path it managed.
6
6
  const LEGACY_RESEARCHER_EXTENSION_MARKER = "_feynmanResearchToolsExtension";
@@ -150,6 +150,17 @@ function removeLegacyResearcherExtension(settings) {
150
150
  else
151
151
  delete researcher.subagentOnlyExtensions;
152
152
  }
153
+ // Foreground subagents run inside the parent process and never load its
154
+ // extensions, so researcher and verifier lost web and paper search there.
155
+ // pi-subagents loads these paths in every child session; they are rewritten on
156
+ // each launch because the install path changes between versions.
157
+ export function feynmanSubagentExtensions(appRoot) {
158
+ const webAccess = resolvePackageRoot(appRoot, "pi-web-access");
159
+ return [join(appRoot, "extensions", "research-tools.ts"), ...(webAccess ? [webAccess] : [])];
160
+ }
161
+ function isFeynmanSubagentExtension(path) {
162
+ return typeof path === "string" && (/[\\/]extensions[\\/]research-tools\.ts$/.test(path) || /[\\/]pi-web-access$/.test(path));
163
+ }
153
164
  export async function ensureFeynmanSettings(settingsPath, bundledSettingsPath, appRoot, defaultThinkingLevel, authPath) {
154
165
  // Validate before model discovery or either settings write. Invalid custom
155
166
  // configuration must not silently turn into an empty/default configuration.
@@ -173,6 +184,12 @@ export async function ensureFeynmanSettings(settingsPath, bundledSettingsPath, a
173
184
  if (isRecord(settings.subagents) && settings.subagents.agentExcludeDirs === undefined) {
174
185
  settings.subagents.agentExcludeDirs = ["~/.agents"];
175
186
  }
187
+ if (isRecord(settings.subagents)) {
188
+ const current = settings.subagents.defaultSubagentOnlyExtensions;
189
+ if (current === undefined || (Array.isArray(current) && current.every(isFeynmanSubagentExtension))) {
190
+ settings.subagents.defaultSubagentOnlyExtensions = feynmanSubagentExtensions(appRoot);
191
+ }
192
+ }
176
193
  if (!settings.defaultProvider || !settings.defaultModel) {
177
194
  const preferredModel = choosePreferredModelRecord(await getAvailableModelRecords(authPath));
178
195
  if (preferredModel) {
@@ -180,15 +197,20 @@ export async function ensureFeynmanSettings(settingsPath, bundledSettingsPath, a
180
197
  settings.defaultModel = preferredModel.id;
181
198
  }
182
199
  }
183
- if (subagentDefaults) {
184
- const current = existsSync(subagentDefaults.path) ? readFileSync(subagentDefaults.path, "utf8") : undefined;
185
- if (current !== subagentDefaults.original) {
186
- throw new Error(`Subagent config changed during settings normalization: ${subagentDefaults.path}. Retry without overwriting it.`);
187
- }
200
+ // If another process wrote the subagent config while models were being
201
+ // discovered, keep its version instead of overwriting it or failing startup.
202
+ const subagentCurrent = subagentDefaults && existsSync(subagentDefaults.path) ? readFileSync(subagentDefaults.path, "utf8") : undefined;
203
+ if (subagentDefaults && subagentCurrent === subagentDefaults.original) {
188
204
  mkdirSync(dirname(subagentDefaults.path), { recursive: true });
189
- writeFileSync(subagentDefaults.path, subagentDefaults.content, {
190
- encoding: "utf8", mode: 0o600, flag: subagentDefaults.original === undefined ? "wx" : "w",
191
- });
205
+ try {
206
+ writeFileSync(subagentDefaults.path, subagentDefaults.content, {
207
+ encoding: "utf8", mode: 0o600, flag: subagentDefaults.original === undefined ? "wx" : "w",
208
+ });
209
+ }
210
+ catch (error) {
211
+ if (error.code !== "EEXIST")
212
+ throw error;
213
+ }
192
214
  }
193
215
  const content = JSON.stringify(settings, null, 2) + "\n";
194
216
  if (content === existing?.source)
@@ -4,6 +4,7 @@ import {
4
4
  clearPaperAnnotation,
5
5
  disconnect,
6
6
  getPaper,
7
+ isLoggedIn,
7
8
  listPaperAnnotations,
8
9
  readPaperCode,
9
10
  searchPapers,
@@ -41,14 +42,18 @@ const paperSectionsSchema = Type.Unsafe<string[]>({
41
42
  }),
42
43
  });
43
44
 
44
- export function registerAlphaTools(pi: ExtensionAPI): void {
45
+ export function registerAlphaTools(pi: ExtensionAPI, signedIn = isLoggedIn()): void {
45
46
  // The alphaXiv MCP client keeps a connection open; close it so print and
46
47
  // JSON runs can exit once the session ends.
47
48
  pi.on("session_shutdown", async () => {
48
49
  await disconnect();
49
50
  });
51
+ // Search, paper, Q&A, and code tools need an alphaXiv account. Without one,
52
+ // every call failed, so leave them out and let the model use the other
53
+ // paper sources. Annotations are local and stay available.
54
+ const registerAccountTool: ExtensionAPI["registerTool"] = signedIn ? (tool) => pi.registerTool(tool) : () => {};
50
55
 
51
- pi.registerTool({
56
+ registerAccountTool({
52
57
  name: "alpha_search",
53
58
  label: "Alpha Search",
54
59
  description:
@@ -65,7 +70,7 @@ export function registerAlphaTools(pi: ExtensionAPI): void {
65
70
  },
66
71
  });
67
72
 
68
- pi.registerTool({
73
+ registerAccountTool({
69
74
  name: "alpha_get_paper",
70
75
  label: "Alpha Get Paper",
71
76
  description:
@@ -96,7 +101,7 @@ export function registerAlphaTools(pi: ExtensionAPI): void {
96
101
  },
97
102
  });
98
103
 
99
- pi.registerTool({
104
+ registerAccountTool({
100
105
  name: "alpha_ask_paper",
101
106
  label: "Alpha Ask Paper",
102
107
  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.",
@@ -140,7 +145,7 @@ export function registerAlphaTools(pi: ExtensionAPI): void {
140
145
  },
141
146
  });
142
147
 
143
- pi.registerTool({
148
+ registerAccountTool({
144
149
  name: "alpha_read_code",
145
150
  label: "Alpha Read Code",
146
151
  description: "Read files from a paper's GitHub repository. Use '/' for repo overview.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "license": "MIT",
6
6
  "type": "module",