From 2f4864fb0a257b58791bc9844bcf8fae74833b27 Mon Sep 17 00:00:00 2001 From: Salhi-zh Date: Thu, 29 Oct 2026 20:59:15 +0100 Subject: [PATCH 1/2] build: fix the two breaks that keep CI red on main `bun run build:sdk` fails on main for every recent commit, and the binary build stops behind it. Two independent causes: 1. common's JSON types were mutable - `{ [key: string]: JSONValue }` and `JSONValue[]` - while the model SDKs type their payloads as `Readonly<...>`. A mutable index signature does not accept a read-only one, so every providerOptions assignment was a type error: seven of them, from sdk/src/impl/llm.ts:269 through common/src/util/messages.ts and agent-runtime's stream parsers. JSON that crosses a boundary is read-only, so the type now says so. The one place that assembles a new payload (agent-runtime's gravity-index `omitUndefined`) builds a writable `Record` and returns that. 2. cli/src imports `picocolors` in three files and it was declared in no package.json, so the binary build stopped at `Could not resolve: "picocolors"`. Declared at ^1.1.1. Verified with the four steps CI runs, in that order, from a clean tree: bun install --frozen-lockfile exit 0 bun run build:sdk exit 0 (was 7 TS errors and "declaration bundling failed") bun freebuff/cli/build.ts 0.0.0-ci exit 0 cli/bin/freebuff --version + smoke-binary.ts OK, boot matched --- bun.lock | 3 +++ cli/package.json | 1 + common/src/types/json.ts | 11 +++++++++-- .../src/tools/handlers/tool/gravity-index.ts | 4 +++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 7930375f8d..cbb9f27906 100644 --- a/bun.lock +++ b/bun.lock @@ -52,6 +52,7 @@ "jimp": "^1.6.0", "node-machine-id": "^1.1.12", "open": "^10.1.0", + "picocolors": "^1.1.1", "pino": "9.4.0", "react": "^19.0.0", "remark-breaks": "^4.0.0", @@ -1111,6 +1112,8 @@ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "pino": ["pino@9.4.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^7.0.0", "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w=="], diff --git a/cli/package.json b/cli/package.json index f873cd35aa..d7ab26d05e 100644 --- a/cli/package.json +++ b/cli/package.json @@ -41,6 +41,7 @@ "jimp": "^1.6.0", "node-machine-id": "^1.1.12", "open": "^10.1.0", + "picocolors": "^1.1.1", "pino": "9.4.0", "react": "^19.0.0", "remark-breaks": "^4.0.0", diff --git a/common/src/types/json.ts b/common/src/types/json.ts index 244e9a31d8..f42f246af4 100644 --- a/common/src/types/json.ts +++ b/common/src/types/json.ts @@ -21,9 +21,16 @@ export const jsonValueSchema: z.ZodType = z.lazy(() => export const jsonObjectSchema: z.ZodType = z.lazy(() => z.record(z.string(), jsonValueSchema.optional()), ) -export type JSONObject = { [key in string]: JSONValue | undefined } +/** + * JSON as it actually crosses boundaries: read-only. The model SDKs type their + * payloads with `Readonly<...>` objects and arrays, and a mutable index + * signature does not accept them, so every `providerOptions` assignment was a + * type error. Reading is all the CLI does with this data anyway; the one place + * that needs a mutable copy builds `Record` for itself. + */ +export type JSONObject = { readonly [key in string]: JSONValue | undefined } export const jsonArraySchema: z.ZodType = z.lazy(() => z.array(jsonValueSchema), ) -export type JSONArray = JSONValue[] +export type JSONArray = readonly JSONValue[] diff --git a/packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts b/packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts index 2aa4374d22..a7770441b5 100644 --- a/packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts +++ b/packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts @@ -16,7 +16,9 @@ import type { Message } from '@codebuff/common/types/messages/codebuff-message' import type { AgentState } from '@codebuff/common/types/session-state' const omitUndefined = (value: Record) => { - const result: JSONObject = {} + // Built mutably and returned as `JSONObject`: JSON is read-only on the way + // back in, but assembling a new payload needs a writable index here. + const result: Record = {} for (const [key, field] of Object.entries(value)) { if (field !== undefined) { result[key] = field From 4e3a1613533ef7b7c9c17bae12e4ecff166eac42 Mon Sep 17 00:00:00 2001 From: Salhi-zh Date: Thu, 29 Oct 2026 21:04:05 +0100 Subject: [PATCH 2/2] ci: pass the public env to the binary build, where it is inlined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiled binary bakes NEXT_PUBLIC_* in at build time. The workflow set them only on the smoke step, so the build ran with none of them: the values were baked in as undefined, every invocation died in common/src/env.ts, and the smoke step reported "--smoke-api-url exited 1 without printing a URL". Reproduced from a clean environment (env -i, both directions): build without NEXT_PUBLIC_* → --smoke-api-url exits 1 in the env schema build with this env block → probe prints the URL; tree-sitter, dotenv isolation and boot all pass --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 698d155819..eca30ffdd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,20 @@ jobs: run: bun run build:sdk - name: Build Freebuff binary + # The compiled binary inlines NEXT_PUBLIC_* at build time, so the values + # must be present HERE, not only when the binary is run: with an empty + # build environment they are baked in as undefined and every later + # invocation dies in the env schema (smoke-binary.ts documents the same + # contract from the probe side). + env: + NEXT_PUBLIC_CB_ENVIRONMENT: prod + NEXT_PUBLIC_CODEBUFF_APP_URL: https://codebuff.com + NEXT_PUBLIC_SUPPORT_EMAIL: support@freebuff.com + NEXT_PUBLIC_POSTHOG_API_KEY: phc_public_placeholder + NEXT_PUBLIC_POSTHOG_HOST_URL: https://us.i.posthog.com + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: pk_test_placeholder + NEXT_PUBLIC_STRIPE_CUSTOMER_PORTAL: https://billing.stripe.com/p/login/test + NEXT_PUBLIC_WEB_PORT: '3000' run: bun freebuff/cli/build.ts 0.0.0-ci - name: Smoke test Freebuff binary