fix(desktop): advance Pi runtime pin to 0.82.1 for packaging PR lane (#2465)

## Summary
- Advance the matched Pi runtime pin (`pi-ai`, `pi-coding-agent`,
`pi-agent-core`, `pi-tui`) from **0.82.0 → 0.82.1** so
electron-builder's production-dependency walk accepts `pi-agent-core`'s
`pi-ai@^0.82.1` requirement.
- Fixes the Desktop packaging PR-lane failure:
`Production dependency @earendil-works/pi-ai not found for package
@earendil-works/pi-agent-core` (required `^0.82.1`).
- Keep the workspace override guard; update pin-policy fixtures and CLI
package-config expectations.
- Tighten the advisory packaging step-order test so it asserts against
the real `electron-builder --dir` step (not a missing release-only step
name that previously passed via `indexOf === -1`).
- Run `pnpm dedupe` so the packaging lane's lockfile dedupe
early-warning is clean.

## Context
#2439 pinned the full Pi closure at 0.82.0 and made recent main-based
packaging runs green. This advances to the current upstream patch so
deploy + electron-builder stay aligned with `pi-agent-core@0.82.1`'s
declared dependency range.

## Test plan
- [x] `node scripts/check-pi-versions-pinned.mjs`
- [x] `node --test scripts/__tests__/check-pi-versions-pinned.test.mjs`
- [x] `pnpm --filter @runfusion/fusion exec vitest run
src/__tests__/package-config.test.ts`
- [x] `pnpm --filter @fusion/desktop exec vitest run
src/__tests__/release-workflow.test.ts`
- [x] `pnpm dedupe --check`
- [ ] GitHub: Desktop packaging (should run full packaging walk —
lockfile/package.json touched)
- [ ] GitHub: PR Checks (Lint, Typecheck, Build, Gate)
This commit is contained in:
gsxdsm
2026-07-26 23:47:49 -07:00
committed by GitHub
parent 3c5d07ab01
commit 8b039a543e
15 changed files with 366 additions and 657 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Keep desktop packaging on a packageable Pi 0.82.1 runtime closure.
category: fix
dev: Advances the matched Pi runtime pin (pi-ai, pi-coding-agent, pi-agent-core, pi-tui) from 0.82.0 to 0.82.1 so electron-builder accepts pi-agent-core's pi-ai@^0.82.1 dependency during the Desktop packaging PR lane.

View File

@@ -23,22 +23,37 @@ runs:
- name: Install pnpm
uses: pnpm/action-setup@v4
# Do NOT cache the linked node_modules tree. pnpm builds node_modules from
# symlinks/junctions into the content-addressable store, and actions/cache does
# not preserve Windows junctions across its tar/restore — a restored tree has
# broken peer links (e.g. @vitejs/plugin-react can't resolve its `vite` peer →
# ERR_MODULE_NOT_FOUND), which silently broke the Windows release build. The
# pnpm *store* is already cached by actions/setup-node (`cache: pnpm`), so
# `pnpm install --frozen-lockfile` is fast with a warm store and relinks
# correctly per-OS/arch every time (also fixing the prior arch-key and per-job
# cache-race issues this node_modules cache was patched for).
#
# FNXC:CI 2026-07-26-23:05:
# skip-install callers (agent-browser pack fixture, version.yml) never create a
# pnpm store. setup-node's post-job cache save then fails hard with
# "Path Validation Error: Path(s) specified in the action for caching do(es)
# not exist" and marks an otherwise-successful pack job failed. Only enable
# the pnpm store cache when this composite will actually install.
- name: Setup Node.js
if: ${{ inputs.skip-install != 'true' }}
uses: actions/setup-node@v5
with:
node-version: ${{ inputs.node-version }}
cache: pnpm
registry-url: ${{ inputs.registry-url }}
# Do NOT cache the linked node_modules tree. pnpm builds node_modules from
# symlinks/junctions into the content-addressable store, and actions/cache does
# not preserve Windows junctions across its tar/restore — a restored tree has
# broken peer links (e.g. @vitejs/plugin-react can't resolve its `vite` peer →
# ERR_MODULE_NOT_FOUND), which silently broke the Windows release build. The
# pnpm *store* is already cached by actions/setup-node above (`cache: pnpm`), so
# `pnpm install --frozen-lockfile` is fast with a warm store and relinks
# correctly per-OS/arch every time (also fixing the prior arch-key and per-job
# cache-race issues this node_modules cache was patched for).
- name: Setup Node.js (no pnpm store cache)
if: ${{ inputs.skip-install == 'true' }}
uses: actions/setup-node@v5
with:
node-version: ${{ inputs.node-version }}
registry-url: ${{ inputs.registry-url }}
- name: Install dependencies
if: ${{ inputs.skip-install != 'true' }}
shell: bash

View File

@@ -67,8 +67,8 @@
"dependencies": {
"@agentclientprotocol/sdk": "0.24.0",
"agent-browser": "0.26.0",
"@earendil-works/pi-ai": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-ai": "0.82.1",
"@earendil-works/pi-coding-agent": "0.82.1",
"claude-code-cli-acp": "0.1.1",
"dockerode": "^4.0.12",
"electron": "^33.4.11",

View File

@@ -259,6 +259,25 @@ describe("Merge gate (.github/workflows/pr-checks.yml)", () => {
expect(compositeAction.inputs?.["install-args"]?.default).toBe("--frozen-lockfile");
});
/*
FNXC:CI 2026-07-26-23:05:
skip-install pack jobs never create a pnpm store; setup-node must not enable
cache: pnpm on that path or post-job cache save fails the whole job after a
successful pack (agent-browser-install pack-fixture).
*/
it("disables pnpm store cache when skip-install is true", () => {
const setupSteps = (compositeAction.runs?.steps ?? []).filter(
(step: any) => typeof step.uses === "string" && step.uses.startsWith("actions/setup-node@"),
);
const withCache = setupSteps.find((step: any) => step.with?.cache === "pnpm");
const withoutCache = setupSteps.find((step: any) => step.with?.cache === undefined || step.with?.cache === "");
expect(withCache?.if).toContain("skip-install");
expect(withCache?.if).toContain("!=");
expect(withoutCache?.if).toContain("skip-install");
expect(withoutCache?.if).toContain("==");
expect(withoutCache?.with?.cache).toBeUndefined();
});
it("keeps lint as install + lint only, without Bun/setup build coupling", () => {
const lintSteps = workflow.jobs?.lint?.steps ?? [];
expect(
@@ -826,6 +845,8 @@ describe("Cross-platform agent-browser install workflow", () => {
const packFixture = workflow.jobs?.["pack-fixture"];
const installSmoke = workflow.jobs?.["install-smoke"];
expect(packFixture?.["runs-on"]).toBe("ubuntu-latest");
// FNXC:CI 2026-07-26-23:05: pack fixture must keep skip-install so the composite
// takes the no-store-cache setup-node path (see setup-node-pnpm action).
expect(findCompositeSetupStep(packFixture?.steps ?? [])?.with?.["skip-install"]).toBe("true");
expect(installSmoke?.needs).toBe("pack-fixture");
expect(installSmoke?.["runs-on"]).toBe("${{ matrix.os }}");

View File

@@ -53,10 +53,12 @@ function assertRuntimeDepsAreNotOptionalPeers(pkg: any, label: string): void {
).not.toBe(true);
}
// FNXC:DesktopPackaging 2026-07-26-22:55: Exact matched Pi runtime pin — keep in sync with
// pnpm-workspace.yaml overrides and check-pi-versions-pinned.mjs (currently 0.82.1).
for (const dependencyName of ["@earendil-works/pi-coding-agent", "@earendil-works/pi-ai"]) {
expect(dependencies, `${label}: ${dependencyName} must remain a required runtime dependency`).toHaveProperty(
dependencyName,
"0.82.0",
"0.82.1",
);
expect(dependencies[dependencyName], `${label}: ${dependencyName} must be a clean exact semver`).toMatch(
EXACT_SEMVER,

View File

@@ -51,7 +51,7 @@
"test:pg-gate": "vitest run --config vitest.pg.config.ts src/__tests__/postgres/handoff-to-review-atomicity.pg.test.ts src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts --silent=passed-only --reporter=dot"
},
"devDependencies": {
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.1",
"@types/dockerode": "^3.3.41",
"@types/node": "^25.5.0",
"@vitest/coverage-v8": "^4.1.0",

View File

@@ -107,7 +107,7 @@
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.36.4",
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.1",
"@fusion-plugin-examples/claude-runtime": "workspace:*",
"@fusion-plugin-examples/cli-printing-press": "workspace:*",
"@fusion-plugin-examples/compound-engineering": "workspace:*",

View File

@@ -106,16 +106,31 @@ describe("desktop release workflow wiring", () => {
expect(verifier).not.toContain("found ${platform}; expected ${expectedPlatform}");
const advisoryPackaging = await readRepoFile(".github/workflows/desktop-packaging.yml");
for (const workflow of [release, testRelease, advisoryPackaging]) {
/*
FNXC:DesktopEmbeddedPostgres 2026-07-15-11:55:
This verifier reads electron-builder's unpacked tree, so invoking it before
the Linux packaging command would only validate stale output.
FNXC:DesktopPackaging 2026-07-26-22:55:
Release/test-release package via the named "Package Linux desktop artifacts"
step; the advisory PR lane uses electron-builder --dir ("Validate packageable
closure"). Assert order against each workflow's real packaging step so a
missing release step cannot silently pass via indexOf === -1.
*/
for (const workflow of [release, testRelease]) {
expect(workflow).toContain("Verify Linux AppImage embedded Postgres packaging");
expect(workflow).toContain("node scripts/verify-desktop-linux-pg-packaging.mjs");
// FNXC:DesktopEmbeddedPostgres 2026-07-15-11:55:
// This verifier reads electron-builder's unpacked tree, so invoking it
// before the Linux packaging command would only validate stale output.
expect(workflow).toContain("Package Linux desktop artifacts");
expect(workflow.indexOf("node scripts/verify-desktop-linux-pg-packaging.mjs")).toBeGreaterThan(
workflow.indexOf("Package Linux desktop artifacts"),
);
}
expect(advisoryPackaging).toContain("Verify Linux AppImage embedded Postgres packaging");
expect(advisoryPackaging).toContain("node scripts/verify-desktop-linux-pg-packaging.mjs");
expect(advisoryPackaging).toContain("Validate packageable closure (electron-builder --dir)");
expect(advisoryPackaging.indexOf("node scripts/verify-desktop-linux-pg-packaging.mjs")).toBeGreaterThan(
advisoryPackaging.indexOf("Validate packageable closure (electron-builder --dir)"),
);
});
it("wires release aggregation to include desktop assets across platforms", async () => {

View File

@@ -38,8 +38,8 @@
"test:watch": "vitest src/__tests__/executor-*.test.ts --watch"
},
"dependencies": {
"@earendil-works/pi-ai": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-ai": "0.82.1",
"@earendil-works/pi-coding-agent": "0.82.1",
"@fusion/core": "workspace:*",
"@fusion/pi-claude-cli": "workspace:*",
"@modelcontextprotocol/sdk": "^1.0.0",

View File

@@ -224,12 +224,12 @@ plus a per-provider read-modify-merge (FileAuthStorageBackend.persistProviderCha
refreshOAuthTokenWithLock re-read the file under a lock and spread
{...currentData, [provider]: credential} rather than flushing a whole-file in-memory
snapshot). packages/engine/package.json already pins this at
"@earendil-works/pi-coding-agent": "0.82.0" (exact matched runtime pair; locked per-provider
"@earendil-works/pi-coding-agent": "0.82.1" (exact matched runtime pair; locked per-provider
merge requires >=0.80.3) — do not downgrade below 0.80.x, and re-verify this comment against
dist/core/auth-storage.js if the pin is ever lowered. See
FNXC:ProviderAuth 2026-07-24-12:00:
FN-8564 retains Fusion's credential-store adapter on Pi 0.82.0. The release adds Kimi Code and
FN-8564 retains Fusion's credential-store adapter on Pi 0.82.x. The release adds Kimi Code and
OpenRouter OAuth, so new upstream login providers must still use the same queued, cross-process
credential persistence contract rather than bypassing Fusion auth storage.
packages/engine/src/__tests__/auth-storage-concurrency.test.ts for the regression coverage.

View File

@@ -2627,7 +2627,7 @@ export async function createFnAgent(options: AgentOptions): Promise<AgentResult>
ModelRuntime path already adopted in 0.80.8.
FNXC:ModelCatalog 2026-07-24-12:00:
FN-8564 advances the exact matched pair to 0.82.0. Its catalog now exposes only
FN-8564 advances the exact matched pair to 0.82.x. Its catalog now exposes only
provider-verified reasoning levels and adds Kimi/OpenRouter OAuth plus constrained tools;
retain Fusion's ModelRuntime session construction and supplemental catalog merges rather
than duplicating upstream provider behavior.

View File

@@ -23,12 +23,12 @@
"@agentclientprotocol/sdk": "0.24.0"
},
"peerDependencies": {
"@earendil-works/pi-ai": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.0"
"@earendil-works/pi-ai": "0.82.1",
"@earendil-works/pi-coding-agent": "0.82.1"
},
"devDependencies": {
"@earendil-works/pi-ai": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-ai": "0.82.1",
"@earendil-works/pi-coding-agent": "0.82.1",
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^4.1.0"

875
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,10 +28,13 @@ overrides:
# FNXC:DesktopPackaging 2026-07-25-17:15: Legacy `pnpm deploy` resolves pi-coding-agent's
# transitive ranges without the workspace lockfile. Pin the complete Pi runtime closure to one
# exact version so new agent-core or tui patches cannot split the packageable desktop tree.
'@earendil-works/pi-agent-core': 0.82.0
'@earendil-works/pi-ai': 0.82.0
'@earendil-works/pi-coding-agent': 0.82.0
'@earendil-works/pi-tui': 0.82.0
# FNXC:DesktopPackaging 2026-07-26-22:55: Advance the matched closure 0.82.0 → 0.82.1 so
# electron-builder's production-dependency walk accepts pi-agent-core's pi-ai@^0.82.1 requirement
# (Desktop packaging PR-lane failure when deploy resolved a newer agent-core patch beside 0.82.0 ai).
'@earendil-works/pi-agent-core': 0.82.1
'@earendil-works/pi-ai': 0.82.1
'@earendil-works/pi-coding-agent': 0.82.1
'@earendil-works/pi-tui': 0.82.1
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
# Desktop release jobs cross-build Intel and ARM64 artifacts on one host. Install
# optional native payloads for both CPU families on the current OS so each

View File

@@ -7,10 +7,19 @@ import {
validateWorkspaceOverrides,
} from "../check-pi-versions-pinned.mjs";
/*
FNXC:DesktopPackaging 2026-07-26-22:55:
Fixture versions track the workspace's exact matched Pi runtime pin (currently 0.82.1).
Keep these in sync when advancing overrides/manifests so the policy guard still exercises
matched-set and range rejection against the live pin surface.
*/
const PINNED_VERSION = "0.82.1";
const OTHER_VERSION = "0.82.0";
const pinnedManifest = {
dependencies: {
"@earendil-works/pi-ai": "0.82.0",
"@earendil-works/pi-coding-agent": "0.82.0",
"@earendil-works/pi-ai": PINNED_VERSION,
"@earendil-works/pi-coding-agent": PINNED_VERSION,
},
};
@@ -24,7 +33,7 @@ describe("check-pi-versions-pinned", () => {
});
it("rejects caret, tilde, wildcard, x, and comparator ranges", () => {
for (const version of ["^0.82.0", "~0.82.0", "*", "0.82.x", ">=0.82.0"]) {
for (const version of [`^${PINNED_VERSION}`, `~${PINNED_VERSION}`, "*", "0.82.x", `>=${PINNED_VERSION}`]) {
const violations = validate({
...pinnedManifest,
dependencies: { ...pinnedManifest.dependencies, "@earendil-works/pi-ai": version },
@@ -36,7 +45,7 @@ describe("check-pi-versions-pinned", () => {
it("rejects a matched-set version mismatch", () => {
const violations = validate({
...pinnedManifest,
dependencies: { ...pinnedManifest.dependencies, "@earendil-works/pi-coding-agent": "0.82.1" },
dependencies: { ...pinnedManifest.dependencies, "@earendil-works/pi-coding-agent": OTHER_VERSION },
});
assert.equal(violations.some((violation) => violation.includes("same exact version")), true);
});
@@ -53,7 +62,7 @@ describe("check-pi-versions-pinned", () => {
cannot split agent-core or tui from the exact runtime version.
*/
it("requires one exact workspace override for every staged Pi runtime package", () => {
const coherentOverrides = Object.fromEntries(PI_RUNTIME_PACKAGES.map((packageName) => [packageName, "0.82.0"]));
const coherentOverrides = Object.fromEntries(PI_RUNTIME_PACKAGES.map((packageName) => [packageName, PINNED_VERSION]));
assert.deepEqual(validateWorkspaceOverrides(coherentOverrides), []);
assert.equal(
@@ -62,12 +71,12 @@ describe("check-pi-versions-pinned", () => {
true,
);
assert.equal(
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-agent-core": "^0.82.0" })
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-agent-core": `^${PINNED_VERSION}` })
.some((violation) => violation.includes("pi-agent-core") && violation.includes("exact semver")),
true,
);
assert.equal(
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-tui": "0.82.1" })
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-tui": OTHER_VERSION })
.some((violation) => violation.includes("one exact version")),
true,
);