Agent Smith System Prompt Workflow
Overview
Agent Smith builds a single system-prompt string per turn from deterministic blocks, then prepends it to conversation messages for the LLM.
High-level behavior:
Resolve active role (
direct/orchestrator/worker/explorer).Build prompt blocks in fixed order.
Join non-empty blocks with double newlines.
Prepend result as
systemmessage before transcript messages.Build tool specs separately (
tool:llm_specs(role)), independent from prompt text.
Primary assembly entrypoint: agent_smith/agent_smith/prompt.lua (build_system_prompt, build_messages).
Source-of-truth files
Prompt assembly
agent_smith/agent_smith/prompt.luaagent_smith/agent_smith/prompt/template.luaagent_smith/agent_smith/prompt/role.luaagent_smith/agent_smith/prompt/project.lua
Role specs + policy matrix
agent_smith/agent_smith/role.luaagent_smith/agent_smith/role/matrix.lua
Runtime/turn call sites
agent_smith/agent_smith/core/turn.luaagent_smith/agent_smith/core/runtime.luaagent_smith/agent_smith/conversation.lua
Dispatch child-role path
agent_smith/agent_smith/dispatch/worker.lua
/promptcommand pathagent_smith/agent_smith/command/builtin.lua
Supporting docs
agent_smith/README.mdagent_smith/docs/ARCHITECTURE.md
End-to-end runtime call flow (normal turn)
runtime:process_raw_input(line)routes non-slash input toruntime:run_turn(...)(process_raw_input/run_turnincore/runtime.lua).runtime:run_turndelegates tocore.turn.run(run_turnincore/runtime.lua).During the turn, project memories for the turn scope are loaded from persistence and injected into the prompt build:
repos = services.persistence.repositories()→repos.project_memory.list(active_turn.project_id)→services.prompt.build_system_prompt(active_turn, role_spec, project_memories)(load_project_memoriesand thebuild_system_promptcall incore/turn.lua).User message is appended to conversation state (
append_userincore/turn.lua/conversation.lua).conversation.build_model_messagesprepends thesystemmessage, then the transcript messages (build_model_messagesinconversation.lua).Tool definitions are attached separately via
services.tool:llm_specs(role_spec.name)(core/turn.lua).LLM request is sent with
{ model, messages, tools? }(core/turn.lua).
Exact system-prompt block order
build_system_prompt(context, role_spec, project_memories) in prompt.lua builds blocks in this order:
Role frame
Project context
Project-memory index (only when memories are provided)
Assembly behavior:
The
assemblehelper inprompt/template.luafilters empty blocks and concatenates non-empty blocks with"\n\n"(double newline).
Per-block details
1) Role frame
Source:
prompt/role.luaSelector:
role_spec.prompt_profilefrom role catalog (role.lua)API:
get_prompt_fragment(profile_name)(prompt/role.lua)
Profiles provided:
direct: full coding assistant, can dispatch worker/explorer.orchestrator: planning/delegation assistant, no file mutation, use record/recall.worker: delegated implementation agent, may dispatch to explorer.explorer: read-focused investigation agent, do not mutate files.
2) Project context
Source:
prompt/project.luaThe block is emitted only when
turn.projectis present.Markdown shape:
## Project information
- CWD: <turn.cwd> # included when available
- Repository root: <project.root|cwd> # included when available
- Git origin: <project.origin> # optional
- Normalized origin: <project.normalized># optional
- Project ID: <turn.project_id> # optional
## Project context # optional section
<contents of <repo-root>/<runtime.index_file>>
runtime.index_filedefaults toINDEX.md(config loader default).The index file is resolved only at Git repository root (
<repo-root>/<runtime.index_file>), not relative to arbitrary CWD locations.The
## Project contextsection is appended only when the resolved index file exists and is non-empty.
3) Project-memory index
Builder:
prompt.luaAdded only when
project_memoriesis non-emptyStructure:
## Project memory indexproject_idper-entry
id+label(the memory's first content line, capped ~80 chars)
Role-by-role prompt differences
Role spec source: role.lua; policy source: role/matrix.lua; role text source: prompt/role.lua.
direct
prompt_profile:direct(role.lua)Dispatch targets:
worker,explorer(role.lua,matrix.lua)Allowed tools (
matrix.lua):bash,read,code_map,code_grep,code_files,code_symbols,code_outline,edit,write,web_search,web_fetch,note,dispatch
Denied tools:
report,record,recall
orchestrator
prompt_profile:orchestrator(role.lua)Dispatch targets:
worker,explorer(role.lua,matrix.lua)Allowed tools (
matrix.lua):read,code_map,code_grep,code_files,code_symbols,code_outline,git,record,recall,note,dispatch
Denied tools:
bash,edit,write,web_search,web_fetch,report
worker
prompt_profile:worker(role.lua)Dispatch targets:
exploreronly (role.lua,matrix.lua)Allowed tools (
matrix.lua):bash,read,code_map,code_grep,code_files,code_symbols,code_outline,edit,write,web_search,web_fetch,report,dispatch
Denied tools:
record,recall,note
explorer
prompt_profile:explorer(role.lua)Dispatch targets: none (
role.lua,matrix.lua)Allowed tools (
matrix.lua):bash,read,code_map,code_grep,code_files,code_symbols,code_outline,web_search,web_fetch,report
Denied tools:
edit,write,record,recall,note,dispatch
/prompt command path
Route + execution path:
/promptparsed by the command router (command/router.lua).Runtime slash path invokes
runtime:run_command(...)(core/runtime.lua).Builtin handler routes to
handle_prompt(command/builtin.lua).Preferred path uses
build_current_system_promptfrom the runtime context (command/builtin.lua).Runtime callback loads memories for the current scope via
persistence.repositories().project_memory.list(scope.project_id)and builds the prompt (core/runtime.lua).Fallback path resolves repositories from the command context and likewise loads
repos.project_memory.list(project_id)before prompt build (command/builtin.lua).If project id/repositories/listing are unavailable or fail, both paths degrade to
project_memories = {}and still return a prompt (without the memory block).
Dispatch child-role prompt path
Child dispatch flow (dispatch/worker.lua):
Build child execution context with
active_role = job.target_role(dispatch/worker.lua).Build child turn request with
role = job.target_role(dispatch/worker.lua).Launch child via
core.turn.run(child_runtime, child_request, ...)(dispatch/worker.lua).Child turn uses the same normal turn prompt path, including
load_project_memories(...)andbuild_system_prompt(active_turn, role_spec, project_memories)(core/turn.lua).
So dispatch children inherit the same turn-based memory loading behavior as normal turns.
Project-memory runtime behavior
Normal turns load project memories from persistence repositories via
project_memory.list(active_turn.project_id)before prompt assembly (load_project_memoriesincore/turn.lua)./promptloads memories for the current scope in the runtime callback and in the builtin fallback using command-context repositories (core/runtime.lua,command/builtin.lua).On missing
project_id, missing repositories, or repository/list failures, prompt building continues with empty memories; the project-memory block is omitted.