Skip to content

Actor Agent

The Actor Agent is the system component that embodies a single entity and produces narrative prose describing what that entity does, says, or thinks next. It is the “inner voice” of an NPC (or player character), generating behavior proposals that are then validated and executed by the rest of the engine.

  1. Epistemic boundedness — The actor only sees what its entity would perceive: public attributes of other entities, private attributes explicitly ACL’d to it, its own memory buffer, and co-located entities. It does not have system-level access to all world state.

  2. Proposal, not mutation — The actor generates a proposal (narrative prose). It never mutates world state, persists to the database, or writes to memory directly. Validation, execution, and persistence are the Architect’s job.

  3. Free prose → structured intents — The actor outputs free natural-language prose. This is fed to the IntentDecoder, which splits and classifies it into a sequence of typed intents.

The actor prompt is assembled by ActorPromptBuilder and has two parts:

Establishes the role, rules, and output contract:

  • The LLM is the character, not a narrator or system.
  • The character may produce three kinds of behavior:
    • Spoken dialoguedialogue intent.
    • Physical/logical actionaction intent.
    • Inner thought / reflectionmonologue intent.
  • The character must stay in-character, respect its knowledge bounds, and refer to others by subjective aliases (not system UUIDs).
  • Not every turn requires an outward action — internal monologue alone is valid.
  • The character controls only itself.

Epistemically bounded, with these sections:

Section Content Source
Current moment The subjective present time worldState.clock.get().toISOString()
The world as you perceive it Self-visible attributes, co-located entities + their visible attributes, other presences elsewhere serializeSubjectiveWorldState()
Your recent memory Recent BufferEntrys, alias-substituted, with relative time phrasing serializeSubjectiveBufferEntry()

No system UUIDs, no private attributes the entity lacks ACL access to, and no objective-world-state dump are present.

Monologue ("monologue") is the third intent type. Its properties:

  • No perceivertargetIds is always []. No other entity perceives or can react to a monologue.
  • No validation — The Architect’s processIntent short-circuits for monologues.
  • Direct-to-memory — Written directly to the actor’s buffer with no outcome field.
  • Defensive guardLLMValidator.validate has an early-return guard so a stray monologue can never reach the validation LLM.
[ActorAgent.act()]
├─ 1. ActorPromptBuilder.build(entity, worldState)
│ → system prompt + user context (subjective world + memory + time)
├─ 2. IActorProseGenerator.generate(entityId, systemPrompt, userContext)
│ ├─ LLMActorProseGenerator: queries LLM via generateStructuredResponse
│ └─ CLIProseGenerator: prompts human player via CLI / readline interface
│ → narrativeProse: string
├─ 3. IntentDecoder.decode(worldState, actorId, prose)
│ → IntentSequence (dialogue | action | monologue intents)
└─ returns { narrativeProse, intents }
[Caller (e.g. game loop)]
├─ for each intent in intents:
│ ├─ if intent.type === "monologue": short-circuit, write to buffer
│ ├─ if intent.type === "dialogue": validate (always valid), write to buffer
│ └─ if intent.type === "action": validate, generate time delta, advance clock
└─ world state persisted to DB
File Role
packages/actor/src/actor-prompt-builder.ts Assembles the epistemically-bounded actor prompt
packages/actor/src/actor.ts ActorAgent class: orchestrates prompt → LLM → decoder flow
packages/actor/src/index.ts Package exports
packages/core/src/world.ts:72 serializeSubjectiveWorldState()
packages/intent/src/intent.ts:8 IntentTypeSchema — includes "monologue"
packages/intent/src/intent-decoder.ts:30 Decoder system prompt
packages/architect/src/architect.ts:35 Monologue short-circuit
packages/architect/src/llm-validator.ts:19 Defensive monologue guard