Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,10 @@ jobs:
if: ${{ github.event_name != 'pull_request' }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
continue-on-error: ${{ matrix.node-version == '20.x' }}

strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
node-version: "20.x"
- os: ubuntu-latest
node-version: "24.x"
- os: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion apps/host-daemon/scripts/build-bundles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ async function main() {
bundle: true,
conditions: ["source"],
entryPoints: [target.entryPoint],
external: createNativeExternalPatterns(),
external: createNativeExternalPatterns({
bundledPackages: target.bundledPackages,
}),
format: "esm",
legalComments: "none",
minify: true,
Expand Down
1 change: 1 addition & 0 deletions apps/host-daemon/scripts/bundle-manifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const bundleTargets = [
},
{
banner: NODE_ESM_REQUIRE_BANNER,
bundledPackages: ["jiti"],
entryPoint: resolve(
workspaceRoot,
"packages",
Expand Down
3 changes: 2 additions & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"@better-auth/drizzle-adapter": "^1.6.23",
"@hono/node-server": "^1.19.11",
"@hono/node-ws": "^1.3.0",
"@mariozechner/pi-ai": "^0.70.5",
"@earendil-works/pi-ai": "^0.82.0",
"@opentelemetry/api": "1.9.1",
"@tailwindcss/node": "^4.3.0",
"@tailwindcss/oxide": "^4.3.0",
"better-auth": "^1.6.23",
Expand Down
7 changes: 4 additions & 3 deletions apps/server/src/assets/install-machine.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,17 @@ case "$(uname -s)" in
esac

if ! command -v node >/dev/null 2>&1; then
echo "bb-app requires Node.js 20.19 or newer (Node.js 22 LTS is recommended), but node is not on PATH." >&2
echo "bb-app requires Node.js 22.19, 24, or 26, but node is not on PATH." >&2
exit 1
fi
node_version=$(node -p 'process.versions.node')
node_supported=$(node -e '
const [major, minor] = process.versions.node.split(".").map(Number);
process.exit(major > 20 || (major === 20 && minor >= 19) ? 0 : 1);
const supported = (major === 22 && minor >= 19) || major === 24 || major === 26;
process.exit(supported ? 0 : 1);
' && echo yes || echo no)
if [ "$node_supported" != yes ]; then
echo "Node.js $node_version is too old; bb-app requires Node.js 20.19 or newer (Node.js 22 LTS is recommended)." >&2
echo "Node.js $node_version is unsupported; bb-app requires Node.js 22.19, 24, or 26." >&2
exit 1
fi
node_bin=$(command -v node)
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/services/ai/commit-message.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderTemplate } from "@bb/templates";
import type { LoggedWorkSessionDeps } from "../../types.js";
import { Type } from "@mariozechner/pi-ai";
import { Type } from "@earendil-works/pi-ai";
import { InferenceTimeoutError, inferenceComplete } from "./inference.js";
import { runtimeErrorLogFields } from "../lib/error-log-fields.js";

Expand Down
14 changes: 8 additions & 6 deletions apps/server/src/services/ai/inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import {
parseProviderModelConfig,
type ProviderModelInfo,
} from "@bb/config/inference-model";
import { complete, getModel, validateToolCall } from "@mariozechner/pi-ai";
import type { Static, TSchema, Tool, ToolCall } from "@mariozechner/pi-ai";
import { validateToolCall } from "@earendil-works/pi-ai";
import type { Static, TSchema, Tool, ToolCall } from "@earendil-works/pi-ai";
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
import type { AppDeps, LoggedWorkSessionDeps } from "../../types.js";
import { ApiError } from "../../errors.js";
import { runLiveCommandAndWait } from "../hosts/live-command-wait.js";
Expand All @@ -14,15 +15,16 @@ import { backsHostDaemonAiServices } from "./host-daemon-ai-provider.js";
type BaseInferenceDeps = Pick<AppDeps, "config" | "logger">;
type InferenceCompleteDeps = LoggedWorkSessionDeps;

const inferenceModels = builtinModels();

function getInferenceModel(
deps: BaseInferenceDeps,
): ReturnType<typeof getModel> | null {
): ReturnType<typeof inferenceModels.getModel> | null {
const modelInfo = parseProviderModelConfig({
name: "BB_INFERENCE",
value: deps.config.inferenceModel,
});
// @ts-expect-error — pi-ai overloads getModel per provider; our provider string is dynamic
const model = getModel(modelInfo.provider, modelInfo.modelId);
const model = inferenceModels.getModel(modelInfo.provider, modelInfo.modelId);
if (!model) {
deps.logger.warn(
{ provider: modelInfo.provider },
Expand Down Expand Up @@ -168,7 +170,7 @@ export async function inferenceComplete<T extends TSchema>(

const timeoutMs = args.timeoutMs;
const abortController = timeoutMs ? new AbortController() : null;
const completionPromise = complete(
const completionPromise = inferenceModels.complete(
model,
{
messages: [
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/services/threads/title-generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { renderTemplate } from "@bb/templates";
import { getThread, updateThread } from "@bb/db";
import type { PromptInput } from "@bb/domain";
import type { AppDeps, LoggedWorkSessionDeps } from "../../types.js";
import { Type } from "@mariozechner/pi-ai";
import { Type } from "@earendil-works/pi-ai";
import { InferenceTimeoutError, inferenceComplete } from "../ai/inference.js";
import { runtimeErrorLogFields } from "../lib/error-log-fields.js";

Expand Down
10 changes: 4 additions & 6 deletions apps/server/test/ai/commit-message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,12 @@ const commitMessageArgs = {
shortstat: "1 file changed, 1 insertion(+)\n",
};

vi.mock("@mariozechner/pi-ai", async (importOriginal) => {
const actual = await importOriginal<typeof import("@mariozechner/pi-ai")>();
return {
...actual,
vi.mock("@earendil-works/pi-ai/providers/all", () => ({
builtinModels: () => ({
complete: piAiMocks.complete,
getModel: piAiMocks.getModel,
};
});
}),
}));

async function createCommitMessageDeps(): Promise<TestCommitMessageDeps> {
const harness = await createTestAppHarness({
Expand Down
2 changes: 1 addition & 1 deletion apps/server/test/ai/inference.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Type } from "@mariozechner/pi-ai";
import { Type } from "@earendil-works/pi-ai";
import { describe, expect, it } from "vitest";
import {
InferenceTimeoutError,
Expand Down
10 changes: 4 additions & 6 deletions apps/server/test/threads/generated-branch-names.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,12 @@ function mockThreadMetadataCompletion(metadata: MockThreadMetadata) {
};
}

vi.mock("@mariozechner/pi-ai", async (importOriginal) => {
const actual = await importOriginal<typeof import("@mariozechner/pi-ai")>();
return {
...actual,
vi.mock("@earendil-works/pi-ai/providers/all", () => ({
builtinModels: () => ({
complete: piAiMocks.complete,
getModel: piAiMocks.getModel,
};
});
}),
}));

function mockThreadMetadata(metadata: MockThreadMetadata): void {
piAiMocks.getModel.mockReturnValue({ provider: "test" });
Expand Down
14 changes: 7 additions & 7 deletions docs/platform-support.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- Diátaxis: reference -->

# Platform Support

## Supported host environments
Expand All @@ -8,10 +10,9 @@

Supported npm package runtimes:

- Node.js 22 LTS
- Node.js 22.19 or newer in the Node.js 22 release line
- Node.js 24 LTS
- Node.js 26 Current
- Node.js 20 is best-effort only because it is end-of-life upstream

Windows support means the Linux stack runs entirely inside WSL2:

Expand Down Expand Up @@ -137,13 +138,12 @@ rebuild the native dependency, for example `npm rebuild better-sqlite3`.
- Pull requests run the `bb-app` tarball smoke on Ubuntu and macOS with Node.js
22, validating the packed npm artifact through `npx --package`.
- Pushes to `main` and manually dispatched CI runs also run the `bb-app` tarball
smoke on Ubuntu and macOS with Node.js 24 and 26. Node.js 20 runs as a
best-effort Ubuntu compatibility signal only.
smoke on Ubuntu and macOS with Node.js 24 and 26.
- Branch protection should require `Checks (ubuntu-latest, Node 22.x)`,
`Package Smoke (ubuntu-latest, Node 22.x)`, and
`Package Smoke (macos-latest, Node 22.x)`. The Node.js 20, 24, and 26
compatibility smoke jobs do not run on pull requests and should not be
configured as required PR checks.
`Package Smoke (macos-latest, Node 22.x)`. The Node.js 24 and 26 compatibility
smoke jobs do not run on pull requests and should not be configured as
required PR checks.
- Native Windows CI is intentionally not required because Windows support uses
the Linux runtime path inside WSL2 rather than a separate native Windows
product path.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "MIT",
"type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
"node": "^22.19.0 || ^24.0.0 || ^26.0.0"
},
"scripts": {
"scripts:prepare": "pnpm exec turbo run build --filter=@bb/scripts --output-logs=none --log-prefix=none --summarize=false",
Expand Down
4 changes: 2 additions & 2 deletions packages/agent-providers/src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ const builtInAgentProviderById = new Map(
);

/**
* Best default model per provider. Subset of pi-mono's `defaultModelPerProvider`:
* https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/model-resolver.ts
* Best default model per provider. Subset of Pi's `defaultModelPerProvider`:
* https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/model-resolver.ts
*/
export const PI_DEFAULT_MODEL_PER_PROVIDER: PiDefaultModelPerProvider = {
anthropic: "claude-opus-4-8",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,5 @@ mapping, unhandled-event envelopes, command-output normalization) live in
- `@bb/domain` — shared types (ThreadEvent, ProviderThreadEvent, PromptInput, ToolCallRequest, etc.)
- `@bb/templates` — markdown templates for provider instructions
- `@anthropic-ai/claude-agent-sdk` — Claude Code
- `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent` — Pi
- `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent` — Pi
- `zod` — schema validation at provider boundaries
4 changes: 2 additions & 2 deletions packages/agent-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
"@bb/host-daemon-contract": "workspace:*",
"@bb/process-utils": "workspace:*",
"@modelcontextprotocol/sdk": "^1.29.0",
"@mariozechner/pi-ai": "^0.70.5",
"@mariozechner/pi-coding-agent": "^0.70.5",
"@earendil-works/pi-ai": "^0.82.0",
"@earendil-works/pi-coding-agent": "^0.82.0",
"zod": "^4.3.6"
},
"devDependencies": {
Expand Down
Loading