Skip to main content

Agent System Technical Reference

Complete technical reference for crocbot’s agent orchestration system. Covers configuration, system prompts, model selection, execution flow, sub-agents, memory, and tool policies.

Table of Contents

  1. Architecture Overview
  2. Configuration Schema
  3. System Prompts
  4. Model Selection
  5. Execution Pipeline
  6. Sub-Agents
  7. Tool Policies
  8. Memory & Context
  9. Agent Types
  10. Key Functions Reference
  11. Extending the Agent System

Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────┐
│                            Agent Orchestration                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────┐    ┌─────────────────┐    ┌──────────────────────────────┐ │
│  │   Config    │───▶│  Agent Scope    │───▶│     Model Selection          │ │
│  │  (agents)   │    │  Resolution     │    │  (provider/model + auth)     │ │
│  └─────────────┘    └─────────────────┘    └──────────────────────────────┘ │
│         │                   │                           │                   │
│         ▼                   ▼                           ▼                   │
│  ┌─────────────┐    ┌─────────────────┐    ┌──────────────────────────────┐ │
│  │  Bindings   │    │ System Prompt   │    │   Auth Profile Store         │ │
│  │  (routing)  │    │    Builder      │    │   (API keys + cooldown)      │ │
│  └─────────────┘    └─────────────────┘    └──────────────────────────────┘ │
│                             │                           │                   │
│                             ▼                           ▼                   │
│                    ┌─────────────────────────────────────────────────────┐  │
│                    │              runEmbeddedPiAgent()                   │  │
│                    │  ├── Context window guard                           │  │
│                    │  ├── Build payloads (system + user messages)        │  │
│                    │  ├── Run attempt with model/auth                    │  │
│                    │  ├── Stream messages + handle tool calls            │  │
│                    │  ├── Fallback on failure (next model in chain)      │  │
│                    │  └── Compaction on context overflow                 │  │
│                    └─────────────────────────────────────────────────────┘  │
│                                        │                                    │
│                    ┌───────────────────┼────────────────────┐               │
│                    ▼                   ▼                    ▼               │
│           ┌──────────────┐    ┌──────────────┐    ┌──────────────────────┐  │
│           │  Sub-Agent   │    │   Session    │    │   Cron/Isolated      │  │
│           │   Spawning   │    │  Transcript  │    │      Agents          │  │
│           └──────────────┘    └──────────────┘    └──────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Source File Map

CategoryFilesPurpose
Config Typessrc/config/types.agents.tsAgentConfig, AgentsConfig, AgentBinding
Config Typessrc/config/types.agent-defaults.tsAgentDefaultsConfig, CliBackendConfig
Agent Scopesrc/agents/agent-scope.tsresolveAgentConfig(), resolveDefaultAgentId()
System Promptssrc/agents/system-prompt.tsbuildAgentSystemPrompt()
System Promptssrc/agents/subagent-announce.tsbuildSubagentSystemPrompt()
Model Selectionsrc/agents/model-selection.tsresolveDefaultModelForAgent(), parseModelRef()
Model Fallbacksrc/agents/model-fallback.tsrunWithModelFallback()
Model Authsrc/agents/model-auth.tsAuth profile store, API key resolution
Main Executionsrc/agents/pi-embedded-runner/run.tsrunEmbeddedPiAgent()
Model Discoverysrc/agents/pi-embedded-runner/model.tsresolveModel()
Tool Policiessrc/agents/pi-tools.policy.tsresolveSubagentToolPolicy(), filterToolsByPolicy()
Defaultssrc/agents/defaults.tsDEFAULT_PROVIDER, DEFAULT_MODEL, DEFAULT_CONTEXT_TOKENS
Sub-Agent Spawnsrc/agents/tools/sessions-spawn-tool.tscreateSessionsSpawnTool()
Sub-Agent Registrysrc/agents/subagent-registry.tsregisterSubagentRun()
Context Guardsrc/agents/context-window-guard.tsevaluateContextWindowGuard()
Compactionsrc/agents/compaction.tscompactEmbeddedPiSessionDirect()
Cron Agentsrc/cron/isolated-agent/run.tsrunCronIsolatedAgentTurn()
CLI Agentsrc/agents/cli-runner.tsrunCliAgent()

Configuration Schema

Agent List (config.agents.list[])

type AgentConfig = {
  id: string;                          // Unique agent identifier
  default?: boolean;                   // Mark as default agent
  name?: string;                       // Human-readable name
  workspace?: string;                  // Working directory (default: ~/croc-{agentId})
  agentDir?: string;                   // Agent state dir (default: ~/.crocbot/agents/{agentId}/agent)
  model?: AgentModelConfig;            // Model override (see below)
  memorySearch?: MemorySearchConfig;   // Per-agent memory search settings
  humanDelay?: HumanDelayConfig;       // Delay between block replies
  heartbeat?: HeartbeatConfig;         // Per-agent heartbeat overrides
  identity?: IdentityConfig;           // Agent identity/persona
  groupChat?: GroupChatConfig;         // Group chat behavior
  subagents?: {
    allowAgents?: string[];            // Allow spawning under other agent ids ("*" = any)
    model?: AgentModelConfig;          // Default model for spawned sub-agents
  };
  sandbox?: {
    mode?: "off" | "non-main" | "all"; // Sandbox mode
    workspaceAccess?: "none" | "ro" | "rw";
    sessionToolsVisibility?: "spawned" | "all";
    scope?: "session" | "agent" | "shared";
    docker?: SandboxDockerSettings;
    browser?: SandboxBrowserSettings;
    prune?: SandboxPruneSettings;
  };
  tools?: AgentToolsConfig;            // Per-agent tool policies
};

Model Configuration

type AgentModelConfig =
  | string                              // "provider/model" format
  | {
      primary?: string;                 // Primary model
      fallbacks?: string[];             // Fallback chain
    };

Agent Defaults (config.agents.defaults)

type AgentDefaultsConfig = {
  model?: {
    primary?: string;                   // Default primary model
    fallbacks?: string[];               // Default fallback chain
  };
  imageModel?: {
    primary?: string;                   // Image-capable model
    fallbacks?: string[];
  };
  models?: Record<string, {             // Model catalog with aliases
    alias?: string;                     // Short alias (e.g., "opus" → "anthropic/claude-opus-4-5")
    params?: Record<string, unknown>;   // Provider-specific params
  }>;
  workspace?: string;                   // Default workspace
  userTimezone?: string;                // IANA timezone (e.g., "Asia/Jerusalem")
  timeFormat?: "auto" | "12" | "24";    // Time format preference
  contextTokens?: number;               // Display-only context window
  thinkingDefault?: ThinkLevel;         // Default thinking level
  verboseDefault?: "off" | "on" | "full";
  elevatedDefault?: "off" | "on" | "ask" | "full";
  maxConcurrent?: number;               // Max concurrent agent runs (default: 1)
  timeoutSeconds?: number;              // Run timeout
  heartbeat?: HeartbeatConfig;          // Periodic background runs
  subagents?: {
    maxConcurrent?: number;             // Max concurrent sub-agents (default: 1)
    archiveAfterMinutes?: number;       // Auto-archive after N minutes
    model?: AgentModelConfig;           // Default sub-agent model
  };
  cliBackends?: Record<string, CliBackendConfig>;  // CLI fallback backends
  compaction?: AgentCompactionConfig;   // Compaction tuning
  memorySearch?: MemorySearchConfig;    // Vector memory defaults
  sandbox?: SandboxConfig;              // Default sandbox settings
};

Agent Bindings (config.bindings[])

Routes channels/chats to specific agents:
type AgentBinding = {
  agentId: string;                      // Target agent
  match: {
    channel: string;                    // "telegram", "whatsapp", etc.
    accountId?: string;                 // Specific account
    peer?: {
      kind: "dm" | "group" | "channel";
      id: string;
    };
    guildId?: string;                   // Discord guild
    teamId?: string;                    // Slack team
  };
};

JSON Config Example

{
  "agents": {
    "defaults": {
      "model": {
        "primary": "openai-codex/gpt-5.1-codex-mini",
        "fallbacks": ["anthropic/claude-opus-4-5", "google/gemini-2.5-pro"]
      },
      "models": {
        "anthropic/claude-opus-4-5": { "alias": "opus" },
        "openai-codex/gpt-5.1-codex-mini": { "alias": "codex" }
      },
      "workspace": "~/croc",
      "userTimezone": "Asia/Jerusalem",
      "maxConcurrent": 2,
      "subagents": {
        "maxConcurrent": 4,
        "archiveAfterMinutes": 30
      }
    },
    "list": [
      {
        "id": "main",
        "default": true,
        "name": "Krox",
        "workspace": "~/croc",
        "identity": {
          "name": "Krox",
          "emoji": "🐊"
        }
      }
    ]
  },
  "bindings": [
    {
      "agentId": "main",
      "match": { "channel": "telegram" }
    }
  ]
}

System Prompts

Main System Prompt

File: src/agents/system-prompt.ts Function: buildAgentSystemPrompt(params) The system prompt is assembled from multiple sections based on promptMode:
ModeDescriptionUse Case
"full"All sectionsMain agent
"minimal"Reduced sections (tooling, workspace, runtime)Sub-agents
"none"Basic identity line onlyMinimal contexts

Prompt Sections

You are a personal assistant running inside crocbot.

## Tooling
Tool availability (filtered by policy):
- read: Read file contents
- write: Create or overwrite files
- edit: Make precise edits to files
- exec: Run shell commands (pty available for TTY-required CLIs)
- web_search: Search the web (Brave API)
- web_fetch: Fetch and extract readable content from a URL
- message: Send messages and channel actions
- sessions_spawn: Spawn a sub-agent session
[... more tools based on availability ...]

## Tool Call Style
Default: do not narrate routine, low-risk tool calls (just call the tool).
[...]

## crocbot CLI Quick Reference
[... CLI help ...]

## Skills (mandatory)                    # Skipped in minimal mode
[... skills guidance ...]

