@companion-ai/feynman 0.5.41 → 0.5.42

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.
@@ -2,5 +2,6 @@
2
2
  "theme": "feynman",
3
3
  "quietStartup": true,
4
4
  "collapseChangelog": true,
5
- "editorPaddingX": 1
5
+ "editorPaddingX": 1,
6
+ "showHardwareCursor": true
6
7
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
2
+ "$schema": "https://pi.dev/schemas/theme.schema.json",
3
3
  "name": "feynman",
4
4
  "vars": {
5
5
  "ink": "#d3c6aa",
package/RELEASES.md CHANGED
@@ -6,6 +6,21 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v0.5.42 - 2026-10-13
10
+
11
+ - **Pi 1.2.0 and pi-subagents 0.78.0.**
12
+ - With an OpenRouter key, `/model` lists only the models that key can use, with their real context sizes and prices.
13
+ - "The upstream provider returned an error" is retried instead of ending the turn, and Anthropic requests rejected for too many strict tools are retried once.
14
+ - `feynman -p` warns and exits 1 when the answer was cut off at the model's output limit.
15
+ - On Windows, reading or writing saved logins no longer fails with `EPERM` while an old lock is cleaned up.
16
+ - Pressing `Esc` keeps a research run stopped; a subagent finishing a moment later no longer restarts it.
17
+ - Reports are no longer overwritten when a subagent run is resumed, and a timed-out subagent's partial output reaches the lead agent.
18
+ - The `workflowScript contains dynamic child launches` warning no longer prints on every parallel run.
19
+ - **`feynman model login` offers every provider Pi supports with an API key.** That is 41 providers instead of Feynman's own list of 16, adding DeepSeek, Fireworks, Together, NVIDIA, Google Vertex, Cloudflare, Qwen and others. Pi now asks for what each provider needs, so Cloudflare's extra fields and Bedrock's profile or bearer-token options work. Pressing Enter on an empty key no longer saves an empty credential.
20
+ - **Turning off Feynman's telemetry also turns off Pi's.** `FEYNMAN_TELEMETRY=off` (or `DO_NOT_TRACK=1`) now sets Pi's own `PI_TELEMETRY=0` for its install reporting.
21
+ - Feynman now honors Pi's own `sessionDir` setting and `PI_CODING_AGENT_SESSION_DIR` when you don't pass `--session-dir`.
22
+ - `feynman model set` and the default-model check use Pi's settings API, so they match what Pi launches with (including trusted project settings).
23
+
9
24
  ## v0.5.41 - 2026-10-12
10
25
 
11
26
  - **A fresh `npm install` works again.** Pi 1.2.0, released today, made new npm installs of Feynman 0.5.40 fail on every command except `-v` (`does not provide an export named 'MCP_TYPESCRIPT_PREAMBLE'`). Pi 1.1.0 asks for its own sub-packages with open version ranges, so npm pulled Pi 1.2.0's copies. Feynman now pins those sub-packages to the Pi version it ships. The curl and PowerShell installers were not affected.
package/dist/cli.js CHANGED
@@ -29,6 +29,7 @@ import { fetchLatestFeynmanVersion, getFeynmanUpgradeLines, isNewerVersion } fro
29
29
  import { runDoctor, runStatus } from "./setup/doctor.js";
30
30
  import { setupPreviewDependencies } from "./setup/preview.js";
31
31
  import { runSetup } from "./setup/setup.js";
32
+ import { SettingsManager } from "@earendil-works/pi-coding-agent";
32
33
  import { noModelMessage } from "./setup/prompts.js";
33
34
  import { captureTelemetryEvent, captureTelemetryException, getCliTelemetryMetadata, initializePostHogTelemetry, shutdownPostHogTelemetry, telemetryErrorProperties, telemetryFirstRunNotice, } from "./telemetry/posthog.js";
34
35
  import { ASH, printAsciiHeader, printInfo, printPanel, printSection, RESET, SAGE } from "./ui/terminal.js";
@@ -516,7 +517,12 @@ async function runMain(input) {
516
517
  throw new Error("Unable to determine the installed Feynman version.");
517
518
  }
518
519
  const workingDir = resolve(values.cwd ?? process.cwd());
519
- const sessionDir = resolve(values["session-dir"] ?? getDefaultSessionDir(feynmanHome));
520
+ // Pi's order: --session-dir, then PI_CODING_AGENT_SESSION_DIR, then the sessionDir
521
+ // setting; Feynman's own default comes last.
522
+ const sessionDir = resolve(workingDir, values["session-dir"] ??
523
+ process.env.PI_CODING_AGENT_SESSION_DIR ??
524
+ SettingsManager.create(workingDir, feynmanAgentDir).getSessionDir() ??
525
+ getDefaultSessionDir(feynmanHome));
520
526
  const feynmanSettingsPath = resolve(feynmanAgentDir, "settings.json");
521
527
  const feynmanAuthPath = resolve(feynmanAgentDir, "auth.json");
522
528
  const { defaultThinkingLevel, launchThinkingLevel } = resolveThinkingConfig(values.thinking ?? process.env.FEYNMAN_THINKING);
@@ -1,9 +1,8 @@
1
1
  import { SettingsManager } from "@earendil-works/pi-coding-agent";
2
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFileSync } from "node:fs";
3
3
  import { exec as execCallback } from "node:child_process";
4
4
  import { dirname } from "node:path";
5
5
  import { promisify } from "node:util";
6
- import { readJson } from "../pi/settings.js";
7
6
  import { promptChoice, promptSecret, promptSelect, promptText, SetupCancelledError, validateApiKeyInput } from "../setup/prompts.js";
8
7
  import { openUrl } from "../system/open-url.js";
9
8
  import { printInfo, printSection, printSuccess, printWarning } from "../ui/terminal.js";
@@ -54,19 +53,32 @@ async function selectOAuthProvider(authPath, action) {
54
53
  }
55
54
  return selection;
56
55
  }
57
- function resolveApiKeyProvider(input) {
56
+ // Endpoints Pi has no API-key sign-in for: Feynman writes their models.json entry.
57
+ const LOCAL_API_KEY_SETUPS = MODEL_API_KEY_PROVIDERS.filter((provider) => ["lm-studio", "litellm", "__custom__"].includes(provider.id));
58
+ // Every provider Pi can sign in to with an API key (its own list, labels and
59
+ // prompts), plus Feynman's local and custom endpoint setups.
60
+ export async function listApiKeyProviders(authPath) {
61
+ const envVars = new Map(MODEL_API_KEY_PROVIDERS.map((provider) => [provider.id, provider.envVar]));
62
+ const piProviders = (await createModelRuntime(authPath))
63
+ .getProviders()
64
+ .filter((provider) => provider.auth.apiKey)
65
+ .map((provider) => ({ id: provider.id, label: provider.name, envVar: envVars.get(provider.id) }))
66
+ .sort((left, right) => left.label.localeCompare(right.label));
67
+ return [...piProviders, ...LOCAL_API_KEY_SETUPS];
68
+ }
69
+ async function resolveApiKeyProvider(authPath, input) {
58
70
  const normalizedInput = normalizeProviderId(input);
59
71
  if (!normalizedInput) {
60
72
  return undefined;
61
73
  }
62
- return MODEL_API_KEY_PROVIDERS.find((provider) => provider.id === normalizedInput);
74
+ return (await listApiKeyProviders(authPath)).find((provider) => provider.id === normalizedInput);
63
75
  }
64
76
  export async function resolveModelProviderForCommand(authPath, input) {
65
77
  const oauthProvider = await resolveOAuthProvider(authPath, input);
66
78
  if (oauthProvider) {
67
79
  return { kind: "oauth", id: oauthProvider.id };
68
80
  }
69
- const apiKeyProvider = resolveApiKeyProvider(input);
81
+ const apiKeyProvider = await resolveApiKeyProvider(authPath, input);
70
82
  if (apiKeyProvider) {
71
83
  return { kind: "api-key", id: apiKeyProvider.id };
72
84
  }
@@ -84,14 +96,15 @@ function apiKeyProviderHint(provider) {
84
96
  }
85
97
  return provider.envVar ?? provider.id;
86
98
  }
87
- async function selectApiKeyProvider() {
88
- const options = MODEL_API_KEY_PROVIDERS.map((provider) => ({
99
+ async function selectApiKeyProvider(authPath) {
100
+ const providers = await listApiKeyProviders(authPath);
101
+ const options = providers.map((provider) => ({
89
102
  value: provider,
90
103
  label: provider.label,
91
104
  hint: apiKeyProviderHint(provider),
92
105
  }));
93
106
  options.push({ value: "cancel", label: "Cancel" });
94
- const defaultProvider = MODEL_API_KEY_PROVIDERS.find((provider) => provider.id === "openai") ?? MODEL_API_KEY_PROVIDERS[0];
107
+ const defaultProvider = providers.find((provider) => provider.id === "openai") ?? providers[0];
95
108
  const selection = await promptSelect("Choose an API-key provider:", options, defaultProvider);
96
109
  if (selection === "cancel") {
97
110
  return undefined;
@@ -501,36 +514,6 @@ async function verifyCustomProvider(setup, authPath) {
501
514
  }
502
515
  printInfo("Verification: skipped network probe for this API mode.");
503
516
  }
504
- async function verifyBedrockCredentialChain() {
505
- const { defaultProvider } = await import("@aws-sdk/credential-provider-node");
506
- const credentials = await defaultProvider({})();
507
- if (!credentials?.accessKeyId || !credentials?.secretAccessKey) {
508
- throw new Error("AWS credential chain resolved without usable Bedrock credentials.");
509
- }
510
- }
511
- async function configureBedrockProvider(authPath) {
512
- printSection("AWS Credentials: Amazon Bedrock");
513
- printInfo("Feynman will verify the AWS SDK credential chain used by Pi's Bedrock provider.");
514
- printInfo("Supported sources include AWS_PROFILE, ~/.aws credentials/config, SSO, ECS/IRSA, and EC2 instance roles.");
515
- try {
516
- await verifyBedrockCredentialChain();
517
- const modelRuntime = await createModelRuntime(authPath);
518
- if (!(await modelRuntime.checkAuth("amazon-bedrock"))) {
519
- throw new Error("Pi could not resolve the verified AWS credential chain for Amazon Bedrock.");
520
- }
521
- printSuccess("Verified the AWS credential chain used by Amazon Bedrock.");
522
- printInfo("Use `feynman model list` to see available Bedrock models.");
523
- return true;
524
- }
525
- catch (error) {
526
- printWarning(`AWS credential verification failed: ${error instanceof Error ? error.message : String(error)}`);
527
- printInfo("Configure AWS credentials first, for example:");
528
- printInfo(" export AWS_PROFILE=default");
529
- printInfo(" # or set AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY");
530
- printInfo(" # or use an EC2/ECS/IRSA role with valid Bedrock access");
531
- return false;
532
- }
533
- }
534
517
  async function maybeSetRecommendedDefaultModel(settingsPath, authPath) {
535
518
  if (!settingsPath) {
536
519
  return;
@@ -546,7 +529,7 @@ async function maybeSetRecommendedDefaultModel(settingsPath, authPath) {
546
529
  }
547
530
  }
548
531
  async function configureApiKeyProvider(authPath, providerId) {
549
- const provider = providerId ? resolveApiKeyProvider(providerId) : await selectApiKeyProvider();
532
+ const provider = providerId ? await resolveApiKeyProvider(authPath, providerId) : await selectApiKeyProvider(authPath);
550
533
  if (!provider) {
551
534
  if (providerId) {
552
535
  throw new Error(`Unknown API-key model provider: ${providerId}`);
@@ -554,9 +537,6 @@ async function configureApiKeyProvider(authPath, providerId) {
554
537
  printInfo("API key setup cancelled.");
555
538
  return false;
556
539
  }
557
- if (provider.id === "amazon-bedrock") {
558
- return configureBedrockProvider(authPath);
559
- }
560
540
  if (provider.id === "lm-studio") {
561
541
  const setup = await promptLmStudioProviderSetup();
562
542
  if (!setup) {
@@ -627,22 +607,34 @@ async function configureApiKeyProvider(authPath, providerId) {
627
607
  if (provider.envVar) {
628
608
  printInfo(`Tip: to avoid writing secrets to disk, set ${provider.envVar} in your shell or .env.`);
629
609
  }
630
- const apiKey = await promptSecret("Paste API key (leave empty to use env var instead)", undefined, validateApiKeyInput);
631
- if (!apiKey) {
632
- if (provider.envVar) {
633
- printInfo(`Set ${provider.envVar} and rerun setup (or run \`feynman model list\`).`);
634
- }
635
- else {
636
- printInfo("No API key provided.");
637
- }
638
- return false;
639
- }
610
+ // Pi asks for whatever this provider needs (a key, or e.g. Cloudflare's account
611
+ // and gateway, Bedrock's profile or token). Esc cancels.
640
612
  const modelRuntime = await createModelRuntime(authPath);
641
- await modelRuntime.login(provider.id, "api_key", {
642
- prompt: async () => apiKey,
643
- notify: () => undefined,
613
+ const interaction = terminalAuthInteraction(provider.label, new AbortController().signal);
614
+ const signedIn = await modelRuntime
615
+ .login(provider.id, "api_key", {
616
+ ...interaction,
617
+ prompt: async (prompt) => {
618
+ if (prompt.type !== "secret")
619
+ return interaction.prompt(prompt);
620
+ const secret = await promptSecret(prompt.message, prompt.signal, validateApiKeyInput);
621
+ // An empty key would be saved as a credential that fails every request.
622
+ if (!secret)
623
+ throw new SetupCancelledError("no API key entered");
624
+ return secret;
625
+ },
626
+ })
627
+ .then(() => true)
628
+ .catch((error) => {
629
+ if (!(error instanceof SetupCancelledError))
630
+ throw error;
631
+ if (provider.envVar)
632
+ printInfo(`Or set ${provider.envVar} and run feynman again.`);
633
+ return false;
644
634
  });
645
- printSuccess(`Saved API key for ${provider.id} in auth storage.`);
635
+ if (!signedIn)
636
+ return false;
637
+ printSuccess(`Saved ${provider.label} credentials in auth storage.`);
646
638
  const baseUrl = await promptText("Base URL override (optional, include /v1 for OpenAI-compatible endpoints)", "");
647
639
  if (baseUrl) {
648
640
  const modelsJsonPath = getModelsJsonPath(authPath);
@@ -683,12 +675,12 @@ async function resolveAvailableModelSpec(authPath, input) {
683
675
  }
684
676
  return undefined;
685
677
  }
678
+ // The default model Pi will launch with, read through Pi's SettingsManager.
686
679
  export function getCurrentModelSpec(settingsPath) {
687
- const settings = readJson(settingsPath);
688
- if (typeof settings.defaultProvider === "string" && typeof settings.defaultModel === "string") {
689
- return `${settings.defaultProvider}/${settings.defaultModel}`;
690
- }
691
- return undefined;
680
+ const settings = SettingsManager.create(process.cwd(), dirname(settingsPath));
681
+ const provider = settings.getDefaultProvider();
682
+ const model = settings.getDefaultModel();
683
+ return provider && model ? `${provider}/${model}` : undefined;
692
684
  }
693
685
  export async function printModelList(settingsPath, authPath) {
694
686
  const status = await collectModelStatus(settingsPath, authPath);
@@ -758,32 +750,9 @@ export function describeOAuthLoginFailure(providerId, error) {
758
750
  async function refreshProviderModels(modelRuntime, providerId) {
759
751
  await modelRuntime.refresh({ providers: [providerId], allowNetwork: true, signal: AbortSignal.timeout(15_000) }).catch(() => undefined);
760
752
  }
761
- export async function loginModelProvider(authPath, providerId, settingsPath) {
762
- if (providerId) {
763
- const resolvedProvider = await resolveModelProviderForCommand(authPath, providerId);
764
- if (!resolvedProvider) {
765
- throw new Error(`Unknown model provider: ${providerId}`);
766
- }
767
- if (resolvedProvider.kind === "api-key") {
768
- const configured = await configureApiKeyProvider(authPath, resolvedProvider.id);
769
- if (configured) {
770
- await maybeSetRecommendedDefaultModel(settingsPath, authPath);
771
- }
772
- return configured;
773
- }
774
- }
775
- const provider = providerId ? await resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "login");
776
- if (!provider) {
777
- if (providerId) {
778
- throw new Error(`Unknown model provider: ${providerId}`);
779
- }
780
- printInfo("Login cancelled.");
781
- return false;
782
- }
783
- const modelRuntime = await createModelRuntime(authPath);
784
- const abortController = new AbortController();
785
- const deviceId = await getOrCreateDeviceId(authPath);
786
- await modelRuntime.login(provider.id, "oauth", {
753
+ // Runs Pi's own login prompts (selects, secrets, text, browser URLs) in the terminal.
754
+ function terminalAuthInteraction(providerLabel, signal) {
755
+ return {
787
756
  prompt: async (prompt) => {
788
757
  if (prompt.type === "select") {
789
758
  return promptSelect(prompt.message, prompt.options.map((option) => ({
@@ -802,7 +771,7 @@ export async function loginModelProvider(authPath, providerId, settingsPath) {
802
771
  },
803
772
  notify: (event) => {
804
773
  if (event.type === "auth_url") {
805
- printSection(`Login: ${provider.name ?? provider.id}`);
774
+ printSection(`Login: ${providerLabel}`);
806
775
  if (openUrl(event.url)) {
807
776
  printInfo("Opened the login URL in your browser.");
808
777
  }
@@ -816,7 +785,7 @@ export async function loginModelProvider(authPath, providerId, settingsPath) {
816
785
  return;
817
786
  }
818
787
  if (event.type === "device_code") {
819
- printSection(`Login: ${provider.name ?? provider.id}`);
788
+ printSection(`Login: ${providerLabel}`);
820
789
  printInfo(`Visit ${event.verificationUri} and enter code: ${event.userCode}`);
821
790
  openUrl(event.verificationUri);
822
791
  return;
@@ -825,8 +794,35 @@ export async function loginModelProvider(authPath, providerId, settingsPath) {
825
794
  printInfo(event.message);
826
795
  }
827
796
  },
828
- signal: abortController.signal,
829
- }, { getDeviceId: () => deviceId }).catch((error) => {
797
+ signal,
798
+ };
799
+ }
800
+ export async function loginModelProvider(authPath, providerId, settingsPath) {
801
+ if (providerId) {
802
+ const resolvedProvider = await resolveModelProviderForCommand(authPath, providerId);
803
+ if (!resolvedProvider) {
804
+ throw new Error(`Unknown model provider: ${providerId}`);
805
+ }
806
+ if (resolvedProvider.kind === "api-key") {
807
+ const configured = await configureApiKeyProvider(authPath, resolvedProvider.id);
808
+ if (configured) {
809
+ await maybeSetRecommendedDefaultModel(settingsPath, authPath);
810
+ }
811
+ return configured;
812
+ }
813
+ }
814
+ const provider = providerId ? await resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "login");
815
+ if (!provider) {
816
+ if (providerId) {
817
+ throw new Error(`Unknown model provider: ${providerId}`);
818
+ }
819
+ printInfo("Login cancelled.");
820
+ return false;
821
+ }
822
+ const modelRuntime = await createModelRuntime(authPath);
823
+ const abortController = new AbortController();
824
+ const deviceId = await getOrCreateDeviceId(authPath);
825
+ await modelRuntime.login(provider.id, "oauth", terminalAuthInteraction(provider.name ?? provider.id, abortController.signal), { getDeviceId: () => deviceId }).catch((error) => {
830
826
  throw describeOAuthLoginFailure(provider.id, error);
831
827
  });
832
828
  printSuccess(`Model provider login complete: ${provider.id}`);
@@ -888,11 +884,10 @@ export async function setDefaultModelSpec(settingsPath, authPath, spec) {
888
884
  throw new Error(await describeUnavailableModelSpec(authPath, spec));
889
885
  }
890
886
  const [provider, ...rest] = resolvedSpec.split("/");
891
- const modelId = rest.join("/");
892
- const settings = readJson(settingsPath);
893
- settings.defaultProvider = provider;
894
- settings.defaultModel = modelId;
895
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
887
+ // Pi's SettingsManager merges only the changed keys under its lock.
888
+ const settings = SettingsManager.create(process.cwd(), dirname(settingsPath));
889
+ settings.setDefaultModelAndProvider(provider, rest.join("/"));
890
+ await settings.flush();
896
891
  printSuccess(`Research default model set to ${resolvedSpec}`);
897
892
  }
898
893
  export async function selectDefaultModel(settingsPath, authPath) {
@@ -2,7 +2,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "n
2
2
  import { createRequire } from "node:module";
3
3
  import { delimiter, dirname, resolve, win32 } from "node:path";
4
4
  import { MERMAID_FALLBACK_PATHS, PANDOC_FALLBACK_PATHS, resolveBrowser, resolveExecutable, } from "../system/executables.js";
5
- import { getPostHogChildEnv } from "../telemetry/posthog.js";
5
+ import { getPostHogChildEnv, isTelemetryDisabled } from "../telemetry/posthog.js";
6
6
  // Pi packages shipped as Feynman dependencies and loaded from their install
7
7
  // paths through settings.json `packages` (Pi's documented local-path source).
8
8
  export const BUNDLED_PI_PACKAGES = ["pi-subagents", "pi-web-access", "pi-docparser", "pi-btw"];
@@ -156,8 +156,9 @@ export function buildPiEnv(options, executables) {
156
156
  FEYNMAN_BIN_PATH: getFeynmanCliBinPath(options.appRoot),
157
157
  PI_CODING_AGENT_DIR: options.feynmanAgentDir,
158
158
  PANDOC_PATH: pandocPath,
159
- PI_HARDWARE_CURSOR: process.env.PI_HARDWARE_CURSOR ?? "1",
160
159
  PI_SKIP_VERSION_CHECK: process.env.PI_SKIP_VERSION_CHECK ?? "1",
160
+ // A Feynman telemetry opt-out also turns off Pi's install telemetry (docs/environment-variables.md).
161
+ ...(isTelemetryDisabled() && process.env.PI_TELEMETRY === undefined ? { PI_TELEMETRY: "0" } : {}),
161
162
  MERMAID_CLI_PATH: mermaidPath,
162
163
  PUPPETEER_EXECUTABLE_PATH: browserPath,
163
164
  ...getPostHogChildEnv(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.5.41",
3
+ "version": "0.5.42",
4
4
  "description": "The open source AI research agent.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -79,21 +79,20 @@
79
79
  "dependencies": {
80
80
  "@clack/prompts": "^1.8.1",
81
81
  "@companion-ai/alpha-hub": "0.1.7",
82
- "@earendil-works/chord": "1.1.0",
83
- "@earendil-works/pi-codemode": "1.1.0",
84
- "@earendil-works/pi-mcp": "1.1.0",
85
- "@earendil-works/pi-telemetry": "1.1.0",
82
+ "@earendil-works/pi-codemode": "1.2.0",
83
+ "@earendil-works/pi-mcp": "1.2.0",
84
+ "@earendil-works/pi-telemetry": "1.2.0",
86
85
  "fast-xml-parser": "5.11.1",
87
86
  "pi-btw": "0.8.0",
88
87
  "pi-docparser": "4.0.0",
89
- "pi-subagents": "0.77.0",
88
+ "pi-subagents": "0.78.0",
90
89
  "pi-web-access": "0.39.0",
91
90
  "posthog-node": "^5.53.0"
92
91
  },
93
92
  "peerDependencies": {
94
- "@earendil-works/pi-agent-core": "1.1.0",
95
- "@earendil-works/pi-ai": "1.1.0",
96
- "@earendil-works/pi-coding-agent": "1.1.0",
93
+ "@earendil-works/pi-agent-core": "1.2.0",
94
+ "@earendil-works/pi-ai": "1.2.0",
95
+ "@earendil-works/pi-coding-agent": "1.2.0",
97
96
  "@earendil-works/pi-tui": "*",
98
97
  "typebox": "*"
99
98
  },