22 KiB
AgentCore.jl - AgentHarness Design Reference
Status
Note
: The AgentHarness module (
src/agent_harness.jl) is not yet implemented. This document describes the intended design based on types defined insrc/harness_types.jl. The types, events, and interfaces below are defined but the harness that connects them is a planned feature.Several modules referenced in
src/AgentCore.jlare also not yet implemented:compaction/compaction.jl,compaction/utils.jl,compaction/branch_summarization.jl,utils/truncate.jl,utils/shell_output.jl,proxy.jl.Type placeholders not yet defined:
AgentLoopConfig,Promise,AbortSignal,EventStream,Context. TheSessionRepomethods inharness_types.jlreturnPromise()stubs.
AgentHarness Architecture (Planned)
AgentHarness = Agent + Session + Resources + Hooks
AgentHarness (to be implemented in src/agent_harness.jl)
├── Manages Agent instances
├── Provides Session persistence via SessionRepo
├── Manages resources (skills, prompt templates)
├── Handles extension hooks (BeforeAgentStart, BeforeProviderPayload, etc.)
└── Coordinates tool execution with AgentHarnessToolContextSource
AgentHarnessOptions (src/harness_types.jl:1067)
├── session::Session
├── models::Any
├── tools::Union{Vector{TTool}, Nothing}
├── resources::Union{AgentHarnessResources, Nothing}
├── system_prompt::Union{AgentHarnessSystemPrompt, Nothing}
├── stream_options::Union{AgentHarnessStreamOptions, Nothing}
├── retry::Union{Any, Nothing}
├── model::Model
├── thinking_level::Union{ThinkingLevel, Nothing}
├── active_tool_names::Union{Vector{String}, Nothing}
├── steering_mode::Union{QueueMode, Nothing}
├── follow_up_mode::Union{QueueMode, Nothing}
└── tool_context::Union{AgentHarnessToolContextSource, Nothing}
Event Type Hierarchy (Actual)
The harness event types are defined as mutable struct in harness_types.jl.
They are NOT subtypes of AgentHarnessEvent or AgentHarnessOwnEvent - those
abstract types exist but nothing inherits from them.
AgentEvent (abstract, types.jl:196)
├── AgentStartEvent (types.jl:198)
├── AgentEndEvent (types.jl:199)
├── TurnStartEvent (types.jl:202)
├── TurnEndEvent (types.jl:203)
├── MessageStartEvent (types.jl:207)
├── MessageUpdateEvent (types.jl:210)
├── MessageEndEvent (types.jl:214)
├── ToolExecutionStartEvent (types.jl:217)
├── ToolExecutionUpdateEvent (types.jl:222)
└── ToolExecutionEndEvent (types.jl:228)
AgentHarnessOwnEvent (abstract, harness_types.jl:850)
└── (nothing inherits from this)
AgentHarnessEvent (abstract, harness_types.jl:856)
└── (nothing inherits from this)
Harness event structs (harness_types.jl) - mutable structs, not subtypes:
├── BeforeAgentStartEvent (line 653)
├── ContextEvent (line 665)
├── BeforeProviderRequestEvent (line 674)
├── BeforeProviderPayloadEvent (line 685)
├── AfterProviderResponseEvent (line 695)
├── ToolCallEvent (line 705)
├── ToolResultEvent (line 716)
├── SessionBeforeCompactEvent (line 731)
├── SessionCompactEvent (line 743)
├── SessionBeforeTreeEvent (line 753)
├── SessionTreeEvent (line 763)
├── RetryScheduledEvent (line 775)
├── RetryAttemptStartEvent (line 788)
├── RetryFinishedEvent (line 797)
├── ModelUpdateEvent (line 806)
├── ThinkingLevelUpdateEvent (line 817)
├── ToolsUpdateEvent (line 827)
└── ResourcesUpdateEvent (line 840)
Types (from harness_types.jl)
AgentHarnessOptions (line 1067)
mutable struct AgentHarnessOptions{TC<:Any, TSkill<:Skill, TPromptTemplate<:PromptTemplate, TTool<:AgentHarnessTool}
session::Session
models::Any
tools::Union{Vector{TTool}, Nothing}
resources::Union{AgentHarnessResources{TSkill, TPromptTemplate}, Nothing}
system_prompt::Union{AgentHarnessSystemPrompt{TC, TSkill, TPromptTemplate, TTool}, Nothing}
stream_options::Union{AgentHarnessStreamOptions, Nothing}
retry::Union{Any, Nothing}
model::Model
thinking_level::Union{ThinkingLevel, Nothing}
active_tool_names::Union{Vector{String}, Nothing}
steering_mode::Union{QueueMode, Nothing}
follow_up_mode::Union{QueueMode, Nothing}
tool_context::Union{AgentHarnessToolContextSource{TC}, Nothing}
end
Purpose: Configure AgentHarness with all necessary options (defined but harness not implemented).
AgentHarnessResources (line 82)
mutable struct AgentHarnessResources{TSkill<:Skill, TPromptTemplate<:PromptTemplate}
promptTemplates::Union{Vector{TPromptTemplate}, Nothing}
skills::Union{Vector{TSkill}, Nothing}
end
Skill (line 68)
mutable struct Skill
name::String
description::String
content::String
filePath::String
disableModelInvocation::Bool
end
Loading: loadSkills(env, dir) is defined in skills.jl but parsing is stubbed - currently returns nothing, diagnostics. The frontmatter parsing code (lines 266-300 of skills.jl) is commented out as TODO.
Skill format:
<!-- SKILL.md -->
{
"name": "File Operations",
"description": "Handle file system operations",
"disable-model-invocation": false
}
---
# File Operations Skill
This skill provides instructions for working with files...
PromptTemplate (line 76)
mutable struct PromptTemplate
name::String
description::Union{String, Nothing}
content::String
end
Loading: loadPromptTemplates(env, paths) is defined in prompt_templates.jl but parsing is stubbed - currently returns nothing, diagnostics. Frontmatter parsing is commented out as TODO (lines 188-215).
Format:
<!-- template.md -->
{
"description": "Generate commit message"
}
---
Generate a git commit message for:
$1
$ARGUMENTS
AgentHarnessStreamOptions (line 109)
mutable struct AgentHarnessStreamOptions
transport::Union{String, Nothing}
timeout_ms::Union{Int64, Nothing}
max_retries::Union{Int64, Nothing}
max_retry_delay_ms::Union{Int64, Nothing}
headers::Union{Dict{String, String}, Nothing}
metadata::Union{Dict{String, Any}, Nothing}
cache_retention::Union{String, Nothing}
end
AgentHarnessStreamOptionsPatch (line 119)
mutable struct AgentHarnessStreamOptionsPatch
transport::Union{String, Nothing}
timeout_ms::Union{Int64, Nothing}
max_retries::Union{Int64, Nothing}
max_retry_delay_ms::Union{Int64, Nothing}
cache_retention::Union{String, Nothing}
headers::Union{Dict{String, String}, Nothing}
metadata::Union{Dict{String, Any}, Nothing}
end
AgentHarnessTool (line 91)
mutable struct AgentHarnessTool{TContext, TParameters, TDetails}
name::String
label::String
description::String
parameters::TParameters
execute::Function
prepareArguments::Union{Function, Nothing}
executionMode::Union{ToolExecutionMode, Nothing}
end
AgentHarnessToolContextSource (line 101)
mutable struct AgentHarnessToolContextSource{TContext}
context::Union{TContext, Function}
end
AgentHarnessSystemPrompt (line 1059)
mutable struct AgentHarnessSystemPrompt{TC<:Any, TSkill<:Skill, TPromptTemplate<:PromptTemplate, TTool<:AgentHarnessTool}
value::Union{String, Function}
end
SessionRepo Interface (stubs in harness_types.jl:564-588)
abstract type SessionRepo<
TMetadata<:SessionMetadata,
TCreateOptions,
TListOptions
> end
function create(repo::SessionRepo, options::TCreateOptions)::Promise{Session}
return Promise() # STUB - Promise type not defined
end
function open(repo::SessionRepo, metadata::TMetadata)::Promise{Session}
return Promise() # STUB
end
function list(repo::SessionRepo, options::TListOptions)::Promise{Vector{TMetadata}}
return Promise() # STUB
end
function delete(repo::SessionRepo, metadata::TMetadata)::Promise{Nothing}
return Promise() # STUB
end
function fork(repo::SessionRepo, source::TMetadata, options::Dict{String, Any})::Promise{Session}
return Promise() # STUB
end
Note
: These methods are stubs in
harness_types.jl.Promiseis not defined anywhere.
JsonlSessionRepo (src/session/jsonl_repo.jl)
mutable struct JsonlSessionRepo <: SessionRepo{
JsonlSessionMetadata,
JsonlSessionCreateOptions,
JsonlSessionListOptions
}
fs::Any
sessions_root_input::String
sessions_root::Union{String, Nothing}
function JsonlSessionRepo(; sessions_root::String, fs::Any)
new(fs, sessions_root, nothing)
end
end
Note
: Constructor uses keyword arguments (
sessions_root=,fs=), NOT positional.
JsonlSessionStorage (src/session/jsonl_storage.jl)
file_path::Stringmetadata::T(SessionMetadata)entries::Vector{SessionTreeEntry}by_id::Dict{String, SessionTreeEntry}labels_by_id::Dict{String, String}current_leaf_id::Union{String, Nothing}
Methods: getMetadata, getLeafId, setLeafId, createEntryId, appendEntry, getEntry, findEntries, getLabel, getSessionName, getSessionStats, getPathToRootOrCompaction, getEntries.
Agent (src/agent.jl)
mutable struct Agent
_state::AgentState
listeners::Set{Tuple{Function, Ref{Bool}}}
steering_queue::PendingMessageQueue
follow_up_queue::PendingMessageQueue
convert_to_llm::Function
transform_context::Union{Function, Nothing}
stream_function::StreamFn
get_api_key::Union{Function, Nothing}
on_payload::Union{Function, Nothing}
on_response::Union{Function, Nothing}
before_tool_call::Union{Function, Nothing}
after_tool_call::Union{Function, Nothing}
prepare_next_turn::Union{Function, Nothing}
prepare_next_turn_with_context::Union{Function, Nothing}
active_run::Union{ActiveRun, Nothing}
session_id::Union{String, Nothing}
thinking_budgets::Union{Dict{String, Int64}, Nothing}
transport::String
max_retry_delay_ms::Union{Int64, Nothing}
tool_execution::ToolExecutionMode
end
Key methods:
subscribe(agent, listener)- subscribe to events, returns unsubscribe functionget_state(agent)- get current AgentStatesteer(agent, message)- queue message for injection after current turnfollowUp(agent, message)- queue message to run after agent would stopprompt(agent, input, images)- start a new prompt (input can be String, AgentMessage, or Vector{AgentMessage})continue!(agent)- continue from current transcriptwaitForIdle(agent)- resolve when current run finishesabort(agent)- abort current run (partially implemented)reset!(agent)- clear all stateclearSteeringQueue(agent)/clearFollowUpQueue(agent)/clearAllQueues(agent)hasQueuedMessages(agent)- check for pending messagescreateContextSnapshot(agent)- create AgentContext snapshotcreateLoopConfig(agent, options)- create AgentLoopConfig
Note
:
AgentLoopConfigtype is not defined in any visible file. It is referenced inagent.jl:368andagent_loop.jl.
AgentLoop (src/agent_loop.jl)
Key functions:
agentLoop(prompts, context, config, signal, stream_fn)- main loop, returns EventStreamagentLoopContinue(context, config, signal, stream_fn)- continue from existing contextrunAgentLoop(...)- internal run, emits events viaemit::AgentEventSinkrunAgentLoopContinue(...)- internal continue runrunLoop(...)- shared main loop logicstreamAssistantResponse(...)- stream LLM response with event emissionexecuteToolCalls(...)- execute tool calls (sequential or parallel)executeToolCallsSequential(...)- sequential executionexecuteToolCallsParallel(...)- parallel execution via Threads.@spawn
The loop flow:
AgentStartEventemittedTurnStartEventemitted (first turn only from agentLoop, not from runLoop)- Steering messages drained and emitted as
MessageStartEvent/MessageEndEvent streamAssistantResponsecalled - transforms context, converts to LLM messages, calls stream_fn- For each tool call in response: execute sequentially or in parallel
TurnEndEventemitted with message and tool resultsprepare_next_turnhook (if configured) called- If
should_stop_after_turnreturns true or no pending messages,AgentEndEventemitted - Follow-up messages drained and loop repeats
AgentLoopConfig fields (referenced, not defined)
Created in agent.jl:368-401:
model, reasoning (thinking_level), session_id, on_payload, on_response,
transport, thinking_budgets, max_retry_delay_ms, tool_execution,
before_tool_call, after_tool_call, prepare_next_turn, convert_to_llm,
transform_context, get_api_key, get_steering_messages, get_follow_up_messages
Hook System (Planned - Harness Not Implemented)
The following hook types are defined as event/result structs in harness_types.jl
but no harness implementation exists to trigger or handle them. These are
intended to be used by the future AgentHarness module.
BeforeAgentStartEvent (line 653)
mutable struct BeforeAgentStartEvent{TSkill, TPromptTemplate}
type::String
prompt::String
images::Union{Vector{ImageContent}, Nothing}
system_prompt::String
resources::AgentHarnessResources{TSkill, TPromptTemplate}
end
Result: BeforeAgentStartResult (line 862) - messages::Union{Vector{AgentMessage}, Nothing}, system_prompt::Union{String, Nothing}
ContextEvent (line 665)
mutable struct ContextEvent
type::String
messages::Vector{AgentMessage}
end
Result: ContextResult (line 871) - messages::Vector{AgentMessage}
BeforeProviderRequestEvent (line 674)
mutable struct BeforeProviderRequestEvent
type::String
model::Model
session_id::String
stream_options::AgentHarnessStreamOptions
end
Result: BeforeProviderRequestResult (line 879) - stream_options::Union{AgentHarnessStreamOptionsPatch, Nothing}
BeforeProviderPayloadEvent (line 685)
mutable struct BeforeProviderPayloadEvent
type::String
model::Model
payload::Any
end
Result: BeforeProviderPayloadResult (line 887) - payload::Any
AfterProviderResponseEvent (line 695)
mutable struct AfterProviderResponseEvent
type::String
status::Int64
headers::Dict{String, String}
end
ToolCallEvent (line 705)
mutable struct ToolCallEvent
type::String
tool_call_id::String
tool_name::String
input::Dict{String, Any}
end
Result: ToolCallResult (line 895) - block::Union{Bool, Nothing}, reason::Union{String, Nothing}
ToolResultEvent (line 716)
mutable struct ToolResultEvent
type::String
tool_call_id::String
tool_name::String
input::Dict{String, Any}
content::Vector{MessageContent}
details::Any
is_error::Bool
usage::Union{Usage, Nothing}
end
Result: ToolResultPatch (line 904) - content, details, is_error, usage, terminate (all Union{...})
SessionBeforeCompactEvent (line 731)
mutable struct SessionBeforeCompactEvent
type::String
preparation::Any
branch_entries::Vector{SessionTreeEntry}
custom_instructions::Union{String, Nothing}
signal::Any
end
Result: SessionBeforeCompactResult (line 916) - cancel::Union{Bool, Nothing}, compaction::Union{CompactResult, Nothing}
SessionBeforeTreeEvent (line 753)
mutable struct SessionBeforeTreeEvent
type::String
preparation::Any
signal::Any
end
Result: SessionBeforeTreeResult (line 925) - cancel, summary, custom_instructions, replace_instructions, label
SessionCompactEvent (line 743)
mutable struct SessionCompactEvent
type::String
compaction_entry::CompactionEntry
from_hook::Bool
end
SessionTreeEvent (line 763)
mutable struct SessionTreeEvent
type::String
new_leaf_id::Union{String, Nothing}
old_leaf_id::Union{String, Nothing}
summary_entry::Union{BranchSummaryEntry, Nothing}
from_hook::Union{Bool, Nothing}
end
Session (src/session/session.jl)
mutable struct Session{T<:SessionMetadata}
storage::SessionStorage{T}
context_build_options::SessionContextBuildOptions
end
Key methods:
getMetadata(session)/getStorage(session)/getLeafId(session)/getEntry(session, id)getEntries(session, options)/getBranch(session, from_id)buildContextEntries(session, options)/buildContext(session, options)getLabel(session, id)/getSessionStats(session)/getSessionName(session)appendMessage(session, message)→ entry_idappendThinkingLevelChange(session, level)→ entry_idappendModelChange(session, provider, model_id)→ entry_idappendActiveToolsChange(session, active_tool_names)→ entry_idappendCompaction(session, summary, first_kept_entry_id, tokens_before, ...)→ entry_idappendCustomEntry(session, custom_type, data)→ entry_idappendCustomMessageEntry(session, custom_type, content, display, details)→ entry_idappendLabel(session, target_id, label)→ entry_idappendSessionName(session, name)→ entry_idmoveTo(session, entry_id, summary)→ new_leaf_id or nothing (line 392)
Session Tree Entries (types.jl and harness_types.jl)
abstract type SessionTreeEntry end
struct MessageEntry <: SessionTreeEntry
base::SessionTreeEntryBase # or direct fields in harness_types.jl
message::AgentMessage
end
struct ThinkingLevelChangeEntry <: SessionTreeEntry
base::SessionTreeEntryBase
thinking_level::String
end
struct ModelChangeEntry <: SessionTreeEntry
base::SessionTreeEntryBase
provider::String
model_id::String
end
struct ActiveToolsChangeEntry <: SessionTreeEntry
base::SessionTreeEntryBase
active_tool_names::Vector{String}
end
struct CompactionEntry{T} <: SessionTreeEntry
base::SessionTreeEntryBase
summary::String
first_kept_entry_id::Union{String, Nothing}
tokens_before::Int64
retained_tail::Union{Vector{AgentMessage}, Nothing}
details::Union{T, Nothing}
usage::Union{Usage, Nothing}
from_hook::Bool
end
struct BranchSummaryEntry{T} <: SessionTreeEntry
base::SessionTreeEntryBase
from_id::String
summary::String
details::Union{T, Nothing}
usage::Union{Usage, Nothing}
from_hook::Bool
end
struct CustomEntry{T} <: SessionTreeEntry
base::SessionTreeEntryBase
custom_type::String
data::Union{T, Nothing}
end
struct CustomMessageEntry{T} <: SessionTreeEntry
base::SessionTreeEntryBase
custom_type::String
content::String
details::Union{T, Nothing}
display::Bool
end
struct LabelEntry <: SessionTreeEntry
base::SessionTreeEntryBase
target_id::String
label::Union{String, Nothing}
end
struct SessionInfoEntry <: SessionTreeEntry
base::SessionTreeEntryBase
name::Union{String, Nothing}
end
struct LeafEntry <: SessionTreeEntry
base::SessionTreeEntryBase
target_id::Union{String, Nothing}
end
Resource Loading (stubs)
loadSkills (skills.jl:61)
skills, diagnostics = loadSkills(env, "/path/to/skills")
Note
: Parsing is stubbed (line 302 returns
nothing, diagnostics). The frontmatter parsing code is commented out (lines 266-300).formatSkillInvocation(skill, additional_instructions)is implemented.
loadPromptTemplates (prompt_templates.jl:43)
templates, diagnostics = loadPromptTemplates(env, "/path/to/templates")
Note
: Parsing is stubbed (line 217 returns
nothing, diagnostics). The frontmatter parsing code is commented out (lines 188-215).formatPromptTemplateInvocation(template, args)andparseCommandArgs(args_string)andsubstituteArgs(content, args)are implemented.
Missing Types / Modules
The following types are referenced in the code but not defined:
AgentLoopConfig- referenced inagent.jl:368,agent_loop.jlPromise- referenced inharness_types.jlAbortSignal- referenced inagent_loop.jlEventStream- referenced inagent_loop.jl:158Context- referenced inagent_loop.jl:376AgentToolResultMutable- referenced inagent_loop.jlFinalizedToolCallOutcome,PreparedToolCall,ImmediateToolCallOutcome,ExecutedToolCallOutcome- defined inagent_loop.jl:639-661(these exist)
The following modules are referenced in AgentCore.jl but files don't exist:
compaction/compaction.jlcompaction/utils.jlcompaction/branch_summarization.jlutils/truncate.jlutils/shell_output.jlproxy.jl
AgentCore Exports (from AgentCore.jl:61-147)
The module exports: AgentMessage, AgentTool, AgentContext, AgentEvent, ThinkingLevel, ToolExecutionMode, QueueMode, AgentState, Agent, AgentOptions, AgentLoopConfig, agentLoop, agentLoopContinue, runAgentLoop, runAgentLoopContinue, AgentHarness, AgentHarnessOptions, AgentHarnessEvent, AgentHarnessResources, AgentHarnessSystemPrompt, Session, SessionStorage, SessionRepo, JsonlSessionStorage, JsonlSessionRepo, InMemorySessionStorage, InMemorySessionRepo, createBashTool, createReadTool, createWriteTool, createEditTool, ExecutionEnv, compact, prepareCompaction, DEFAULT_COMPACTION_SETTINGS, generateSummary, generateBranchSummary, truncateHead, truncateTail, formatSize, DEFAULT_MAX_LINES, DEFAULT_MAX_BYTES, convertToLlm, bashExecutionToText, formatSkillsForSystemPrompt, loadSkills, formatSkillInvocation, loadPromptTemplates, formatPromptTemplateInvocation, parseCommandArgs, substituteArgs, streamProxy, ProxyStreamOptions, setDefaultStreamFn, getDefaultStreamFn, uuidv7, create_timestamp.
Best Practices
- Agent hooks (planned): Use
beforeAgentStartfor initialization,beforeProviderPayloadfor custom metadata,toolCallfor blocking dangerous operations - Skills: Organize by domain (file operations, database queries, HTTP requests, git operations)
- Templates: Use for common patterns (commit messages, code review, testing prompts)
- Sessions: Compact periodically, use branches for exploration, clean up old sessions
- Monitoring: Track token counts, watch API costs, optimize tool execution
- Tool execution: Choose between
EXECUTION_SEQUENTIALandEXECUTION_PARALLELbased on tool dependencies