Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/align-pi-packages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@open-codesign/core": patch
"@open-codesign/providers": patch
"@open-codesign/desktop": patch
---

Migrate the pi agent, model, and coding-agent packages together to the maintained 0.79.x releases (at least 0.79.0), which fix extension loading, credential-file, and HTML-export security issues. Align the application's HTTP runtime with pi's fixed Undici 8.12.4 release and require Node.js 22.19.0 or newer.
2 changes: 1 addition & 1 deletion .github/prompts/codex-pr-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Open CoDesign is an open-source AI design tool — Electron desktop app that tur
**Project constraints:**
- ≤ 30 prod dependencies
- Shipped app/runtime dependencies and copied/bundled assets must be MIT-compatible permissive. Workflow-only CI/release actions may use copyleft licenses when they are not vendored or distributed and their outputs are ordinary metadata/manifests.
- All LLM calls via `@mariozechner/pi-ai` (no direct provider SDK imports in app code)
- All LLM calls via `@earendil-works/pi-ai` (no direct provider SDK imports in app code)
- No silent fallbacks for user-visible failure, data loss, auth/security decisions,
or persisted state. Best-effort cleanup, optional discovery, and non-critical
listing paths may degrade quietly when the UI still presents a truthful state.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ These are project-level commitments, not preferences:
- **Commits**: Conventional Commits, enforced by commitlint.
- **Versioning**: Changesets. Don't hand-edit `CHANGELOG.md`.
- **Node**: 22 LTS (pinned via `.nvmrc` + `engines`).
- **Model layer**: All LLM calls go through `@mariozechner/pi-ai`. Don't import provider SDKs directly in app code; if pi-ai lacks a feature, add it to `packages/providers` as a thin extension.
- **Model layer**: All LLM calls go through `@earendil-works/pi-ai`. Don't import provider SDKs directly in app code; if pi-ai lacks a feature, add it to `packages/providers` as a thin extension.

### Frontend stack (locked)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ Have a different priority in mind? [Open an issue](https://github.com/OpenCowork
## Built on

- Electron + React 19 + Vite 6 + Tailwind v4
- `@mariozechner/pi-ai` and `pi-coding-agent` (model/provider and agent-loop primitives)
- `@earendil-works/pi-ai` and `pi-coding-agent` (model/provider and agent-loop primitives)
- `electron-builder`

## Reporting issues
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ v0.2 已经把 Open CoDesign 从一次性生成器升级成一个本地设计 ag
## 基于这些技术构建

- Electron + React 19 + Vite 6 + Tailwind v4
- `@mariozechner/pi-ai` 与 `pi-coding-agent`(模型 / provider 与 agent loop 基础能力)
- `@earendil-works/pi-ai` 与 `pi-coding-agent`(模型 / provider 与 agent loop 基础能力)
- `electron-builder`

## 社群
Expand Down
8 changes: 4 additions & 4 deletions apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const WORKSPACE_PACKAGES = [
'@open-codesign/ui',
];
const BUNDLED_RUNTIME_PACKAGES = [
'@mariozechner/pi-agent-core',
'@mariozechner/pi-ai',
'@mariozechner/pi-coding-agent',
'@earendil-works/pi-agent-core',
'@earendil-works/pi-ai',
'@earendil-works/pi-coding-agent',
'electron-log',
'electron-log/main',
'electron-updater',
Expand Down Expand Up @@ -71,7 +71,7 @@ export default defineConfig(({ mode }) => ({
...(mode === 'background-debug' ? { output: { chunkFileNames: '[name]-[hash].js' } } : {}),
treeshake: {
moduleSideEffects: (id) =>
!id.includes('/node_modules/@mariozechner/pi-coding-agent/dist/'),
!id.includes('/node_modules/@earendil-works/pi-coding-agent/dist/'),
},
external: ['electron', 'puppeteer-core'],
plugins: [copyPromptSections()],
Expand Down
8 changes: 4 additions & 4 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
"ms": "2.1.3",
"parse5": "8.0.1",
"puppeteer-core": "^24.42.0",
"undici": "^7.30.1"
"undici": "8.12.4"
},
"devDependencies": {
"@mariozechner/pi-agent-core": "^0.72.1",
"@mariozechner/pi-ai": "^0.72.1",
"@mariozechner/pi-coding-agent": "^0.72.1",
"@earendil-works/pi-agent-core": "^0.79.0",
"@earendil-works/pi-ai": "^0.79.0",
"@earendil-works/pi-coding-agent": "^0.79.0",
"@open-codesign/artifacts": "workspace:*",
"@open-codesign/core": "workspace:*",
"@open-codesign/exporters": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/codex-models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getCodexModelOptions } from './codex-models';
import { readCodexConfig } from './imports/codex-config';

vi.mock('./imports/codex-config', () => ({ readCodexConfig: vi.fn() }));
vi.mock('@mariozechner/pi-ai', () => ({
vi.mock('@earendil-works/pi-ai', () => ({
getModels: (provider: string) => (provider === 'openai-codex' ? [{ id: 'catalog-model' }] : []),
}));

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/codex-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function getCodexModelOptions(): Promise<CodexModelOptions> {
(imported?.activeProvider === 'codex-openai' &&
!imported.providers.some((provider) => provider.id === 'codex-openai'));
const suggestedModel = usesBuiltinProvider ? (imported?.activeModel ?? null) : null;
const { getModels } = await import('@mariozechner/pi-ai');
const { getModels } = await import('@earendil-works/pi-ai');
return {
suggestedModel,
models: [
Expand Down
8 changes: 4 additions & 4 deletions apps/desktop/src/main/generation-native-cancel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { EventEmitter } from 'node:events';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import type { AgentOptions, StreamFn } from '@mariozechner/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@mariozechner/pi-ai';
import type { AgentOptions, StreamFn } from '@earendil-works/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@earendil-works/pi-ai';
import { generateViaAgent } from '@open-codesign/core';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
Expand All @@ -24,8 +24,8 @@ const native = vi.hoisted(() => ({
stream: vi.fn<StreamFn>(),
windows: [] as Electron.BrowserWindow[],
}));
vi.mock('@mariozechner/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@mariozechner/pi-agent-core')>();
vi.mock('@earendil-works/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@earendil-works/pi-agent-core')>();
return {
...actual,
Agent: class extends actual.Agent {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/ipc/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ export function registerGenerateIpc({ db, getMainWindow }: RegisterGenerateIpcDe
});
await research.saveNativeSources?.(records, input.signal);
},
persistMessage: (message: import('@mariozechner/pi-ai').AssistantMessage) => {
persistMessage: (message: import('@earendil-works/pi-ai').AssistantMessage) => {
const store = chatStoreOptions();
if (store) appendSessionNativeSearchMessage(store, designId, message);
},
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/main/ipc/generate.workspace-rename.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { mkdir, readdir, readFile, rm, symlink, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { Agent } from '@mariozechner/pi-agent-core';
import { createAssistantMessageEventStream, getModel } from '@mariozechner/pi-ai';
import { Agent } from '@earendil-works/pi-agent-core';
import { createAssistantMessageEventStream, getModel } from '@earendil-works/pi-ai';
import type { Design } from '@open-codesign/shared';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/migration/v01-to-v02.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, mkdirSync, renameSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import type { Message, Usage } from '@mariozechner/pi-ai';
import type { Message, Usage } from '@earendil-works/pi-ai';
import { SessionManager } from '@open-codesign/core';
import { normalizeDesignFilePath } from '../snapshots-db';
import { prepareWorkspaceWriteContent } from '../workspace-file-content';
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/session-chat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('session design brief storage', () => {
const db = initSnapshotsDb(path.join(root, 'design-store.json'));
const design = createDesign(db, 'Native sources');
updateDesignWorkspace(db, design.id, root);
const message: import('@mariozechner/pi-ai').AssistantMessage = {
const message: import('@earendil-works/pi-ai').AssistantMessage = {
role: 'assistant',
api: 'anthropic-messages',
provider: 'anthropic',
Expand Down
6 changes: 3 additions & 3 deletions apps/desktop/src/main/session-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const NATIVE_SEARCH_CUSTOM_TYPE = 'open-codesign.native-search.v1';
export function appendSessionNativeSearchMessage(
opts: SessionChatStoreOptions,
designId: string,
message: import('@mariozechner/pi-ai').AssistantMessage,
message: import('@earendil-works/pi-ai').AssistantMessage,
): void {
const manager = openSession(opts, designId);
manager.appendCustomEntry(NATIVE_SEARCH_CUSTOM_TYPE, { schemaVersion: 1, message });
Expand All @@ -49,7 +49,7 @@ export function appendSessionNativeSearchMessage(
export function readSessionNativeSearchMessages(
opts: SessionChatStoreOptions,
designId: string,
): import('@mariozechner/pi-ai').AssistantMessage[] {
): import('@earendil-works/pi-ai').AssistantMessage[] {
const file = sessionFileForDesign(opts.sessionDir, designId);
if (!existsSync(file)) return [];
const manager = SessionManager.open(file, opts.sessionDir, resolveSessionCwd(opts, designId));
Expand All @@ -66,7 +66,7 @@ export function readSessionNativeSearchMessages(
return [];
const value = custom.data as {
schemaVersion?: number;
message?: import('@mariozechner/pi-ai').AssistantMessage;
message?: import('@earendil-works/pi-ai').AssistantMessage;
};
const message = value.message;
if (
Expand Down
53 changes: 53 additions & 0 deletions apps/desktop/src/main/undici-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { readFileSync, realpathSync } from 'node:fs';
import { createServer } from 'node:http';
import { createRequire } from 'node:module';
import { getGlobalDispatcher } from 'undici';
import { describe, expect, it, vi } from 'vitest';
import { withTlsBypass } from './tls-override';

vi.mock('./logger', () => ({
getLogger: () => ({ warn: vi.fn(), error: vi.fn() }),
}));

describe('bundled pi HTTP runtime', () => {
it('uses the same installed Undici version as the application external', () => {
const appRequire = createRequire(import.meta.url);
const piRequire = createRequire(
realpathSync(
new URL('../../node_modules/@earendil-works/pi-coding-agent/package.json', import.meta.url),
),
);
const version = (from: ReturnType<typeof createRequire>): string => {
const metadata = JSON.parse(readFileSync(from.resolve('undici/package.json'), 'utf8')) as {
version: string;
};
return metadata.version;
};

// electron-vite resolves bundled pi's external production dependencies
// from the app. Checking the nested install alone misses a runtime mismatch.
expect(version(appRequire)).toBe(version(piRequire));
});

it('bridges the app dispatcher to native fetch and restores it after a request', async () => {
const server = createServer((_request, response) => response.end('runtime-ok'));
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
const original = getGlobalDispatcher();
try {
const address = server.address();
if (address === null || typeof address === 'string') throw new Error('Missing test port');
const text = await withTlsBypass(true, async () => {
expect(getGlobalDispatcher()).not.toBe(original);
const response = await fetch(`http://127.0.0.1:${address.port}/`);
return response.text();
});
expect(text).toBe('runtime-ok');
expect(getGlobalDispatcher()).toBe(original);
} finally {
server.closeAllConnections();
await new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
});
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"url": "https://github.com/OpenCoworkAI/open-codesign/issues"
},
"engines": {
"node": ">=22"
"node": ">=22.19.0"
},
"packageManager": "pnpm@10.33.4",
"scripts": {
Expand All @@ -39,14 +39,16 @@
"overrides": {
"@xmldom/xmldom@<0.8.15": "0.8.15",
"pptxgenjs@4.0.1>image-size": "-",
"undici@>=8.0.0 <8.12.4": "8.12.4",
"undici@>=7.0.0 <7.30.1": "7.30.1",
"fast-xml-parser@<5.7.0": ">=5.7.0",
"vitepress>vite": "6.4.2",
"esbuild@<0.25.0": ">=0.25.0",
"tar@<7.5.22": "7.5.22"
},
"patchedDependencies": {
"pptxgenjs@4.0.1": "patches/pptxgenjs@4.0.1.patch",
"@mariozechner/pi-ai@0.72.1": "patches/@mariozechner__pi-ai@0.72.1.patch"
"@earendil-works/pi-ai@0.79.10": "patches/@earendil-works__pi-ai@0.79.10.patch"
}
},
"devDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"@mariozechner/pi-agent-core": "^0.72.1",
"@mariozechner/pi-ai": "^0.72.1",
"@mariozechner/pi-coding-agent": "^0.72.1",
"@earendil-works/pi-agent-core": "^0.79.0",
"@earendil-works/pi-ai": "^0.79.0",
"@earendil-works/pi-coding-agent": "^0.79.0",
"@open-codesign/providers": "workspace:*",
"@open-codesign/shared": "workspace:*",
"@sinclair/typebox": "^0.34.49"
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/active-messages.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Agent, type AgentMessage } from '@mariozechner/pi-agent-core';
import { Agent, type AgentMessage } from '@earendil-works/pi-agent-core';
import {
type AssistantMessage,
createAssistantMessageEventStream,
getModel,
} from '@mariozechner/pi-ai';
} from '@earendil-works/pi-ai';
import type { ActiveRunMessageInputV1, ActiveRunMessageV1 } from '@open-codesign/shared';
import { Type } from '@sinclair/typebox';
import { describe, expect, it, vi } from 'vitest';
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/active-messages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Agent, AgentEvent, AgentMessage } from '@mariozechner/pi-agent-core';
import type { Agent, AgentEvent, AgentMessage } from '@earendil-works/pi-agent-core';
import {
type ActiveRunMessageInputV1,
type ActiveRunMessageV1,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/agent-session.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { AuthStorage } from '@mariozechner/pi-coding-agent';
import { AuthStorage } from '@earendil-works/pi-coding-agent';
import { describe, expect, it } from 'vitest';
import { createCodesignSession } from './agent-session.js';

Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/agent-session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* v0.2 agent-session wrapper around `@mariozechner/pi-coding-agent`.
* v0.2 agent-session wrapper around `@earendil-works/pi-coding-agent`.
*
* Spike result (docs/spike-pi-results.md, 2026-04-24):
* pi-coding-agent satisfies all v0.2 hard needs (bash hook, session
Expand All @@ -13,7 +13,7 @@
*/

import path from 'node:path';
import type { Api, Model } from '@mariozechner/pi-ai';
import type { Api, Model } from '@earendil-works/pi-ai';
import {
type AgentSession,
type AuthStorage,
Expand All @@ -25,7 +25,7 @@ import {
isToolCallEventType,
ModelRegistry,
SessionManager,
} from '@mariozechner/pi-coding-agent';
} from '@earendil-works/pi-coding-agent';

export interface PermissionDecision {
/** Allow this command to run. */
Expand Down Expand Up @@ -95,4 +95,4 @@ export async function createCodesignSession(options: CreateSessionOptions): Prom
return { session: result.session, result, sessionFile };
}

export { AuthStorage, ModelRegistry, SessionManager } from '@mariozechner/pi-coding-agent';
export { AuthStorage, ModelRegistry, SessionManager } from '@earendil-works/pi-coding-agent';
8 changes: 4 additions & 4 deletions packages/core/src/agent.active-message-retry.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Agent, AgentMessage, AgentOptions, StreamFn } from '@mariozechner/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@mariozechner/pi-ai';
import type { Agent, AgentMessage, AgentOptions, StreamFn } from '@earendil-works/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@earendil-works/pi-ai';
import type { ActiveRunMessageV1 } from '@open-codesign/shared';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ActiveRunMessages } from './active-messages';
Expand All @@ -10,8 +10,8 @@ const native = vi.hoisted(() => ({
agents: [] as Agent[],
}));

vi.mock('@mariozechner/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@mariozechner/pi-agent-core')>();
vi.mock('@earendil-works/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@earendil-works/pi-agent-core')>();
return {
...actual,
Agent: class extends actual.Agent {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/agent.design-loop.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import type { Agent, AgentMessage, AgentOptions, StreamFn } from '@mariozechner/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@mariozechner/pi-ai';
import type { Agent, AgentMessage, AgentOptions, StreamFn } from '@earendil-works/pi-agent-core';
import { type AssistantMessage, createAssistantMessageEventStream } from '@earendil-works/pi-ai';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { generateViaAgent } from './agent';
import type { GenerateInput } from './index';
Expand All @@ -13,8 +13,8 @@ import type { TextEditorFsCallbacks } from './tools/text-editor';
const native = vi.hoisted(() => ({ stream: vi.fn<StreamFn>(), agents: [] as Agent[] }));

// Substitute only pi's streaming boundary; Agent scheduling, tools and recovery are real.
vi.mock('@mariozechner/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@mariozechner/pi-agent-core')>();
vi.mock('@earendil-works/pi-agent-core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@earendil-works/pi-agent-core')>();
return {
...actual,
Agent: class extends actual.Agent {
Expand Down
Loading
Loading