# 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 with Type Transformations ### Complete User Input → Conversation History Flow ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Level 1: User Input │ └─────────────────────────────────────────────────────────────────────────────┘ User Input • String: "Hello, what's in the directory?" • AgentMessage: UserMessage(...) • Vector{AgentMessage}: [UserMessage(...), AssistantMessage(...)] │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Agent.prompt() / normalizePromptInput() │ │ │ │ Type Dispatch: │ │ • String → UserMessage("user", [TextContent(input)], ts) │ │ • AgentMessage → [input] (wrap in array) │ │ • Vector{AgentMessage} → input (pass-through) │ │ │ │ Output: Vector{AgentMessage} │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ AgentState.messages (AgentMessage[]) │ │ │ │ AgentMessage Types: │ │ • UserMessage (role: "user") │ │ • AssistantMessage (role: "assistant") │ │ • ToolResultMessage (role: "toolResult") │ │ • BashExecutionMessage (custom) │ │ • CompactionSummaryMessage (custom) │ │ • BranchSummaryMessage (custom) │ └──────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Level 2: AgentLoop Processing │ └─────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ transform_context() (optional hook) │ │ │ │ Input: Vector{AgentMessage} │ │ Output: Vector{AgentMessage} (transformed) │ │ - Can truncate, filter, or modify messages │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────┐ │ convertToLlm() - Type Transformation Pipeline │ │ │ │ Input: Vector{AgentMessage} │ │ Output: Vector{Message} (for LLM API) │ │ │ │ Single Dispatch Mapping: │ │ • UserMessage → UserMessage (pass-through) │ │ • AssistantMessage → AssistantMessage (pass-through) │ │ • ToolResultMessage → ToolResultMessage (pass-through) │ │ │ │ Custom Message Conversions: │ │ • BashExecutionMessage → UserMessage (via bashExecutionToText)│ │ • CompactionSummaryMessage → UserMessage (wrapped) │ │ • BranchSummaryMessage → UserMessage (wrapped) │ └────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Context for LLM API │ │ - system_prompt: String │ │ - messages: Vector{Message} │ │ - tools: Vector{AgentTool} │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ LLM API Call (stream_fn) │ │ │ │ Input: model, context, config │ │ Output: Stream{AssistantMessageEvent} │ │ • StartEvent: partial AssistantMessage │ │ • TextStartEvent/TextDeltaEvent/TextEndEvent │ │ • ToolCallStartEvent/ToolCallDeltaEvent/ToolCallEndEvent │ │ • DoneEvent: final AssistantMessage with usage │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ AssistantMessage (returned from LLM) │ │ │ │ • role: "assistant" │ │ • content: Vector{MessageContent} │ │ └─ Contains: TextContent[] and/or ToolCall[] │ │ • api, provider, model: String │ │ • usage: Usage (input, output, cache_read, cache_write) │ │ • stop_reason: String ("done", "length", "error", etc.) │ │ • error_message: Union{String, Nothing} │ │ • timestamp: Timestamp (Int64) │ └──────────────────────────────────────────────────────────────┘ │ ├─► Append to AgentState.messages (AssistantMessage) │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ executeToolCalls() - Tool Processing │ │ │ │ Extract: filter(c -> c isa ToolCall, assistant.content) │ │ Output: ExecutedToolCallBatch │ │ • messages: Vector{ToolResultMessage} │ │ • terminate: Bool │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ ToolResultMessage (for each ToolCall) │ │ │ │ • role: "toolResult" │ │ • tool_call_id: String (matches ToolCall.id) │ │ • tool_name: String (matches ToolCall.name) │ │ • content: Vector{MessageContent} │ │ • details: Any (tool-specific) │ │ • usage: Union{Usage, Nothing} │ │ • added_tool_names: Union{Vector{String}, Nothing} │ │ • is_error: Bool │ │ • timestamp: Timestamp (Int64) │ └──────────────────────────────────────────────────────────────┘ │ ├─► Append to AgentState.messages (ToolResultMessage) │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Updated AgentState.messages (AgentMessage[]) │ │ │ │ Conversation History: │ │ [UserMessage, AssistantMessage, ToolResultMessage, ...] │ └──────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Level 3: Session Storage (optional, for persistence) │ └─────────────────────────────────────────────────────────────────────────────┘ AgentState.messages (Vector{AgentMessage}) │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Session Storage (JSONL) │ │ │ │ SessionTreeEntry Types: │ │ • MessageEntry (agent_message) │ │ • CompactionEntry (summary, tokens_before) │ │ • BranchSummaryEntry (from_id, summary) │ │ • ModelChangeEntry (provider, model_id) │ │ • ThinkingLevelChangeEntry (thinking_level) │ │ • ActiveToolsChangeEntry (active_tool_names) │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Persisted Data (JSON format) │ │ - Each entry has: id, parent_id, timestamp, type │ │ - MessageEntry contains full AgentMessage │ └──────────────────────────────────────────────────────────────┘ ``` ### Tool Call Execution Flow (Detailed) ``` ToolCall (from AssistantMessage.content) │ ├─ type: "tool" ├─ id: "tc_abc123" ├─ name: "bash" ├─ arguments: Dict("command" => "ls -la") └─ partial_json: nothing │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ prepareToolCall() │ │ │ │ Input: tool_call::ToolCall │ │ Output: Union{PreparedToolCall, ImmediateToolCallOutcome} │ │ │ │ Steps: │ │ 1. Find tool by name in current_context.tools │ │ 2. before_tool_call hook (optional) │ │ Input: BeforeToolCallContext │ │ Output: BeforeToolCallResult (block, reason) or nothing │ │ 3. prepareToolCallArguments() (optional) │ │ Input: tool_call.arguments::Dict │ │ Output: prepared_arguments::Any │ │ 4. validateToolArguments() (optional) │ │ Input: prepared_tool_call.arguments │ │ Output: validated_args::Any │ │ 5. Return: PreparedToolCall(kind, tool_call, tool, args) │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ executePreparedToolCall() (if prepared) │ │ │ │ Input: PreparedToolCall │ │ Output: ExecutedToolCallOutcome │ │ │ │ tool.execute(tool_call.id, args, signal, on_update) │ │ │ │ │ └─ Returns: AgentToolResultMutable │ │ • content::Vector{MessageContent} │ │ • details::Any │ │ • usage::Union{Usage, Nothing} │ │ • added_tool_names::Union{Vector{String}, Nothing} │ │ • terminate::Union{Bool, Nothing} │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────┐ │ finalizeExecutedToolCall() │ │ │ │ Input: ExecutedToolCallOutcome │ │ Output: FinalizedToolCallOutcome │ │ │ │ Steps: │ │ 1. after_tool_call hook (optional) │ │ Input: AfterToolCallContext │ │ Output: AfterToolCallResult (patches) │ │ 2. Apply patches to result │ │ 3. Return: FinalizedToolCallOutcome(tool_call, result, error)│ └───────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ createToolResultMessage() │ │ │ │ Input: FinalizedToolCallOutcome │ │ Output: ToolResultMessage │ │ │ │ Fields: │ │ • role: "toolResult" │ │ • tool_call_id: tool_call.id │ │ • tool_name: tool_call.name │ │ • content: result.content │ │ • details: result.details │ │ • usage: result.usage │ │ • added_tool_names: result.added_tool_names │ │ • is_error: is_error │ │ • timestamp: Int64(Dates.now(Dates.UTC).datetime) │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Emit: ToolResultMessage to conversation │ └──────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Summary of Type Transformations │ └─────────────────────────────────────────────────────────────────────────────┘ User Input (String) │ ├─► normalizePromptInput() │ └─► UserMessage (AgentMessage subtype) │ Vector{AgentMessage} │ ├─► transform_context() (optional) │ └─► Vector{AgentMessage} (transformed) │ ├─► convertToLlm() │ └─► Vector{Message} (LLM API format) │ ├── UserMessage (pass-through) │ ├── AssistantMessage (pass-through) │ ├── ToolResultMessage (pass-through) │ └── Custom messages → UserMessage │ AssistantMessage (from LLM) │ ├─► executeToolCalls() │ └─► ToolResultMessage[] │ ToolResultMessage[] │ └─► Appended to AgentState.messages └─► Vector{AgentMessage} (updated conversation history) ``` ## Summary ## 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 ### Data Transformation Summary ``` Input Type Flow: User Input (String/Message) │ ├─ normalizePromptInput() │ └─► Vector{AgentMessage} │ ├─ transform_context() (optional) │ └─► Vector{AgentMessage} (transformed) │ ├─ convertToLlm() │ └─► Vector{Message} (LLM API format) │ ├─ LLM API (stream_fn) │ └─► AssistantMessage │ ├─ executeToolCalls() │ └─► ToolResultMessage[] │ └─► Vector{AgentMessage} (final conversation) ``` ### Key Data Flow Patterns 1. **Message Transformation**: `AgentMessage[] → Message[]` via `convertToLlm()` - UserMessage → UserMessage (pass-through) - AssistantMessage → AssistantMessage (pass-through) - ToolResultMessage → ToolResultMessage (pass-through) - Custom messages (Bash, Compaction, Branch) → UserMessage 2. **Tool Execution**: `ToolCall → ToolResultMessage` - prepareToolCall() validates and prepares - execute() runs the tool - finalize() applies hooks and returns outcome - createToolResultMessage() creates result entry 3. **Event Streaming**: `Stream{Event}` with lifecycle events - AgentStartEvent, TurnStartEvent - MessageStartEvent, MessageUpdateEvent, MessageEndEvent - ToolExecutionStartEvent, ToolExecutionEndEvent - TurnEndEvent, AgentEndEvent Each layer transforms data and passes it to the next layer, with clear interfaces and event hooks for customization.