How to Import Existing Sessions from Other Agents into PI-Desktop

PI-Desktop imports external chat sessions through a two-step IPC workflow where the renderer scans for candidates via scanSessionImportCandidates() and imports them via importSessions(), converting external formats into native SQLite-backed sessions through the Rust host core.

PI-Desktop provides a robust mechanism to migrate conversations from other AI agents into its unified chat interface. This guide explains how to import existing sessions from other agents into PI-Desktop using the IPC bridge and Rust host core, based on the actual implementation in the vastsa/PI-Desktop repository.

Understanding the Session Import Architecture

The import system relies on type-safe IPC communication between the Electron renderer and main process. When importing sessions from other agents, PI-Desktop executes a two-phase workflow: first scanning for available external sessions, then converting and persisting them into the internal SQLite database.

The IPC channel names defined in packages/shared/src/protocol.ts guarantee consistent communication:

// packages/shared/src/protocol.ts
sessionImportScan: "pi-desktop/session/importScan",
sessionImportRun: "pi-desktop/session/importRun",

These identifiers map to IPC.invoke.sessionImportScan and IPC.invoke.sessionImportRun, ensuring the renderer and main process speak the same protocol.

Step 1: Scanning for Importable Sessions

The discovery phase begins when the renderer requests an enumeration of supported external session sources. In apps/desktop/electron/main/ipc/session-ipc.ts, the handler for IPC.invoke.sessionImportScan calls scanAllSources() to identify candidates from other agents.

// apps/desktop/electron/main/ipc/session-ipc.ts (lines 59-66)
IPC.handle("sessionImportScan", async () => {
  const candidates = await scanAllSources();
  return { sessions: candidates };
});

The function returns an array of ImportCandidate objects, each containing metadata such as source, externalId, and name. This allows the UI to present available sessions without loading their full content until selected.

Step 2: Converting and Importing Sessions

Once you select candidate sessions, the main process handles the IPC.invoke.sessionImportRun invocation. The implementation in apps/desktop/electron/main/ipc/session-ipc.ts (lines 69-104) iterates through selections, converts each external session using convertSession(item), and commits them via the "session.import" RPC to the Rust host core.

// apps/desktop/electron/main/ipc/session-ipc.ts (lines 69-104)
IPC.handle("sessionImportRun", async (_, selections) => {
  const results = { imported: 0, skipped: 0, failed: 0 };
  
  for (const item of selections) {
    try {
      const session = convertSession(item);
      await host.call("session.import", session);
      results.imported++;
    } catch (error) {
      console.error(`Failed to import session ${item.externalId}:`, error);
      results.failed++;
    }
  }
  
  return results;
});

Because the actual import logic lives in the host core, imported sessions become first-class citizens in PI-Desktop—they appear in the session list, support editing, and are stored in the same SQLite database as native sessions.

Renderer-Side API Integration

The frontend consumes these IPC channels through convenient wrappers in apps/desktop/src/lib/api.ts. These helpers abstract the raw IPC calls and provide typed interfaces for the renderer process.

// apps/desktop/src/lib/api.ts
export async function scanSessionImportCandidates() {
  return invoke<{ sessions: ImportCandidate[] }>(IPC.invoke.sessionImportScan);
}

export async function importSessions(selections: ImportCandidate[]) {
  return invoke<ImportRunResult>(IPC.invoke.sessionImportRun, selections);
}

Use scanSessionImportCandidates() to populate the import dialog, then pass user selections to importSessions() to execute the migration.

Practical Implementation Examples

Import All Available External Sessions

This example demonstrates scanning for all candidates and importing them unconditionally:

import { scanSessionImportCandidates, importSessions } from "./api";

async function importAll() {
  // Step 1: Scan for candidates
  const { sessions } = await scanSessionImportCandidates();

  // Step 2: Import everything (filter here if needed)
  const selections = sessions;

  // Step 3: Run the import
  const result = await importSessions(selections);
  console.log(`Imported: ${result.imported}, Skipped: ${result.skipped}, Failed: ${result.failed}`);
}

importAll().catch(console.error);

Import from a Specific Agent Source

To import sessions only from a particular external agent, filter the candidates by source ID before calling importSessions():

import { scanSessionImportCandidates, importSessions } from "./api";

async function importFromSource(sourceId: string) {
  const { sessions } = await scanSessionImportCandidates();

  const candidate = sessions.find(c => c.source === sourceId);
  if (!candidate) {
    console.warn(`No session found for source ${sourceId}`);
    return;
  }

  const { imported, skipped, failed } = await importSessions([candidate]);
  console.log(`Result – imported: ${imported}, skipped: ${skipped}, failed: ${failed}`);
}

Summary

  • PI-Desktop stores sessions in a Rust host core with SQLite persistence, making imported sessions indistinguishable from native ones.
  • The two-step import process requires first scanning with scanAllSources() in session-ipc.ts, then converting and importing via host.call("session.import", …).
  • IPC channels are type-safe constants defined in packages/shared/src/protocol.ts, accessed through IPC.invoke.sessionImportScan and IPC.invoke.sessionImportRun.
  • The renderer API in apps/desktop/src/lib/api.ts provides scanSessionImportCandidates() and importSessions() to simplify frontend integration.
  • Imported sessions support full editing capabilities and persist in the native database alongside sessions created within PI-Desktop.

Frequently Asked Questions

What external agent formats does PI-Desktop support for session import?

The specific formats depend on the implementation of scanAllSources() in the main process. The architecture is designed to support any external agent that can provide session metadata (name, externalId, source) and conversation content. You can extend support by modifying the scanner logic in apps/desktop/electron/main/ipc/session-ipc.ts.

Are imported sessions editable after migration?

Yes. Because the convertSession() function transforms external formats into PI-Desktop's internal session structure before calling host.call("session.import", …), imported sessions become first-class citizens. They appear in the session list, support editing, and persist in the SQLite database exactly like native sessions.

Where does PI-Desktop store imported sessions?

According to the source code in vastsa/PI-Desktop, imported sessions are handed to the Rust host core via the "session.import" RPC. The host core stores all sessions—whether native or imported—in the same SQLite database, ensuring consistent data management and retrieval performance.

Can I import multiple sessions simultaneously?

Yes. The importSessions() API accepts an array of ImportCandidate objects, and the IPC handler in session-ipc.ts iterates through all selections in a single invocation. The function returns aggregate statistics (imported, skipped, failed) for the entire batch operation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →