Skip to content

Architecture: make AgentContext a first-class serializable value type #63

Description

@zjshen14

Background

ContextManager is today an opaque internal implementation detail of Agent. It holds the conversation history, activated skill bodies, and system instruction, but exposes them only through mutating methods. There is no way to snapshot, export, import, fork, or inspect the context from outside the agent.

Problems this causes

1. Session resumption is reconstructed, not restoredsrc/state/session.ts re-parses a JSONL log to reconstruct Message[] on resume. This is inherently fragile (see issue #58) because the session log format is not the same as the context format — there is a lossy transformation in both directions.

2. Context compaction (issue #26) is architecturally awkward — compaction requires replacing a window of messages with a summary. With context as opaque internal state, compaction must be implemented inside ContextManager itself, making it a god object responsible for both history management and compression strategy.

3. Multi-agent is blocked — a parent agent cannot pass its context to a child agent, or receive a sub-agent's context back, because context is not a transferable value.

4. Forking/branching is impossible — exploratory branches (e.g. "try approach A, if it fails, revert to here and try approach B") require context snapshots.

5. Debugging is opaque — users cannot inspect "what context does the agent have right now?" without reading raw session files.

Proposed design

Make context a plain serializable value type:

// src/core/context.ts
export interface AgentContext {
  messages: Message[];
  activatedSkills: Record<string, string>; // name → injected body
  systemInstruction: string;
  metadata: {
    cwd: string;
    sessionId: string;
    gitSnapshot: string;
    tmpDir?: string;
  };
}

The agent is a processor: (AgentContext, userInput) → (AgentContext, AsyncIterable<AgentEvent>). Context is passed in and a new context is returned (or the old one mutated with a clear snapshot API).

// Fork for exploratory branching
const snapshot = agent.snapshotContext();
await agent.run("try approach A");
if (failed) {
  agent.restoreContext(snapshot);
  await agent.run("try approach B");
}

Session persistence becomes trivial: serialize the AgentContext directly to JSON rather than reconstructing it from an event log.

Acceptance criteria

  • AgentContext is a plain, JSON-serializable interface (no class, no private fields)
  • Agent exposes snapshotContext(): AgentContext and restoreContext(ctx: AgentContext): void
  • Session save/load uses AgentContext serialization directly, replacing reconstructMessages()
  • Context compaction can be implemented as a pure function compact(ctx: AgentContext): AgentContext without touching Agent internals
  • Existing session resumption behaviour is preserved

Location

  • src/agent/context.tsContextManager class
  • src/state/session.tsreconstructMessages() can be replaced
  • src/agent/core.tsAgent class interface

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureStructural design decisions and system-wide refactorsenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions