NPC State And Protocol

View as Markdown

NPC state in the UE plugin lives in NPCSlice, not in a standalone immutable FNPC value. The slice mirrors the entity-adapter pattern from @forbocai/core.

Primary Type

1USTRUCT(BlueprintType)
2struct FNPCInternalState {
3 UPROPERTY(BlueprintReadOnly) FString Id;
4 UPROPERTY(BlueprintReadOnly) FString Persona;
5 UPROPERTY(BlueprintReadOnly) FAgentState State;
6 UPROPERTY(BlueprintReadOnly) TArray<FNPCHistoryEntry> History;
7 FAgentAction LastAction;
8 bool bHasLastAction;
9 UPROPERTY(BlueprintReadOnly) bool bIsBlocked;
10 UPROPERTY(BlueprintReadOnly) FString BlockReason;
11 UPROPERTY(BlueprintReadOnly) TArray<FNPCStateLogEntry> StateLog;
12};

The slice keeps an entity map plus ActiveNpcId.

Register An NPC

1#include "RuntimeStore.h"
2#include "NPC/NPCId.h"
3#include "NPC/NPCSlice.h"
4
5auto Store = createSDKStore();
6
7FNPCInternalState Npc;
8Npc.Id = NPCId::GenerateNPCId();
9Npc.Persona = TEXT("A careful quartermaster.");
10Npc.State = TypeFactory::AgentState(TEXT("{\"mood\":\"guarded\"}"));
11
12Store.dispatch(NPCSlice::Actions::SetNPCInfo(Npc));

SetNPCInfo upserts the entity and also makes it active.

Selectors

  • NPCSlice::SelectNPCById(State, Id)
  • NPCSlice::SelectAllNPCs(State)
  • NPCSlice::SelectActiveNpcId(State)
  • NPCSlice::SelectActiveNPC(State)
1const auto Active = NPCSlice::SelectActiveNPC(Store.getState().NPCs);
2if (Active.hasValue) {
3 UE_LOG(LogTemp, Display, TEXT("%s"), *Active.value.Persona);
4}

Update State

Use slice actions for direct state changes.

1Store.dispatch(NPCSlice::Actions::UpdateNPCState(
2 Npc.Id,
3 TypeFactory::AgentState(TEXT("{\"mood\":\"friendly\"}"))));
  • SetNPCState replaces the full agent state
  • UpdateNPCState merges a JSON delta into the current state
  • AddToHistory, SetHistory, SetLastAction, BlockAction, and ClearBlock support the protocol runtime

Run The Protocol

For synchronous tooling, use SDKOps::ProcessNpc(...).

1#include "CLI/CliOperations.h"
2#include "Memory/MemoryThunks.h"
3#include "Cortex/CortexThunks.h"
4
5SDKOps::InitNodeMemory(Store, TEXT("/tmp/quartermaster.db"));
6SDKOps::InitCortex(Store, TEXT("smollm2-135m"));
7
8FAgentResponse Response =
9 SDKOps::ProcessNpc(Store, Npc.Id, TEXT("What supplies are left?"));

For direct parity with packages/core, dispatch rtk::processNPC(...) yourself from Protocol/ProtocolThunks.h.

Protocol Semantics

  • The API can return IdentifyActor, QueryVector, ExecuteInference, and Finalize instructions.
  • QueryVector requires local memory support in the runtime.
  • ExecuteInference requires local cortex support in the runtime.
  • On successful finalize, the runtime persists memory instructions, merges state deltas, records the last action, and appends user/assistant history.
  • On invalid finalize, the slice records bIsBlocked and BlockReason.

Convenience Wrappers

SDKOps exposes the same NPC lifecycle in synchronous form:

  • SDKOps::CreateNpc(Store, Persona)
  • SDKOps::GetActiveNpc(Store)
  • SDKOps::UpdateNpc(Store, NpcId, Delta)
  • SDKOps::ProcessNpc(Store, NpcId, Text)
  • SDKOps::ListNpcs(Store)