# AgentCore.jl - Architecture Overview ## Top-Down Architecture ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ AgentCore.jl Layers │ └─────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────┐ │ Level 1: AgentHarness (Session Management & Persistence) │ │ - Session persistence with JSONL storage │ │ - Resource management (skills, prompt templates) │ │ - Extension hooks system │ │ - Branch navigation and compaction │ └─────────────────────────────────────────────────────────────────────────┘ │ │ orchestrates ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Level 2: Agent (State Management & Event Streaming) │ │ - Conversation state (messages, tools, system prompt) │ │ - Event streaming and lifecycle management │ │ - Steering and follow-up message queues │ │ - Abort handling │ └─────────────────────────────────────────────────────────────────────────┘ │ │ delegates to ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Level 3: AgentLoop (Core LLM Interaction Loop) │ │ - Stateful LLM interactions │ │ - Tool execution (parallel or sequential) │ │ - Event emission lifecycle │ │ - Steering/follow-up message handling │ └─────────────────────────────────────────────────────────────────────────┘ │ │ transforms to ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Level 4: Session (Conversation History Management) │ │ - Tree-based conversation history │ │ - Branch support with compaction │ │ - Message and metadata persistence │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## Process Flow ### 1. Agent Lifecycle ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Agent Lifecycle │ └─────────────────────────────────────────────────────────────────────────┘ User Code │ │ 1. Create Agent ▼ ┌──────────────┐ │ Agent() │ ──► Initialize state, queues, listeners └──────────────┘ │ │ 2. Subscribe to events ▼ ┌──────────────────┐ │ subscribe() │ ──► Register event handlers └──────────────────┘ │ │ 3. Run prompt ▼ ┌──────────────────┐ │ prompt() │ ──► Validate input, normalize messages └──────────────────┘ │ │ 4. Start AgentLoop ▼ ┌──────────────────┐ │ runPromptMessages│ ──► Create ActiveRun, spawn loop └──────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ AgentLoop (runs in separate thread) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 1. Emit AgentStartEvent │ │ │ │ 2. Emit TurnStartEvent │ │ │ │ 3. Process prompts (emit MessageStart/End) │ │ │ │ 4. ┌──────────────────────────────────────────────┐ │ │ │ │ │ while true: │ │ │ │ │ │ │ Process steering/follow-up messages │ │ │ │ │ │ │ Stream assistant response (LLM call) │ │ │ │ │ │ │ Execute tool calls (parallel/sequential) │ │ │ │ │ │ │ Emit TurnEndEvent │ │ │ │ │ │ │ Check if should stop │ │ │ │ │ │ │ Get next steering messages │ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ │ │ 5. Event streaming ▼ ┌──────────────────┐ │ Event Handlers │ ──► User-defined listeners receive events └──────────────────┘ │ │ 6. Wait for completion ▼ ┌──────────────────┐ │ waitForIdle() │ ──► Resolve when all events processed └──────────────────┘ ``` ### 2. AgentLoop Flow Diagram ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ AgentLoop Process Flow │ └─────────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────┐ │ AgentLoop Entrypoint │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ agentLoop(prompts, context, config, signal, stream_fn) │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ runAgentLoop(prompts, context, config, emit, signal) │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ runLoop() - Main Event Loop │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ └──────────────────────────────┼───────────────────────────────────────┘ │ │ Loop Iteration ▼ ┌────────────────────────────────────────────────────────────────────┐ │ Main Processing Loop │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 1. Get Steering/Follow-up Messages │ │ │ │ ┌────────────────────┐ ┌──────────────────────┐ │ │ │ │ │ steering_queue │ │ follow_up_queue │ │ │ │ │ │ (after assistant) │ │ (after stop) │ │ │ │ │ └────────────────────┘ └──────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 2. Stream Assistant Response │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ transform_context() │ │ │ │ │ │ convert_to_llm(messages) -> Message[] │ │ │ │ │ │ stream_fn(model, context, config) -> Response │ │ │ │ │ │ - Text deltas │ │ │ │ │ │ - Tool call deltas │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ Emit: MessageStartEvent, MessageUpdateEvent, │ │ │ │ │ │ MessageEndEvent │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 3. Execute Tool Calls │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ extract ToolCall from assistant content │ │ │ │ │ │ │ │ │ │ │ │ if EXECUTION_SEQUENTIAL || has_sequential_tool: │ │ │ │ │ │ executeToolCallsSequential() │ │ │ │ │ │ else: │ │ │ │ │ │ executeToolCallsParallel() │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ For each tool call: │ │ │ │ │ │ 1. before_tool_call hook │ │ │ │ │ │ 2. prepareToolCall() │ │ │ │ │ │ 3. execute() │ │ │ │ │ │ 4. after_tool_call hook │ │ │ │ │ │ 5. Emit ToolExecutionStart/Update/EndEvent │ │ │ │ │ │ 6. Emit ToolResultMessage │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 4. Prepare Next Turn │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ prepare_next_turn(context) -> next_turn_snapshot │ │ │ │ │ │ - Optional: Update model/thinking_level │ │ │ │ │ │ - Optional: Update context │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 5. Check Termination Conditions │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ should_stop_after_turn(context) -> bool │ │ │ │ │ │ - Max turns reached? │ │ │ │ │ │ - Tool returned terminate=true? │ │ │ │ │ │ - Steering queue empty and follow-up empty? │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 6. Emit TurnEndEvent (message, tool_results) │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Loop continues until termination condition met │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ └──────────────────────────────┼───────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────────┐ │ AgentEndEvent with final messages │ └────────────────────────────────────────────────────────────────────┘ ``` ### 3. Tool Execution Flow ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Tool Execution Flow │ └─────────────────────────────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────────────────┐ │ Assistant Message with Tool Calls │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ AssistantMessage: │ │ │ │ content: [ │ │ │ │ TextContent("I'll help you"), │ │ │ │ ToolCall(id="tc1", name="bash", args={...}), │ │ │ │ ToolCall(id="tc2", name="read", args={...}) │ │ │ │ ] │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ └───────────────────────────────────────────────────────────────────┘ │ │ executeToolCalls() ▼ ┌───────────────────────────────────────────────────────────────────┐ │ Determine Execution Mode │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ config.tool_execution == EXECUTION_SEQUENTIAL? │ │ │ │ OR any tool has execution_mode == EXECUTION_SEQUENTIAL? │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────┴───────────────┐ │ │ ▼ ▼ │ │ ┌────────────────────────┐ ┌────────────────────────┐ │ │ │ executeSequential() │ │ executeParallel() │ │ │ └────────────────────────┘ └────────────────────────┘ │ │ │ │ │ └──────────────┼───────────────────────────────┼────────────────────┘ │ │ │ │ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ Sequential Execution │ │ Parallel Execution │ │ │ │ │ │ for tool_call in: │ │ for tool_call in: │ │ prepareToolCall() │ │ prepareToolCall() │ │ execute() │ │ execute() (async) │ │ finalize() │ │ │ │ │ │ wait all results │ │ │ └──────────────────────┘ └──────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ For Each Tool Call │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. before_tool_call hook (optional) │ │ │ │ - Can block execution │ │ │ │ 2. prepareToolCall() │ │ │ │ - validateToolArguments() │ │ │ │ - prepareToolCallArguments() (optional) │ │ │ │ 3. Execute Tool: │ │ │ │ tool.execute(tool_call_id, args, signal, on_update) │ │ │ │ 4. after_tool_call hook (optional) │ │ │ │ - Can modify result content │ │ │ │ 5. Emit events: │ │ │ │ - ToolExecutionStartEvent │ │ │ │ - ToolExecutionUpdateEvent (optional) │ │ │ │ - ToolExecutionEndEvent │ │ │ │ 6. Create ToolResultMessage │ │ │ └─────────────────────────────────────────────────────────────┘ │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ Tool Result Messages │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ ToolResultMessage: │ │ │ │ role: "toolResult" │ │ │ │ tool_call_id: "tc1" │ │ │ │ tool_name: "bash" │ │ │ │ content: [TextContent("command output")] │ │ │ │ is_error: false │ │ │ └─────────────────────────────────────────────────────────────┘ │ └───────────────────────────────────────────────────────────────────┘ ``` ### 4. Session & Tree Structure ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Session Tree Structure │ └─────────────────────────────────────────────────────────────────────────┘ Session = Linked List of Entries (tree structure) ┌───────────────────────────────────────────────────────────────────┐ │ Branch Navigation │ │ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ E1 │────▶│ E2 │────▶│ E3 │────▶│ E4 │────▶│ E5 │ (leaf) │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ │ │ Message Message Compaction Message BranchSummary │ │ │ │ E3 is a Compaction Entry: │ │ - Summary of E1, E2 │ │ - first_kept_entry_id: reference to first retained message │ │ - tokens_before: context size before compaction │ │ │ │ E5 is a BranchSummary Entry: │ │ - Summary of branch from from_id │ │ - Represents a fork point in conversation history │ │ │ └───────────────────────────────────────────────────────────────────┘ │ │ Session.moveTo() ▼ ┌───────────────────────────────────────────────────────────────────┐ │ Forking & Branching │ │ │ │ Current branch: │ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ E1 │────▶│ E2 │────▶│ E3 │ │ │ └─────┘ └─────┘ └─────┘ │ │ │ │ │ │ moveTo(E2) │ │ ▼ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ E1 │────▶│ E2 │────▶│ E3' │────▶│ E4' │ (new branch) │ │ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │ │ │ │ create BranchSummary │ │ ▼ │ │ ┌─────┐ │ │ │ E5 │ (branch summary) │ │ └─────┘ │ │ │ └───────────────────────────────────────────────────────────────────┘ ``` ## Component Relationships ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Component Relationships │ └─────────────────────────────────────────────────────────────────────────────┘ User Code │ ├── Creates ──► Agent │ │ │ ├── Uses ──► AgentLoop │ │ │ │ │ ├── Uses ──► StreamFn (LLM API) │ │ │ │ │ └── Uses ──► Session │ │ │ ├── Manages ──► AgentState │ │ │ ├── Queues ──► SteeringQueue │ │ │ └── Queues ──► FollowUpQueue │ └── Interacts With ──► AgentHarness (optional, higher level) │ ├── Manages ──► SessionRepo │ ├── Manages ──► Skills │ └── Manages ──► PromptTemplates ``` ## Data Flow ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Data Flow Between Layers │ └─────────────────────────────────────────────────────────────────────────────┘ User Input (String/Message) │ ▼ ┌──────────────────────┐ │ Agent.prompt() │ │ - normalizeInput() │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ AgentState.messages │ ──► AgentMessage[] └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ AgentLoop │ │ - transform_context │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ convertToLlm() │ ──► Transforms AgentMessage[] to Message[] └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ LLM API (StreamFn) │ │ - Context: Message[] │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ Response (Streaming) │ │ - Text deltas │ │ - Tool call deltas │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ AssistantMessage │ │ - content: Message[] │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ AgentState.messages │ ──► Appended to conversation └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ Tool Execution │ │ - Extract ToolCalls │ │ - Execute tools │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ ToolResultMessage[] │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ AgentState.messages │ ──► Tool results appended └──────────────────────┘ │ │ (Loop back to LLM or end) ▼ ┌──────────────────────┐ │ Session Storage │ │ - JSONL format │ │ - Tree entries │ └──────────────────────┘ ``` ## Summary The AgentCore.jl architecture follows a clean separation of concerns: 1. **AgentHarness** - Highest level, handles persistence and resources 2. **Agent** - State management and event streaming 3. **AgentLoop** - Core LLM interaction loop 4. **Session** - Conversation history management Each layer transforms data and passes it to the next layer, with clear interfaces and event hooks for customization.