Skip to main content

Module Development Guide

This guide explains the design philosophy and interface contracts of PilotDeck core modules.

Principles

Module Boundaries

Each module exposes public API through index.ts and keeps implementation details internal

Dependency Injection

Modules receive dependencies through constructors and interfaces for testability and replacement

Protocol First

Modules define explicit protocol types under protocol/

Testability

Modules should be independently testable with fake or stub dependencies


Gateway Module

Directory: src/gateway/

ObjectResponsibility
GatewayInterface contract for all Gateway implementations
InProcessGatewayIn-process implementation
SessionRouterCreates, finds, and manages sessions
GatewayWsClientWebSocket client wrapper
RemoteGatewayClient-side proxy for a remote Gateway
GatewayElicitationBusHandles ask_user_question prompts
import { createGateway } from "../gateway/index.js";

const gateway = createGateway({
agent: { /* AgentSession options */ },
projectStorage: { projectRoot, pilotHome },
idleSessionTimeoutMs: 30 * 60 * 1000,
});

interface Gateway {
submitTurn(input: GatewaySubmitTurnInput): AsyncIterable<GatewayEvent>;
abortTurn(input: { sessionKey: string }): Promise<void>;
newSession(input: NewSessionInput): Promise<{ sessionKey: string }>;
listSessions(input: ListSessionsInput): Promise<ListSessionsResult>;
}
备注 · Note

When adding a Gateway method, update the protocol interface, InProcessGateway, RemoteGateway, and the WebSocket handler.


Router Module

Directory: src/router/

src/router/
├── RouterRuntime.ts
├── config/schema.ts
├── scenario/decideScenario.ts
├── tokenSaver/classifyAndRoute.ts
├── fallback/runFallbackChain.ts
├── retry/zeroUsageRetry.ts
├── orchestrate/applyOrchestration.ts
├── customRouter/customRouter.ts
├── session/SessionRouterStore.ts
├── stats/TokenStatsCollector.ts
├── protocol/
└── utils/countTokens.ts

To add a TokenSaver tier, update the config schema, classification logic, and tests.

Custom routers implement:

interface PilotDeckCustomRouter {
decide(input: CustomRouterDecideInput): Promise<Partial<RouterDecision> | undefined>;
}

Context Module

Directory: src/context/

ComponentFileResponsibility
PromptAssemblerprompt/PromptAssembler.tsBuild the full System Prompt
MessageProjectorprojection/MessageProjector.tsProject and trim history
TokenBudgetManagerbudget/TokenBudgetManager.tsTrack token budget
CompactionEnginecompaction/CompactionEngine.tsCompress history
MemoryResolvermemory/MemoryResolver.tsLong-term memory retrieval
InstructionDiscoveryinstructions/InstructionDiscovery.tsDiscover project instructions
AttachmentResolverattachments/AttachmentResolver.tsHandle file and image attachments

Compaction has multiple layers:

AutoCompactionPolicy

├── MicroCompaction
├── SnipEngine
├── CompactionEngine
└── ContextOverflowRecovery

A memory provider implements:

interface MemoryResolver {
retrieve(input: MemoryRetrieveInput): Promise<MemoryRetrieveResult>;
captureTurn?(input: MemoryCaptureTurnInput): void;
}

Model Module

Directory: src/model/

All providers are converted into Canonical format:

interface CanonicalModelRequest {
provider: string;
model: string;
messages: CanonicalMessage[];
tools?: CanonicalToolSchema[];
systemPrompt?: string;
}

type CanonicalModelEvent =
| { type: "message_start" }
| { type: "text_delta"; text: string }
| { type: "thinking_delta"; text: string }
| { type: "tool_call_start" }
| { type: "tool_call_delta" }
| { type: "tool_call_end" }
| { type: "message_end"; finishReason: CanonicalFinishReason }
| { type: "error"; error: CanonicalModelError };

To add a provider, create an adapter under providers/, implement request and stream conversion, and register it in ModelProviderRegistry.


Tool Module

Directory: src/tool/

To add a built-in tool:

  1. Create the tool file under builtin/
  2. Define its input schema
  3. Implement PilotDeckToolDefinition
  4. Register it in createBuiltinRegistry
interface PilotDeckToolDefinition {
name: string;
description: string;
inputSchema: PilotDeckToolInputSchema;
execute(
call: PilotDeckToolCall,
context: PilotDeckToolRuntimeContext,
): Promise<PilotDeckToolResult>;
}

Tool scheduling:

  • ConcurrentToolScheduler runs independent operations in parallel
  • SequentialToolScheduler runs dependent or write-sensitive operations in order

Always-On Module

Directory: src/always-on/

ComponentResponsibility
AlwaysOnManagerTop-level manager bound to Gateway
AlwaysOnRuntimeDiscovery runtime
DiscoverySchedulerChecks gates and triggers Discovery
DiscoveryFireRuns one Discovery execution
SignalWatcherWatches file change signals
WorkspaceProviderRegistryProvides git-worktree or snapshot-copy isolation

Notes:

  • Always-On binds to Gateway through bindGateway().
  • It injects Discovery Plan and Report tools.
  • It provides session config overrides for Discovery sessions.