## Memory Recall                         # Skipped in minimal mode
Before answering anything about prior work, decisions, dates...
run memory_search on MEMORY.md + memory/*.md [...]

## Model Aliases                         # Skipped in minimal mode
[... alias mappings ...]

## Workspace
Your working directory is: /home/user/croc
[...]

## Documentation                         # Skipped in minimal mode
crocbot docs: /home/user/projects/crocbot/docs
[...]

## User Identity                         # Skipped in minimal mode
Owner numbers: +1234567890. Treat messages from these numbers as the user.

## Current Date & Time
Time zone: Asia/Jerusalem

## Reply Tags                            # Skipped in minimal mode
[... reply routing ...]

## Messaging                             # Skipped in minimal mode
[... channel/message tool guidance ...]

## Voice (TTS)                           # Skipped in minimal mode
[... TTS hints ...]

## Reactions                             # If configured
[... reaction guidance ...]

## Reasoning Format                      # If reasoning enabled
ALL internal reasoning MUST be inside <think>...</think>.
[...]

# Project Context
[... SOUL.md, CLAUDE.md, etc. injected files ...]

## Silent Replies                        # Skipped in minimal mode
When you have nothing to say, respond with ONLY: [[SILENT_REPLY]]
[...]

## Heartbeats                            # Skipped in minimal mode
[... heartbeat format ...]

## Runtime
Runtime: agent=main | host=krox | os=Linux (x86_64) | node=v22.19.0 | model=openai-codex/gpt-5.1-codex-mini | channel=telegram | capabilities=inlineButtons | thinking=off
Reasoning: off (hidden unless on/stream). Toggle /reasoning; /status shows Reasoning when enabled.

Sub-Agent System Prompt

File: src/agents/subagent-announce.ts Function: buildSubagentSystemPrompt(params) Sub-agents get a minimal, task-focused prompt:
# Subagent Context

You are a **subagent** spawned by the main agent for a specific task.

## Your Role
- You were created to handle: {task description}
- Complete this task. That's your entire purpose.
- You are NOT the main agent. Don't try to be.

## Rules
1. **Stay focused** - Do your assigned task, nothing else
2. **Complete the task** - Your final message will be automatically reported to the main agent
3. **Don't initiate** - No heartbeats, no proactive actions, no side quests
4. **Be ephemeral** - You may be terminated after task completion. That's fine.

## Output Format
When complete, your final response should include:
- What you accomplished or found
- Any relevant details the main agent should know
- Keep it concise but informative

## What You DON'T Do
- NO user conversations (that's main agent's job)
- NO external messages (email, tweets, etc.) unless explicitly tasked
- NO cron jobs or persistent state
- NO pretending to be the main agent
- NO using the `message` tool directly

## Session Context
- Label: {label}
- Requester session: {requesterSessionKey}
- Requester channel: {channel}
- Your session: {childSessionKey}

Injected Context Files

These files are loaded from the workspace and injected into the system prompt:
FilePurpose
SOUL.mdAgent persona and tone
CLAUDE.mdCodebase instructions
BOOTSTRAP.mdAuto-generated during onboarding
MEMORY.mdPersistent memory notes
HEARTBEAT.mdHeartbeat-specific instructions
TOOLS.mdTool usage guidance

Model Selection

Defaults

File: src/agents/defaults.ts
export const DEFAULT_PROVIDER = "anthropic";
export const DEFAULT_MODEL = "claude-opus-4-5";
export const DEFAULT_CONTEXT_TOKENS = 200_000;

Resolution Hierarchy

File: src/agents/model-selection.ts
  1. Per-agent override: config.agents.list[i].model.primary
  2. Global default: config.agents.defaults.model.primary
  3. Fallback: DEFAULT_MODEL constant
function resolveDefaultModelForAgent(params: {
  cfg: crocbotConfig;
  agentId?: string;
}): ModelRef {
  // Check agent-specific override
  const agentModelOverride = params.agentId
    ? resolveAgentModelPrimary(params.cfg, params.agentId)
    : undefined;

  // Merge with defaults and resolve
  return resolveConfiguredModelRef({
    cfg: mergedConfig,
    defaultProvider: DEFAULT_PROVIDER,
    defaultModel: DEFAULT_MODEL,
  });
}

Model Reference Format

type ModelRef = {
  provider: string;  // e.g., "anthropic", "openai-codex", "google"
  model: string;     // e.g., "claude-opus-4-5", "gpt-5.1-codex-mini"
};

// Parsing: "provider/model" or alias lookup
function parseModelRef(raw: string, defaultProvider: string): ModelRef | null {
  // "opus" → alias lookup → { provider: "anthropic", model: "claude-opus-4-5" }
  // "anthropic/claude-opus-4-5" → direct parse
  // "gpt-5" → { provider: defaultProvider, model: "gpt-5" }
}

Model Aliases

Configure shortcuts in config.agents.defaults.models:
{
  "agents": {
    "defaults": {
      "models": {
        "anthropic/claude-opus-4-5": { "alias": "opus" },
        "anthropic/claude-sonnet-4": { "alias": "sonnet" },
        "openai-codex/gpt-5.1-codex-mini": { "alias": "codex" }
      }
    }
  }
}
Usage: /model opus resolves to anthropic/claude-opus-4-5

Fallback Chain

File: src/agents/model-fallback.ts When a model fails (auth error, rate limit, timeout), the system tries the next model in the fallback chain:
// Config
{
  "model": {
    "primary": "openai-codex/gpt-5.1-codex-mini",
    "fallbacks": ["anthropic/claude-opus-4-5", "google/gemini-2.5-pro"]
  }
}

// Execution order on failure:
// 1. openai-codex/gpt-5.1-codex-mini (primary)
// 2. anthropic/claude-opus-4-5 (first fallback)
// 3. google/gemini-2.5-pro (second fallback)

Auth Profile Store

File: src/agents/model-auth.ts Location: ~/.crocbot/agents/{agentId}/auth-profiles.json
type AuthProfileStore = {
  profiles: Record<string, {
    provider: string;
    apiKey: string;
    region?: string;
    lastUsed?: number;
    failure?: {
      reason: string;
      cooldownUntil: number;
    };
  }>;
};
Key Functions:
  • ensureAuthProfileStore(agentDir) - Load/create store
  • resolveAuthProfileOrder(params) - Determine profile evaluation order
  • markAuthProfileUsed(store, profileId) - Update last-used timestamp
  • markAuthProfileFailure(store, profileId, reason) - Mark failed with cooldown
  • isProfileInCooldown(store, profileId) - Check cooldown status
  • getApiKeyForModel(provider, modelId, store) - Retrieve API key

Execution Pipeline

Main Entry Point

File: src/agents/pi-embedded-runner/run.ts Function: runEmbeddedPiAgent(params)
type RunEmbeddedPiAgentParams = {
  sessionId: string;
  sessionKey?: string;
  provider?: string;
  model?: string;
  workspaceDir: string;
  agentDir?: string;
  config?: crocbotConfig;
  prompt: string;
  systemPrompt?: string;
  extraSystemPrompt?: string;
  lane?: string;
  // ... more params
};

type EmbeddedPiRunResult = {
  status: "ok" | "error" | "timeout" | "compaction" | "failover";
  error?: string;
  usage?: UsageLike;
  model?: string;
  provider?: string;
  // ... more fields
};

Execution Flow

runEmbeddedPiAgent()

├─1. Resolve Session & Global Lanes
│   └── enqueueCommandInLane() for concurrency control

├─2. Resolve Model
│   └── resolveModel(provider, modelId, agentDir, config)

├─3. Context Window Guard
│   ├── Warn if < 32k tokens
│   └── Block if < 16k tokens

├─4. Load Auth Profiles
│   ├── ensureAuthProfileStore()
│   └── resolveAuthProfileOrder() with cooldown

├─5. Build Payloads
│   └── buildEmbeddedRunPayloads()
│       ├── System prompt
│       ├── User messages (from transcript)
│       └── Current prompt

├─6. Run Attempt
│   └── runEmbeddedAttempt()
│       ├── Stream messages
│       ├── Handle tool calls
│       └── Collect assistant response

├─7. Handle Failures
│   ├── Auth error → Try next profile
│   ├── Rate limit → Cooldown + fallback
│   ├── Context overflow → Compaction
│   └── Timeout → Failover to next model

└─8. Return Result
    └── { status, usage, model, provider, ... }

Concurrency Control

File: src/agents/pi-embedded-runner/lanes.ts
// Lane types
const AGENT_LANE_MAIN = "agent";      // Main agent runs
const AGENT_LANE_SUBAGENT = "subagent"; // Sub-agent runs

// Concurrency limits (from config)
// agents.defaults.maxConcurrent (default: 1)
// agents.defaults.subagents.maxConcurrent (default: 1)

Context Window Guard

File: src/agents/context-window-guard.ts
const CONTEXT_WINDOW_WARN_BELOW_TOKENS = 32_000;
const CONTEXT_WINDOW_HARD_MIN_TOKENS = 16_000;

function evaluateContextWindowGuard(params): {
  tokens: number;
  source: string;
  shouldWarn: boolean;
  shouldBlock: boolean;
}

Compaction

File: src/agents/compaction.ts When context overflows, old turns are summarized:
// Triggered by context overflow errors
compactEmbeddedPiSessionDirect({
  sessionId,
  messages,
  provider,
  model,
  // ...
})

// Config tuning
{
  "agents": {
    "defaults": {
      "compaction": {
        "mode": "default" | "safeguard",
        "reserveTokensFloor": 0,
        "maxHistoryShare": 0.5,
        "memoryFlush": {
          "enabled": true,
          "softThresholdTokens": 10000
        }
      }
    }
  }
}

Sub-Agents

Spawning via sessions_spawn Tool

File: src/agents/tools/sessions-spawn-tool.ts
const SessionsSpawnToolSchema = Type.Object({
  task: Type.String(),                     // Task description
  label: Type.Optional(Type.String()),     // Display label
  agentId: Type.Optional(Type.String()),   // Target agent (default: same as requester)
  model: Type.Optional(Type.String()),     // Model override
  thinking: Type.Optional(Type.String()),  // Thinking level
  runTimeoutSeconds: Type.Optional(Type.Number()),
  cleanup: Type.Optional(Type.Enum(["delete", "keep"])),
});

Spawn Flow

sessions_spawn({ task: "Research X" })

├─1. Parse requester session key → agent ID

├─2. Check allowAgents permission
│   └── config.agents[i].subagents.allowAgents

├─3. Generate child session key
│   └── "subagent:{uuid}"

├─4. Register sub-agent run
│   └── registerSubagentRun({ runId, childSessionKey, task, cleanup })

├─5. Build child system prompt
│   └── buildSubagentSystemPrompt({ task, requesterSessionKey, ... })

├─6. Call gateway to spawn
│   └── callGateway({ method: "agent", params: { sessionKey, message, ... } })

└─7. Return session key to requester

Sub-Agent Announce Flow

File: src/agents/subagent-announce.ts When a sub-agent completes, results are announced to the requester:
runSubagentAnnounceFlow({
  requesterSessionKey,
  childSessionKey,
  task,
  // ...
})
// Sends summary message to requester session

Sub-Agent Registry

File: src/agents/subagent-registry.ts
type SubagentRunRecord = {
  runId: string;
  childSessionKey: string;
  requesterSessionKey: string;
  task: string;
  cleanup: "delete" | "keep";
  startedAt: number;
};

// In-memory registry
const subagentRuns = new Map<string, SubagentRunRecord>();

// Persistence
// ~/.crocbot/agents/{agentId}/subagent-runs.json

Tool Policies

Default Sub-Agent Deny List

File: src/agents/pi-tools.policy.ts
const DEFAULT_SUBAGENT_TOOL_DENY = [
  // Session management - main agent orchestrates
  "sessions_list",
  "sessions_history",
  "sessions_send",
  "sessions_spawn",     // No nested spawning

  // System admin - dangerous from subagent
  "gateway",
  "agents_list",

  // Interactive setup - not a task
  "whatsapp_login",

  // Status/scheduling - main agent coordinates
  "session_status",
  "cron",

  // Memory - pass relevant info in spawn prompt instead
  "memory_search",
  "memory_get",
];

Policy Resolution

// Global policy
config.tools.allow / config.tools.deny

// Per-provider policy
config.tools.byProvider["anthropic"].allow / .deny
config.tools.byProvider["anthropic/claude-opus-4-5"].allow / .deny

// Per-agent policy
config.agents.list[i].tools.allow / .deny
config.agents.list[i].tools.byProvider[...].allow / .deny

// Sub-agent policy
config.tools.subagents.tools.allow / .deny

Tool Filtering

function filterToolsByPolicy(
  tools: AnyAgentTool[],
  policy?: SandboxToolPolicy
): AnyAgentTool[] {
  // Apply allow/deny patterns
  // Supports wildcards: "group:*", "sessions_*"
}

Tool Groups

Expand via expandToolGroups():
GroupTools
group:fileread, write, edit, ls, find
group:execexec, process
group:webweb_search, web_fetch, browser
group:sessionssessions_list, sessions_history, sessions_send, sessions_spawn
group:admingateway, agents_list, cron

Memory & Context

Session Transcript

Location: ~/.crocbot/sessions/{sessionId}.jsonl Each line is a JSON-serialized message:
type AgentMessage = {
  role: "user" | "assistant" | "system";
  content: string | ContentBlock[];
  timestamp?: number;
  // Tool calls, tool results, etc.
};

Session Store

File: src/config/sessions.ts
// Location: ~/.crocbot/sessions/{store}.json (default: sessions.json)
type SessionStore = Record<string, SessionEntry>;

type SessionEntry = {
  sessionId: string;
  model?: string;
  modelProvider?: string;
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
  // Delivery context, timestamps, etc.
};
File: src/agents/memory-search.ts
type MemorySearchConfig = {
  enabled?: boolean;
  sources?: Array<"memory" | "sessions">;
  provider?: "openai" | "gemini" | "local";
  model?: string;
  store?: {
    driver?: "sqlite";
    path?: string;
    vector?: { enabled?: boolean };
  };
  query?: {
    maxResults?: number;
    minScore?: number;
    hybrid?: { enabled?: boolean; vectorWeight?: number };
  };
};
Tools:
  • memory_search - Semantic search across MEMORY.md + memory/
  • memory_get - Retrieve specific lines by path

Context Pruning

File: src/config/types.agent-defaults.ts
type AgentContextPruningConfig = {
  mode?: "off" | "cache-ttl";
  ttl?: string;                    // Duration (e.g., "30m")
  keepLastAssistants?: number;
  softTrimRatio?: number;
  hardClearRatio?: number;
  tools?: { allow?: string[]; deny?: string[] };
  softTrim?: { maxChars?: number; headChars?: number; tailChars?: number };
  hardClear?: { enabled?: boolean; placeholder?: string };
};

Agent Types

Main Agent

  • Bound to channels via config.bindings
  • Full system prompt (promptMode: "full")
  • All tools available (subject to policy)
  • Persistent across sessions
  • Can spawn sub-agents

Sub-Agents

  • Spawned by main agent via sessions_spawn
  • Minimal system prompt (promptMode: "minimal")
  • Reduced tool set (no session/admin/memory tools)
  • Ephemeral (may be cleaned up after task)
  • Cannot spawn further sub-agents

Cron/Isolated Agents

File: src/cron/isolated-agent/run.ts
runCronIsolatedAgentTurn(params: {
  job: CronJob;
  config: crocbotConfig;
  // ...
}): Promise<RunCronAgentTurnResult>
  • Triggered by cron scheduler
  • Isolated session ({agentId}:cron:{jobId})
  • Scoped to agent workspace

CLI Agents

File: src/agents/cli-runner.ts
runCliAgent(params: {
  provider: string;
  model: string;
  prompt: string;
  // ...
}): Promise<CliAgentResult>
  • Text-only fallback (no tools)
  • Uses external CLI backends (e.g., claude-cli)
  • Configured via config.agents.defaults.cliBackends

Key Functions Reference

Agent Configuration

FunctionFilePurpose
resolveAgentConfig(cfg, agentId)agent-scope.tsGet config for specific agent
resolveDefaultAgentId(cfg)agent-scope.tsGet default agent ID
resolveAgentModelPrimary(cfg, agentId)agent-scope.tsGet agent’s primary model
listAgentEntries(cfg)agents.config.tsList all agents
applyAgentConfig(cfg, params)agents.config.tsUpdate agent config

System Prompts

FunctionFilePurpose
buildAgentSystemPrompt(params)system-prompt.tsBuild main agent prompt
buildSubagentSystemPrompt(params)subagent-announce.tsBuild sub-agent prompt
buildRuntimeLine(info)system-prompt.tsBuild runtime info line

Model Selection

FunctionFilePurpose
resolveDefaultModelForAgent(params)model-selection.tsGet agent’s model
parseModelRef(raw, defaultProvider)model-selection.tsParse “provider/model”
resolveModelRefFromString(params)model-selection.tsResolve with alias lookup
buildModelAliasIndex(params)model-selection.tsBuild alias→ref map

Execution

FunctionFilePurpose
runEmbeddedPiAgent(params)pi-embedded-runner/run.tsMain execution entry
runEmbeddedAttempt(params)pi-embedded-runner/run/attempt.tsSingle run attempt
resolveModel(provider, model, agentDir, cfg)pi-embedded-runner/model.tsResolve model info
evaluateContextWindowGuard(params)context-window-guard.tsCheck context limits
compactEmbeddedPiSessionDirect(params)compaction.tsCompact on overflow

Tool Policies

FunctionFilePurpose
resolveSubagentToolPolicy(cfg)pi-tools.policy.tsGet sub-agent policy
filterToolsByPolicy(tools, policy)pi-tools.policy.tsApply allow/deny
resolveEffectiveToolPolicy(params)pi-tools.policy.tsGet effective policy
isToolAllowedByPolicyName(name, policy)pi-tools.policy.tsCheck single tool

Sub-Agents

FunctionFilePurpose
createSessionsSpawnTool(opts)sessions-spawn-tool.tsCreate spawn tool
registerSubagentRun(record)subagent-registry.tsRegister sub-agent
runSubagentAnnounceFlow(params)subagent-announce.tsAnnounce completion

Extending the Agent System

Adding a New Agent Type

  1. Define Config Type in src/config/types.agents.ts:
    export type CustomAgentConfig = {
      // Custom fields
    };
    
  2. Add System Prompt Builder in src/agents/:
    export function buildCustomAgentSystemPrompt(params: {
      // ...
    }): string {
      return [
        "# Custom Agent Context",
        "...",
      ].join("\n");
    }
    
  3. Create Execution Function:
    export async function runCustomAgent(params: {
      // ...
    }): Promise<CustomAgentResult> {
      // Execution logic
    }
    

Adding Custom Tool Policies

  1. Define Policy in config:
    {
      "tools": {
        "customPolicy": {
          "allow": ["read", "write", "exec"],
          "deny": ["gateway", "sessions_*"]
        }
      }
    }
    
  2. Apply in Code:
    const policy = resolveCustomToolPolicy(cfg);
    const filteredTools = filterToolsByPolicy(allTools, policy);
    

Adding Model Providers

  1. Register Provider in src/agents/models-config.providers.ts:
    registerProvider({
      id: "newprovider",
      name: "New Provider",
      authType: "api_key",
      baseUrl: "https://api.newprovider.com/v1",
    });
    
  2. Add Auth Resolution in src/agents/model-auth.ts
  3. Configure in JSON:
    {
      "models": {
        "providers": {
          "newprovider": {
            "baseUrl": "https://api.newprovider.com/v1",
            "models": [
              { "id": "model-1", "contextWindow": 128000 }
            ]
          }
        }
      }
    }
    

Modifying System Prompts

  1. Add Section Builder in src/agents/system-prompt.ts:
    function buildCustomSection(params: { isMinimal: boolean; /* ... */ }) {
      if (params.isMinimal) return [];
      return [
        "## Custom Section",
        "Your custom guidance here.",
        "",
      ];
    }
    
  2. Include in Main Builder:
    const lines = [
      // ... existing sections ...
      ...buildCustomSection({ isMinimal, /* ... */ }),
    ];
    

