Skip to content

Support source-agnostic external agent session imports #2499

Description

@likun666661

Problem

Maka users may already have valuable coding sessions in Codex, Claude Code, and other agents. Today, Maka can discover some foreign sessions and build a distilled handoff digest, but it cannot migrate a complete external session into Maka as a native Session.

The difficult part is not a single Codex import endpoint. Every external agent has its own storage layout and serialized message format. If format parsing, message cleanup, target Session creation, and product entry points are mixed together, every new agent integration will duplicate infrastructure and leak source-specific branches into Maka.

At the same time, a generic ExternalSessionCleaner or universal external-message intermediate representation would add the wrong abstraction. An external transcript is already a sequence of raw conversation records. The source-specific work is only to convert those records into Maka's existing raw message representation: StoredMessage.

Desired outcome

Provide one small internal abstraction through which multiple external agents can be imported:

  • each source-specific adapter discovers and reads its own native Session format;
  • the adapter converts native records directly to Maka StoredMessage[];
  • there is no shared cleaner, context builder, summary layer, or second message model;
  • one source-agnostic importer validates and atomically persists the converted Session;
  • adding another agent requires implementing and registering one adapter, without changing the importer or Maka persistence logic.

The first PR should establish this internal boundary only. It should not expose a new IPC, CLI, HTTP, or public product interface yet.

Design principles

  1. Adapters own formats. Codex, Claude Code, and future agents parse only their own native storage and serialization details.
  2. Maka already owns the canonical message model. Adapters emit StoredMessage[] directly; there is no ExternalMessage IR.
  3. No ExternalSessionCleaner. Any normalization required by a source is part of that source's adapter conversion.
  4. No context synthesis. Migration preserves raw conversation records; it does not summarize them into a prompt or rebuild context.
  5. The importer is deliberately boring. It selects an adapter, applies target Session settings, validates Maka messages, and commits atomically.
  6. No partial Sessions. One invalid converted message must reject the complete import before Session metadata becomes visible.
  7. External stores remain read-only. An adapter must never mutate the source agent's files or database.

Proposed architecture

flowchart LR
  subgraph Sources["External agent stores (read-only)"]
    Codex["Codex Session"]
    Claude["Claude Code Session"]
    Other["Other Agent Session"]
  end

  subgraph Adapters["Source-specific adapters"]
    CodexAdapter["CodexSessionAdapter"]
    ClaudeAdapter["ClaudeCodeSessionAdapter"]
    OtherAdapter["OtherSessionAdapter"]
    Contract["ExternalSessionAdapter contract"]
  end

  subgraph Maka["Source-agnostic Maka import path"]
    Registry["ExternalSessionAdapterRegistry"]
    Importer["ExternalSessionImporter"]
    Validation["StoredMessage canonical validation"]
    Store["SessionAuthorityStore.createImportedSession"]
    Database[("Maka Session metadata + raw messages")]
  end

  Codex --> CodexAdapter
  Claude --> ClaudeAdapter
  Other --> OtherAdapter

  CodexAdapter --> Contract
  ClaudeAdapter --> Contract
  OtherAdapter --> Contract

  Importer -->|"resolve by adapter id"| Registry
  Registry -.->|"select"| Contract
  Contract -->|"ExternalMakaSession / StoredMessage[]"| Importer
  Importer --> Validation --> Store --> Database
Loading

The dependency direction is one-way: source adapters depend on the Maka message contract; Maka persistence never depends on Codex, Claude Code, or another source format.

Abstract interfaces

type ExternalAgentId = string;

interface ExternalSessionQuery {
  cwd?: string;
  includeArchived?: boolean;
}

interface ExternalSessionSummary {
  id: string;
  name: string;
  cwd: string;
  createdAt?: number;
  updatedAt?: number;
  archived?: boolean;
}

interface ExternalMakaSession {
  sourceSessionId: string;
  metadata: {
    name: string;
    cwd: string;
  };
  messages: readonly StoredMessage[];
}

interface ExternalSessionAdapter {
  readonly id: ExternalAgentId;

  detect(): Promise<boolean>;
  listSessions(
    query?: ExternalSessionQuery,
  ): Promise<readonly ExternalSessionSummary[]>;
  readSession(sessionId: string): Promise<ExternalMakaSession>;
}

Adapters are resolved through a registry with these semantics:

class ExternalSessionAdapterRegistry {
  register(adapter: ExternalSessionAdapter): void;
  get(id: ExternalAgentId): ExternalSessionAdapter | undefined;
  require(id: ExternalAgentId): ExternalSessionAdapter;
  list(): readonly ExternalSessionAdapter[];
}

The generic importer does not understand any source format:

interface ExternalSessionImportRequest {
  adapterId: ExternalAgentId;
  sourceSessionId: string;
  target: Omit<CreateSessionInput, 'cwd' | 'name'> & {
    cwd?: string;
    name?: string;
  };
}

class ExternalSessionImporter {
  import(request: ExternalSessionImportRequest): Promise<SessionHeader>;
}

The importer writes through one atomic Maka storage operation:

interface SessionAuthorityStore {
  createImportedSession(
    input: CreateSessionInput,
    messages: readonly StoredMessage[],
  ): Promise<SessionHeader>;
}

Before persistence, every adapter-produced message is round-tripped through Maka's canonical stored-message decoder. Header creation, messages, and catalog projections are then committed in one transaction.

PR stages

PR 1 — Internal abstraction and atomic import foundation

  • add ExternalSessionAdapter and ExternalSessionAdapterRegistry;
  • add the source-agnostic ExternalSessionImporter;
  • add SessionAuthorityStore.createImportedSession(...);
  • validate converted StoredMessage[] before writing;
  • atomically persist the Session header, raw messages, and catalog projection;
  • add contract and persistence tests;
  • do not add a public product entry point.

PR 2 — Codex adapter

  • document and fixture the supported Codex Session/rollout formats;
  • implement read-only Codex discovery, listing, and conversion;
  • map Codex native records directly to Maka StoredMessage variants;
  • add golden fixtures for text, tool calls/results, malformed records, archive state, ordering, and timestamp behavior;
  • register the adapter internally.

PR 3 — Explicit Maka import workflow

  • add the chosen desktop/CLI interaction for selecting a source and Session;
  • show import progress and actionable conversion errors;
  • define duplicate-import behavior and source provenance if product requirements need it;
  • keep the workflow dependent only on the registry/importer abstraction.

PR 4 — Additional agent adapters and hardening

  • implement Claude Code and other adapters independently;
  • add bounded reads, source-version compatibility fixtures, and observability;
  • verify that adding an adapter requires no changes to the generic importer or Session persistence path.

Acceptance criteria

  • A fake adapter can import user and assistant StoredMessage records and read them back unchanged through Maka's Session store.
  • Target Maka settings may override source name and cwd; source format logic remains inside the adapter.
  • Invalid adapter output rejects the import without exposing a partial Session.
  • Duplicate or missing adapter registrations fail explicitly.
  • The first PR adds no IPC, CLI, HTTP, or renderer surface.
  • There is no ExternalSessionCleaner, shared external-message IR, or context-generation step.

Non-goals for the first PR

  • implementing the Codex parser itself;
  • migrating provider credentials, execution authority, or resumable external runtime state;
  • pretending old tool results are current execution authority;
  • replacing the existing sanitized digest handoff path;
  • exposing the import capability directly to users before the internal contract is proven.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions