FN-6218: restore pi upgrade compatibility
Restore Pi-upgraded workspaces to keep Fusion resources accessible and recover title generation when configured models go stale. - Mark read-only Fusion/Pi provider settings views as project-trusted so extension loading keeps working after Pi upgrades. - Retry task title summarization with automatic model resolution when the configured provider model is missing from the Pi registry. - Cover provider settings trust behavior and stale summarizer fallback paths with regression tests. - Document the read-only settings trust contract and add a patch changeset. Files changed: .changeset/fn-6218-pi-upgrade-regressions.md | 5 + docs/settings-reference.md | 2 + docs/task-management.md | 2 + .../commands/__tests__/provider-settings.test.ts | 16 ++++ packages/cli/src/commands/provider-settings.ts | 5 + packages/core/src/__tests__/ai-summarize.test.ts | 102 +++++++++++++++++++++ packages/core/src/ai-summarize.ts | 85 ++++++++++++----- .../src/__tests__/pi-create-fn-agent.test.ts | 32 +++++++ packages/engine/src/pi.ts | 7 +- 9 files changed, 233 insertions(+), 23 deletions(-) Fusion-Task-Id: FN-6218 Fusion-Task-Lineage: 3d6aca32-a56a-43a0-a57b-8e6abc839ce9
This commit is contained in:
@@ -30,6 +30,7 @@ const readFileSyncMock = vi.fn((_path?: any) => "{}");
|
||||
const realpathSyncNativeMock = vi.fn((path: PathLike) => String(path));
|
||||
const readCustomProvidersMock = vi.fn(() => []);
|
||||
const packageManagerCwdCapture = vi.fn();
|
||||
const packageManagerSettingsCapture = vi.fn();
|
||||
|
||||
// Route async `exec` through the `execSync` mock so the promisify bridge works.
|
||||
// Use Symbol.for("nodejs.util.promisify.custom") directly to avoid async imports
|
||||
@@ -107,10 +108,15 @@ vi.mock("@earendil-works/pi-coding-agent", () => ({
|
||||
}
|
||||
},
|
||||
DefaultPackageManager: class {
|
||||
private readonly settingsManager: any;
|
||||
|
||||
constructor(options: any) {
|
||||
packageManagerCwdCapture(options?.cwd);
|
||||
packageManagerSettingsCapture(options?.settingsManager);
|
||||
this.settingsManager = options?.settingsManager;
|
||||
}
|
||||
async resolve() {
|
||||
this.settingsManager.isProjectTrusted();
|
||||
return packageManagerResolveMock();
|
||||
}
|
||||
},
|
||||
@@ -1272,6 +1278,32 @@ describe("createFnAgent", () => {
|
||||
expect(createAgentSessionMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("exposes project trust on the read-only pi settings view", async () => {
|
||||
const { createReadOnlyPiSettingsView } = await import("../pi.js");
|
||||
|
||||
const view = createReadOnlyPiSettingsView("/tmp", "/mock-agent-dir");
|
||||
|
||||
expect(() => view.isProjectTrusted()).not.toThrow();
|
||||
expect(view.isProjectTrusted()).toBe(true);
|
||||
expect(typeof view.isProjectTrusted()).toBe("boolean");
|
||||
});
|
||||
|
||||
it("passes a project-trusted settings view through package-manager discovery", async () => {
|
||||
const { createFnAgent } = await import("../pi.js");
|
||||
|
||||
await createFnAgent({
|
||||
cwd: "/tmp",
|
||||
systemPrompt: "test",
|
||||
tools: "readonly",
|
||||
});
|
||||
|
||||
const settingsView = packageManagerSettingsCapture.mock.calls.at(-1)?.[0];
|
||||
expect(settingsView).toEqual(expect.objectContaining({ isProjectTrusted: expect.any(Function) }));
|
||||
expect(settingsView.isProjectTrusted()).toBe(true);
|
||||
expect(packageManagerResolveMock).toHaveBeenCalled();
|
||||
expect(createAgentSessionMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("registers extension providers before resolving configured models", async () => {
|
||||
packageManagerResolveMock.mockResolvedValueOnce({
|
||||
extensions: [{ enabled: true, path: "/extensions/zai-provider" }],
|
||||
|
||||
@@ -1080,6 +1080,7 @@ interface PackageManagerSettingsView {
|
||||
getGlobalSettings(): Record<string, any>;
|
||||
getProjectSettings(): Record<string, any>;
|
||||
getNpmCommand(): string[] | undefined;
|
||||
isProjectTrusted(): boolean;
|
||||
}
|
||||
|
||||
function readJsonObject(path: string): Record<string, any> {
|
||||
@@ -1258,7 +1259,7 @@ function siblingAgentDir(agentDir: string, siblingRoot: ".fusion" | ".pi"): stri
|
||||
return join(dirname(dirname(agentDir)), siblingRoot, "agent");
|
||||
}
|
||||
|
||||
function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
||||
export function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageManagerSettingsView {
|
||||
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
||||
const fusionAgentDir = agentDir.includes(`${join(".fusion", "agent")}`)
|
||||
? agentDir
|
||||
@@ -1279,6 +1280,10 @@ function createReadOnlyPiSettingsView(cwd: string, agentDir: string): PackageMan
|
||||
getNpmCommand: () => Array.isArray(mergedSettings.npmCommand)
|
||||
? [...mergedSettings.npmCommand]
|
||||
: undefined,
|
||||
// Pi's SettingsManager defaults projects to trusted. Fusion workspaces are
|
||||
// user-owned, so preserve pre-upgrade behavior and keep project-scoped
|
||||
// .fusion resources loadable through the read-only settings view.
|
||||
isProjectTrusted: () => true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user