Quick Reference

Environment Variables

VariablePurpose
ANTHROPIC_API_KEYAnthropic API key
OPENAI_API_KEYOpenAI API key
GOOGLE_API_KEYGoogle AI API key
BRAVE_API_KEYBrave Search API key

File Locations

PathPurpose
~/.crocbot/crocbot.jsonMain config
~/.crocbot/agents/{agentId}/Per-agent state
~/.crocbot/agents/{agentId}/auth-profiles.jsonAuth profiles
~/.crocbot/sessions/Session transcripts
~/croc/Default workspace
~/croc/SOUL.mdAgent persona
~/croc/MEMORY.mdPersistent memory

Config Paths

PathTypeDescription
agents.defaults.model.primarystringDefault model
agents.defaults.model.fallbacksstring[]Fallback chain
agents.defaults.models.{key}.aliasstringModel alias
agents.defaults.workspacestringDefault workspace
agents.defaults.userTimezonestringIANA timezone
agents.defaults.maxConcurrentnumberConcurrency limit
agents.defaults.subagents.maxConcurrentnumberSub-agent concurrency
agents.list[i].idstringAgent ID
agents.list[i].modelstring/objectPer-agent model
agents.list[i].tools.allowstring[]Tool allowlist
agents.list[i].tools.denystring[]Tool denylist
bindings[i].agentIdstringBound agent
bindings[i].match.channelstringChannel match

Last updated: 2026-02-01