Package Overview
Craft Agents uses workspace packages to share code across apps without publishing to npm. The two primary packages are:
@craft-agent/core Shared TypeScript types and interfaces
@craft-agent/shared Business logic, agent implementation, auth, config
@craft-agent/core
The type definition layer for the entire monorepo. It provides zero-runtime-cost type safety.
Purpose
Shared TypeScript types used by Electron app and shared packages
No runtime code (pure types)
Peer dependencies on SDKs to avoid bundling duplicates
Directory Structure
packages/core/
├── src/
│ ├── index.ts # Main entry point
│ ├── types/
│ │ ├── index.ts # Type re-exports
│ │ ├── workspace.ts # Workspace, auth, config types
│ │ ├── session.ts # Session, metadata types
│ │ └── message.ts # Message, token, event types
│ └── utils/
│ ├── index.ts # Utility re-exports
│ └── debug.ts # Debug logging stub
├── package.json
├── tsconfig.json
└── CLAUDE.md # Agent-readable docs
Type Categories
Workspace Types (types/workspace.ts)
Type Description WorkspaceWorkspace configuration with MCP URL and auth McpAuthTypeMCP authentication: workspace_oauth, workspace_bearer, public AuthTypeAPI setup method: api_key, oauth_token OAuthCredentialsOAuth tokens from authentication flow StoredConfigFull application configuration CumulativeUsageGlobal token/cost tracking
Session Types (types/session.ts)
Type Description SessionConversation scope with SDK session binding StoredSessionSession with persisted messages and tokens SessionMetadataLightweight session info for listings
Message Types (types/message.ts)
Type Description MessageRuntime message with all fields StoredMessagePersisted message format MessageRoleuser, assistant, tool, error, statusToolStatuspending, executing, completed, errorTokenUsageInput/output/cache token counts and cost AgentEventEvents from agent during chat
Exports
// Main export
import type { Workspace , Session , Message } from '@craft-agent/core' ;
// Subpath exports
import type { Session } from '@craft-agent/core/types' ;
import { generateMessageId } from '@craft-agent/core/utils' ;
{
"exports" : {
"." : "./src/index.ts" ,
"./types" : "./src/types/index.ts" ,
"./utils" : "./src/utils/index.ts"
}
}
Design Decisions
Sessions are the primary boundary , not workspaces. Each session has a unique ID and maps 1:1 with an SDK session.
MCP Auth Separation:
Craft OAuth (craft_oauth::global) is ONLY for the Craft API. Each MCP server has its own OAuth via workspace_oauth::{workspaceId}.
Future Migration
Currently, @craft-agent/core only exports types. The migration plan:
Phase 1 (Current): Types only
Phase 2: Move storage logic to core
Phase 3: Move auth, credentials, MCP client
Phase 4: Move agent logic, prompts
@craft-agent/shared
The business logic package containing agent implementation, auth, config, MCP integration, and utilities.
Directory Structure
packages/shared/
├── src/
│ ├── agent/ # Agent backends, permissions, tools
│ │ ├── backend/ # Claude/Pi abstraction layer
│ │ ├── claude-agent.ts # Claude SDK implementation
│ │ ├── pi-agent.ts # Pi SDK implementation
│ │ ├── mode-manager.ts # Permission mode logic
│ │ └── session-scoped-tools.ts
│ ├── auth/ # OAuth, tokens, auth state
│ ├── config/ # Storage, preferences, themes
│ ├── credentials/ # AES-256-GCM encrypted storage
│ ├── mcp/ # MCP client, pool, validation
│ ├── sessions/ # Session persistence, index
│ ├── sources/ # Source types, storage, service
│ ├── automations/ # Event-driven automations
│ ├── skills/ # Skill storage and loading
│ ├── utils/ # Debug, summarization, file handling
│ └── branding.ts # Brand constants
├── package.json
├── tsconfig.json
└── CLAUDE.md
Key Modules
Agent (src/agent/)
claude-agent.ts Wraps Claude Agent SDK with permissions and MCP
pi-agent.ts Wraps Pi SDK with unified interface
mode-manager.ts Permission mode logic (safe/ask/allow-all)
session-scoped-tools.ts Tools available within sessions
Backend Abstraction:
The backend/ directory provides a unified interface for both Claude and Pi agents:
// packages/shared/src/agent/backend/types.ts
export interface AgentBackend {
initialize ( options : InitOptions ) : Promise < void >;
chat ( prompt : string , options ?: ChatOptions ) : AsyncGenerator < AgentEvent >;
abort () : Promise < void >;
cleanup () : Promise < void >;
}
Permission Modes
Three-level permission system per session:
Mode Display Name Behavior 'safe'Explore Read-only, blocks write operations 'ask'Ask to Edit Prompts for bash commands (default) 'allow-all'Auto Auto-approves all commands
import { setPermissionMode , getPermissionMode } from '@craft-agent/shared/agent' ;
setPermissionMode ( sessionId , 'safe' );
const mode = getPermissionMode ( sessionId ); // 'safe'
Permission modes are per-session , not global. SHIFT+TAB cycles modes in the UI.
MCP (src/mcp/)
MCP (Model Context Protocol) integration:
File Purpose mcp-pool.tsCentralized MCP client pool (main process) client.tsMCP client wrapper validation.tsConnection validation api-source-pool-client.tsIn-process API source client
McpClientPool Architecture:
// packages/shared/src/mcp/mcp-pool.ts
export class McpClientPool {
// Sync MCP sources (stdio/SSE)
async sync ( servers : McpServerConfig []) : Promise < void >;
// Sync API sources (REST, Google, Slack)
async syncApiServers ( servers : ApiServerConfig []) : Promise < void >;
// Call tool on any connected source
async callTool ( slug : string , toolName : string , args : object ) : Promise < any >;
}
Main process creates a single McpClientPool instance
pool.sync(mcpServers) connects new MCP sources, disconnects removed ones
pool.syncApiServers(apiServers) connects API sources as in-process clients
Claude backend: proxy tools created via createSourceProxyServers(pool)
Pi backend: proxy tool definitions sent to subprocess
Configuration (src/config/)
File Purpose storage.tsMulti-workspace config at ~/.craft-agent/config.json preferences.tsUser preferences (theme, cache TTL, etc.) theme.tsCascading theme system (app → workspace) watcher.tsFile watcher for live config updates
Theme System:
import { resolveTheme , themeToCSS } from '@craft-agent/shared/config/theme' ;
const theme = resolveTheme ( workspaceId ); // Merges app + workspace themes
const css = themeToCSS ( theme ); // Generate CSS variables
6-color system: background, foreground, accent, info, success, destructive
Credentials (src/credentials/)
Secure credential storage with AES-256-GCM encryption :
import { getCredentialManager } from '@craft-agent/shared/credentials' ;
const manager = getCredentialManager ();
// Store encrypted credential
await manager . set ( 'anthropic_api_key::global' , {
type: 'api_key' ,
apiKey: 'sk-ant-...' ,
});
// Read decrypted credential
const cred = await manager . get ( 'anthropic_api_key::global' );
All credentials stored in ~/.craft-agent/credentials.enc.
Sessions (src/sessions/)
File Purpose storage.tsSession CRUD, JSONL persistence index.tsSession listing and metadata persistence-queue.tsDebounced async writes (500ms)
JSONL Format:
Sessions are stored as JSON Lines (one message per line):
{ "type" : "user" , "content" : "Hello" , "timestamp" : 1234567890 }
{ "type" : "assistant" , "content" : "Hi there!" , "timestamp" : 1234567891 }
JSONL is streaming-friendly and append-only , making it easy to debug and inspect.
Subpath Exports
@craft-agent/shared uses subpath exports for clean imports:
// Agent
import { CraftAgent } from '@craft-agent/shared/agent' ;
import { createBackend } from '@craft-agent/shared/agent/backend' ;
// Config
import { loadStoredConfig } from '@craft-agent/shared/config' ;
import { resolveTheme } from '@craft-agent/shared/config/theme' ;
// Credentials
import { getCredentialManager } from '@craft-agent/shared/credentials' ;
// MCP
import { McpClientPool } from '@craft-agent/shared/mcp' ;
// Sources
import { loadWorkspaceSources } from '@craft-agent/shared/sources' ;
// Utils
import { debug } from '@craft-agent/shared/utils' ;
Full export list (package.json)
{
"exports" : {
"." : "./src/index.ts" ,
"./agent" : "./src/agent/index.ts" ,
"./agent/modes" : "./src/agent/mode-types.ts" ,
"./agent/backend" : "./src/agent/backend/index.ts" ,
"./auth" : "./src/auth/index.ts" ,
"./config" : "./src/config/index.ts" ,
"./config/theme" : "./src/config/theme.ts" ,
"./credentials" : "./src/credentials/index.ts" ,
"./mcp" : "./src/mcp/index.ts" ,
"./sessions" : "./src/sessions/index.ts" ,
"./sources" : "./src/sources/index.ts" ,
"./utils" : "./src/utils/index.ts"
}
}
Internal Structure
Agent Implementation
Both ClaudeAgent and PiAgent implement the AgentBackend interface:
// packages/shared/src/agent/claude-agent.ts
export class ClaudeAgent implements AgentBackend {
async initialize ( options : InitOptions ) : Promise < void > {
// 1. Connect to MCP servers
// 2. Set up permission hooks
// 3. Initialize SDK session
}
async * chat ( prompt : string ) : AsyncGenerator < AgentEvent > {
// 1. Validate permission mode
// 2. Stream agent responses
// 3. Handle tool calls with PreToolUse hook
// 4. Summarize large results with PostToolUse hook
for await ( const event of this . sdk . chat ( prompt )) {
yield event ;
}
}
}
MCP Source Architecture
All source connections (MCP and API) are managed through a centralized pool :
┌─────────────────────────────────────────────────────────────┐
│ McpClientPool (main process) │
│ - Manages all MCP and API connections │
│ - Routes tool calls to correct source │
└─────────────────────────────────────────────────────────────┘
│
├─→ MCP Source (stdio) [Linear, GitHub, custom]
├─→ MCP Source (SSE) [Remote MCP servers]
└─→ API Source (in-proc) [Gmail, Slack, Microsoft]
Dependencies
@craft-agent/core
{
"peerDependencies" : {
"@anthropic-ai/claude-agent-sdk" : ">=0.2.19" ,
"@modelcontextprotocol/sdk" : ">=1.0.0"
}
}
@craft-agent/shared
{
"dependencies" : {
"@craft-agent/core" : "workspace:*" ,
"@craft-agent/session-tools-core" : "workspace:*" ,
"croner" : "^10.0.1" ,
"shell-quote" : "^1.8.3"
},
"peerDependencies" : {
"@anthropic-ai/claude-agent-sdk" : "^0.2.19" ,
"@modelcontextprotocol/sdk" : ">=1.0.0" ,
"zod" : ">=3.0.0"
}
}
Peer dependencies prevent bundling duplicates of large SDKs.
Type Checking
# Check individual package
cd packages/core && bun run tsc --noEmit
cd packages/shared && bun run tsc --noEmit
# Check all packages from root
bun run typecheck:all
Next Steps
Agent Backends Learn how Claude and Pi SDKs are abstracted
Electron App See how packages are used in the main app