bfe060ceb51638f00fb6b9e6eb46773cd87eaa76
raw

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:

  1. Resolve active role (direct / orchestrator / worker / explorer).

  2. Build prompt blocks in fixed order.

  3. Join non-empty blocks with double newlines.

  4. Prepend result as system message before transcript messages.

  5. 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


End-to-end runtime call flow (normal turn)

  1. runtime:process_raw_input(line) routes non-slash input to runtime:run_turn(...) (process_raw_input / run_turn in core/runtime.lua).

  2. runtime:run_turn delegates to core.turn.run (run_turn in core/runtime.lua).

  3. 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_memories and the build_system_prompt call in core/turn.lua).

  4. User message is appended to conversation state (append_user in core/turn.lua / conversation.lua).

  5. conversation.build_model_messages prepends the system message, then the transcript messages (build_model_messages in conversation.lua).

  6. Tool definitions are attached separately via services.tool:llm_specs(role_spec.name) (core/turn.lua).

  7. 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:

  1. Role frame

  2. Project context

  3. Project-memory index (only when memories are provided)

Assembly behavior:


Per-block details

1) Role frame

  • Source: prompt/role.lua

  • Selector: role_spec.prompt_profile from 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.lua

  • The block is emitted only when turn.project is 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_file defaults to INDEX.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 context section is appended only when the resolved index file exists and is non-empty.

3) Project-memory index

  • Builder: prompt.lua

  • Added only when project_memories is non-empty

  • Structure:

    • ## Project memory index

    • project_id

    • per-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: explorer only (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:

  1. /prompt parsed by the command router (command/router.lua).

  2. Runtime slash path invokes runtime:run_command(...) (core/runtime.lua).

  3. Builtin handler routes to handle_prompt (command/builtin.lua).

  4. Preferred path uses build_current_system_prompt from the runtime context (command/builtin.lua).

  5. Runtime callback loads memories for the current scope via persistence.repositories().project_memory.list(scope.project_id) and builds the prompt (core/runtime.lua).

  6. Fallback path resolves repositories from the command context and likewise loads repos.project_memory.list(project_id) before prompt build (command/builtin.lua).

  7. 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):

  1. Build child execution context with active_role = job.target_role (dispatch/worker.lua).

  2. Build child turn request with role = job.target_role (dispatch/worker.lua).

  3. Launch child via core.turn.run(child_runtime, child_request, ...) (dispatch/worker.lua).

  4. Child turn uses the same normal turn prompt path, including load_project_memories(...) and build_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