@companion-ai/feynman 0.5.22 → 0.5.24

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.
@@ -17,6 +17,7 @@ Tools:
17
17
 
18
18
  Delegation:
19
19
  - Use the `researcher`, `writer`, `verifier`, and `reviewer` subagents when decomposition reduces context pressure or parallelizes evidence gathering. Keep delegation internal; do not make the user manage it.
20
+ - To run several subagents in one call, write the workflow script to `outputs/.plans/<slug>-workflow.js`, and only after that write has succeeded, in a later tool call, call `subagent` with `workflow` set to that path. Do not use `workflow: true`: it needs a fenced block in the visible reply and fails when the block is only in your reasoning.
20
21
  - A workflow is finished only when its final artifact is on disk. Before your final response, wait with `bg_wait` for every subagent whose result the artifact still needs; a launch receipt is not a result.
21
22
  - For long workflows, write the plan to disk early and keep its task ledger and verification log current. If `CHANGELOG.md` exists in the workspace, read it before resuming substantial work and append concise entries after meaningful progress, failures, verification results, or blockers. Do not create it for one-shot tasks.
22
23
  - Prefer the smallest investigation or experiment that can reduce uncertainty. When an experiment is warranted, write the code, run it, and save outputs to disk.
package/RELEASES.md CHANGED
@@ -6,6 +6,16 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v0.5.24 - 2026-10-03
10
+
11
+ - **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.
12
+
13
+ ## v0.5.23 - 2026-10-02
14
+
15
+ - **arXiv and PubMed rate limits.** arXiv asks for one request every three seconds, and parallel lookups got `429`; arXiv requests now run at that pace and a 429 is retried once. A PubMed `429`, which a subagent in another process or a shared IP can still trigger, is retried once after a pause.
16
+ - Running several subagents at once from any workflow no longer fails with "workflow: true requires exactly one ```js workflow fenced block": Feynman now always writes the workflow script to a file and runs it by path, as `/deepresearch` already did.
17
+ - Pi 1.0 is not in this release: pi-subagents 0.74.0 cannot start background subagents on it. The fix is merged upstream (nicobailon/pi-subagents#2634) and Feynman will move to Pi 1.0 once it is released.
18
+
9
19
  ## v0.5.22 - 2026-10-01
10
20
 
11
21
  - **Launch warnings and the first-run telemetry notice show inside the session.** Feynman printed them to the terminal just before starting Pi, where Pi 1.0's fullscreen interface (coming in a later Feynman release) hides them for the whole session. They now appear in Pi's own interface: the Windows "No Bash found" warning, the small-local-model warning for research workflows, and the one-time telemetry disclosure. A one-shot `--prompt` run still prints them to stderr.
package/dist/cli.js CHANGED
@@ -467,6 +467,7 @@ async function runMain(input) {
467
467
  "new-session": { type: "boolean" },
468
468
  "no-session": { type: "boolean" },
469
469
  "no-themes": { type: "boolean" },
470
+ "tui-mode": { type: "string" },
470
471
  prompt: { type: "string" },
471
472
  resume: { type: "boolean", short: "r" },
472
473
  "service-tier": { type: "string" },
@@ -694,7 +695,11 @@ async function runMain(input) {
694
695
  explicitModelSpec,
695
696
  resumeRecentSession,
696
697
  // ACP adapters such as pi-acp pass --no-themes with --mode rpc.
697
- piArgs: values["no-themes"] ? [...piArgs, "--no-themes"] : piArgs,
698
+ piArgs: [
699
+ ...piArgs,
700
+ ...(values["no-themes"] ? ["--no-themes"] : []),
701
+ ...(values["tui-mode"] ? ["--tui-mode", values["tui-mode"]] : []),
702
+ ],
698
703
  preLaunchNotice,
699
704
  ...promptOptions,
700
705
  });
@@ -1,5 +1,7 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
2
 
3
+ import { createRequestPacer } from "./request-pacer.js";
4
+
3
5
  type SearchParams = {
4
6
  query: string;
5
7
  };
@@ -27,20 +29,32 @@ function cleanQuery(query: string): string {
27
29
  return clean;
28
30
  }
29
31
 
32
+ // arXiv's API terms ask for no more than one request every three seconds, and
33
+ // parallel lookups got 429. Requests run one after another at that pace, and a
34
+ // 429 is retried once after the same wait.
35
+ const arxivPacer = createRequestPacer();
36
+ export const ARXIV_MIN_GAP_MS = 3000;
37
+
30
38
  async function fetchText(url: URL, accept: string): Promise<string> {
31
- const controller = new AbortController();
32
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
33
- try {
34
- const response = await fetch(url, {
35
- headers: { accept },
36
- signal: controller.signal,
39
+ for (let attempt = 0; ; attempt += 1) {
40
+ const result = await arxivPacer(ARXIV_MIN_GAP_MS, async () => {
41
+ const controller = new AbortController();
42
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
43
+ try {
44
+ const response = await fetch(url, {
45
+ headers: { accept },
46
+ signal: controller.signal,
47
+ });
48
+ if (response.status === 429 && attempt === 0) return undefined;
49
+ if (!response.ok) {
50
+ throw new Error(`${url.hostname} request failed: ${response.status} ${response.statusText}`);
51
+ }
52
+ return await response.text();
53
+ } finally {
54
+ clearTimeout(timeout);
55
+ }
37
56
  });
38
- if (!response.ok) {
39
- throw new Error(`${url.hostname} request failed: ${response.status} ${response.statusText}`);
40
- }
41
- return response.text();
42
- } finally {
43
- clearTimeout(timeout);
57
+ if (result !== undefined) return result;
44
58
  }
45
59
  }
46
60
 
@@ -98,23 +98,32 @@ function prune<T extends Record<string, unknown>>(record: T): Record<string, unk
98
98
 
99
99
  // The abort timer stays armed across the body read: fetch resolves on headers,
100
100
  // so clearing it earlier would leave a stalled body hanging forever.
101
+ // Pacing keeps one process under NCBI's limit, but subagents run in their own
102
+ // processes and an IP can be shared, so a 429 is retried once after a pause.
103
+ const NCBI_RETRY_MS = 1500;
104
+
101
105
  async function send<T>(url: URL, accept: string, read: (response: Response) => Promise<T>): Promise<T> {
102
- return withNcbiRateLimit(url, async () => {
103
- const controller = new AbortController();
104
- const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
105
- try {
106
- const response = await fetch(url, {
107
- headers: {
108
- accept,
109
- "user-agent": "feynman-pubmed-tools/1.0 (https://github.com/Companion-Inc/feynman)",
110
- },
111
- signal: controller.signal,
112
- });
113
- return await read(response);
114
- } finally {
115
- clearTimeout(timeout);
116
- }
117
- });
106
+ for (let attempt = 0; ; attempt += 1) {
107
+ const result = await withNcbiRateLimit(url, async (): Promise<{ retry: true } | { value: T }> => {
108
+ const controller = new AbortController();
109
+ const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
110
+ try {
111
+ const response = await fetch(url, {
112
+ headers: {
113
+ accept,
114
+ "user-agent": "feynman-pubmed-tools/1.0 (https://github.com/Companion-Inc/feynman)",
115
+ },
116
+ signal: controller.signal,
117
+ });
118
+ if (response.status === 429 && attempt === 0) return { retry: true };
119
+ return { value: await read(response) };
120
+ } finally {
121
+ clearTimeout(timeout);
122
+ }
123
+ });
124
+ if ("value" in result) return result.value;
125
+ await new Promise((resolve) => setTimeout(resolve, NCBI_RETRY_MS));
126
+ }
118
127
  }
119
128
 
120
129
  function assertOk(response: Response): void {
@@ -157,6 +157,7 @@ export const legacyFlags = [
157
157
  { usage: "--cwd <path>", description: "Set the working directory for tools." },
158
158
  { usage: "--session-dir <path>", description: "Set the session storage directory." },
159
159
  { usage: "--new-session", description: "Start a new persisted session." },
160
+ { usage: "--tui-mode <fullscreen|regular>", description: "Run the terminal UI fullscreen (default) or in the normal scrollback." },
160
161
  { usage: "--continue, -c", description: "Continue the most recent session (default for an interactive launch)." },
161
162
  { usage: "--resume, -r", description: "Pick a previous session to resume." },
162
163
  { usage: "--session <path|id>", description: "Open a specific session." },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.5.22",
3
+ "version": "0.5.24",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -80,15 +80,15 @@
80
80
  "@clack/prompts": "^1.8.1",
81
81
  "@companion-ai/alpha-hub": "0.1.6",
82
82
  "fast-xml-parser": "5.11.1",
83
- "pi-btw": "0.7.0",
83
+ "pi-btw": "0.7.1",
84
84
  "pi-docparser": "4.0.0",
85
- "pi-subagents": "0.74.0",
85
+ "pi-subagents": "0.75.0",
86
86
  "pi-web-access": "0.35.0",
87
87
  "posthog-node": "^5.53.0"
88
88
  },
89
89
  "peerDependencies": {
90
- "@earendil-works/pi-ai": "0.99.2",
91
- "@earendil-works/pi-coding-agent": "0.99.2",
90
+ "@earendil-works/pi-ai": "1.0.0",
91
+ "@earendil-works/pi-coding-agent": "1.0.0",
92
92
  "@earendil-works/pi-tui": "*",
93
93
  "typebox": "*"
94
94
  },
@@ -68,7 +68,7 @@ If subagents were chosen:
68
68
  - Keep `subagent` tool-call JSON small and valid.
69
69
  - Do not place multi-paragraph instructions inside the `subagent` JSON.
70
70
  - Use only supported `subagent` keys. Do not add extra keys such as `artifacts` unless the tool schema explicitly exposes them.
71
- - For parallel evidence gathering, write a workflow script that returns `await runs.all(...)` to `outputs/.plans/<slug>-workflow.js` with the write tool. After the write succeeds, call `subagent` with `workflow: "./outputs/.plans/<slug>-workflow.js"`, `async: true`, and `globalConcurrencyLimit: 4`. Each item needs a unique stable `key`, plus its agent, short task, and output path. Do not pass the script text inside the tool call.
71
+ - For parallel evidence gathering, write a workflow script that returns `await runs.all(...)` to `outputs/.plans/<slug>-workflow.js` with the write tool. After the write succeeds, in a later tool call (not in parallel with the write), call `subagent` with `workflow: "./outputs/.plans/<slug>-workflow.js"`, `async: true`, and `globalConcurrencyLimit: 4`. Each item needs a unique stable `key`, plus its agent, short task, and output path. Do not pass the script text inside the tool call.
72
72
  - Read the ordered result array and record each child's `ok`, error, and returned output/artifact paths. Ordinary child failures are collected by `runs.all`; validation or infrastructure failure can still fail the workflow. Do not assume every output exists.
73
73
  - Prefer broad guidance such as "use paper search and web search"; if a PDF parser or paper fetch fails, the researcher must continue from metadata, abstracts, and web sources and mark PDF parsing as blocked.
74
74
 
@@ -116,7 +116,7 @@ Briefly summarize: "Source is ~<chars> chars -> <N> chunks -> <N> researcher sub
116
116
 
117
117
  ### 3c. Dispatch researcher subagents
118
118
 
119
- Use one async workflow with a stable key for each chunk and a concurrency limit of four: write the script to `outputs/.plans/<slug>-workflow.js` with the write tool, then, after the write succeeds, call `subagent` with `{ "workflow": "./outputs/.plans/<slug>-workflow.js", "async": true, "globalConcurrencyLimit": 4 }`. Expand the example item below for the actual chunk inventory; retain the single-source restriction and explicit output declaration for every child.
119
+ Use one async workflow with a stable key for each chunk and a concurrency limit of four: write the script to `outputs/.plans/<slug>-workflow.js` with the write tool, then, after the write succeeds, in a later tool call, call `subagent` with `{ "workflow": "./outputs/.plans/<slug>-workflow.js", "async": true, "globalConcurrencyLimit": 4 }`. Expand the example item below for the actual chunk inventory; retain the single-source restriction and explicit output declaration for every child.
120
120
 
121
121
  ```js
122
122
  return await runs.all([