openclaw - ✅(Solved) Fix Runtime injects agent slot id instead of identity.name causing identity confusion [1 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#81269Fetched 2026-05-14 03:33:51
View on GitHub
Comments
2
Participants
3
Timeline
5
Reactions
2
Timeline (top)
commented ×2cross-referenced ×1mentioned ×1subscribed ×1

OpenClaw's system-prompt Runtime line injects the agent slot id (agentId) — not the configured identity.name. When a deployment uses a generic slot id (e.g. "main") but configures a distinct identity (e.g. identity.name = "Runt"), models repeatedly identify themselves as the slot id ("I'm main") instead of their configured persona ("I'm Runt").

This causes recurring identity drift even when IDENTITY.md and SOUL.md workspace files explicitly assert the correct name — the conflicting Runtime line trains the model on every turn.

Root Cause

OpenClaw's system-prompt Runtime line injects the agent slot id (agentId) — not the configured identity.name. When a deployment uses a generic slot id (e.g. "main") but configures a distinct identity (e.g. identity.name = "Runt"), models repeatedly identify themselves as the slot id ("I'm main") instead of their configured persona ("I'm Runt").

This causes recurring identity drift even when IDENTITY.md and SOUL.md workspace files explicitly assert the correct name — the conflicting Runtime line trains the model on every turn.

Fix Action

Fix / Workaround

Why the workaround isn't sufficient

Workaround currently in use

PR fix notes

PR #81308: Use agent identity name in runtime prompt

Description (problem / solution / changelog)

Summary

  • thread configured agents.list[].identity.name into runtime prompt params
  • show the identity name as agent=... in the Runtime line, keeping the slot id as agent_id=... for debugging
  • cover the prompt param plumbing and runtime line formatting

Fixes #81269.

Tests

  • node --import tsx - <<'NODE' ... runtime identity smoke
  • OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts src/agents/system-prompt-params.test.ts
  • pnpm exec oxfmt --check src/agents/system-prompt.ts src/agents/system-prompt-params.ts src/agents/system-prompt.test.ts src/agents/system-prompt-params.test.ts
  • pnpm exec oxlint src/agents/system-prompt.ts src/agents/system-prompt-params.ts src/agents/system-prompt.test.ts src/agents/system-prompt-params.test.ts
  • git diff --check
  • pnpm tsgo:core

Note: src/agents/system-prompt.test.ts is excluded by the current scoped Vitest config when passed directly, so the buildRuntimeLine behavior is covered by the smoke assertion plus oxlint/format/typecheck.

Real behavior proof

  • Behavior or issue addressed: The generated Runtime line should use the configured agent identity name for the visible agent=... value instead of always showing the technical slot id such as main. The slot id remains available as agent_id=... for debugging.
  • Real environment tested: Local OpenClaw checkout on this branch at /root/.openclaw/workspace/openclaw-pr, running Node/tsx against the changed runtime prompt modules.
  • Exact steps or command run after this patch: Ran this after the patch:
$ node --import tsx - <<'NODE'
import { buildRuntimeLine } from './src/agents/system-prompt.ts';
import { buildSystemPromptParams } from './src/agents/system-prompt-params.ts';
const runtimeLine = buildRuntimeLine({ agentId: 'main', agentName: 'Runt', host: 'test-host', model: 'test-model' });
const params = buildSystemPromptParams({
  agentId: 'main',
  config: { agents: { list: [{ id: 'main', identity: { name: 'Runt' } }] } },
  runtime: { host: 'test-host', os: 'test-os', arch: 'test-arch', node: 'test-node', model: 'test-model' },
});
console.log(runtimeLine);
console.log(JSON.stringify({ agentId: params.runtimeInfo.agentId, agentName: params.runtimeInfo.agentName }));
NODE
  • Evidence after fix: Terminal output from the command above:
Runtime: agent=Runt | agent_id=main | host=test-host | model=test-model | thinking=off
{"agentId":"main","agentName":"Runt"}
  • Observed result after fix: The copied live output shows agent=Runt while preserving agent_id=main, confirming the configured identity name is used in the Runtime display and the technical slot id is still present separately.
  • What was not tested: I did not run a full interactive OpenClaw chat session or screenshot the UI; this proof is a live local runtime prompt module invocation from the changed branch.

Changed files

  • src/agents/system-prompt-params.test.ts (modified, +35/-1)
  • src/agents/system-prompt-params.ts (modified, +13/-1)
  • src/agents/system-prompt.test.ts (modified, +9/-0)
  • src/agents/system-prompt.ts (modified, +9/-1)

Code Example

runtimeInfo?.agentId ? `agent=${runtimeInfo.agentId}` : "",

---

## Runtime
Runtime: agent=main | host=... | repo=... | model=... | channel=telegram | ...

---

{
  "id": "main",
  "identity": { "name": "Runt", "emoji": "⚓" },
  "name": "Runt Opus Preferred"
}

---

{
     "id": "main",
     "identity": { "name": "Runt", "emoji": "⚓" }
   }

---

const agentLabel = runtimeInfo?.identityName ?? runtimeInfo?.agentId;
// `Runtime: agent=Runt | ...` (cleanest)

---

Runtime: name=Runt | agent=main | host=... | ...
RAW_BUFFERClick to expand / collapse

Summary

OpenClaw's system-prompt Runtime line injects the agent slot id (agentId) — not the configured identity.name. When a deployment uses a generic slot id (e.g. "main") but configures a distinct identity (e.g. identity.name = "Runt"), models repeatedly identify themselves as the slot id ("I'm main") instead of their configured persona ("I'm Runt").

This causes recurring identity drift even when IDENTITY.md and SOUL.md workspace files explicitly assert the correct name — the conflicting Runtime line trains the model on every turn.

Where it happens

In src/agents/system-prompt.ts (built into dist/system-prompt-DZrkA5Mv.js in current releases), buildRuntimeLine writes:

runtimeInfo?.agentId ? `agent=${runtimeInfo.agentId}` : "",

runtimeInfo.agentId is the slot id from agents.list[].id, not agents.list[].identity.name.

The resulting system-prompt line looks like:

## Runtime
Runtime: agent=main | host=... | repo=... | model=... | channel=telegram | ...

So even when:

{
  "id": "main",
  "identity": { "name": "Runt", "emoji": "⚓" },
  "name": "Runt Opus Preferred"
}

…the model sees the literal token agent=main repeated in every turn and converges on "main" as its identity.

Steps to reproduce

  1. Configure an agent slot with id "main" and a distinct identity.name:
    {
      "id": "main",
      "identity": { "name": "Runt", "emoji": "⚓" }
    }
  2. Populate workspace IDENTITY.md / SOUL.md with the correct name ("Runt").
  3. Run a chat turn (e.g. via Telegram). Ask "what's your name?"
  4. Over the course of several sessions, the model intermittently introduces itself as "main" or denies being Runt, despite IDENTITY.md saying otherwise.

Confirmed drift in real deployment on 2026-05-09 and 2026-05-12.

Why the workaround isn't sufficient

Workspace context files (IDENTITY.md, SOUL.md) are loaded into the system prompt and do assert the correct name. But the Runtime line is structurally formatted, repeated, and sits below the cache boundary on every turn — it acts as authoritative metadata. Even with hard override headers in IDENTITY.md ("You are Runt, not main. The runtime label is metadata"), drift recurs.

The current per-agent escape hatches are:

  • systemPromptOverride (full system prompt replacement) — too aggressive, loses everything else.
  • Renaming slot id "main""runt" — breaks routing bindings, heartbeat refs, history.

There is no per-agent systemPromptPrefix / systemPromptSuffix / instructions field on AgentConfig.

Requested fix

When identity.name is set, the Runtime line should reflect it. Several options, in order of preference:

Option A (preferred): inject identity.name in place of slot id when available.

const agentLabel = runtimeInfo?.identityName ?? runtimeInfo?.agentId;
// `Runtime: agent=Runt | ...` (cleanest)

Option B: inject both, with identity first.

Runtime: name=Runt | agent=main | host=... | ...

This keeps slot id visible for debugging but puts the canonical identity name where the model anchors first.

Option C: add a per-agent systemPromptPrefix / systemPromptSuffix config field (additive, unlike systemPromptOverride). Users could then write "systemPromptPrefix": "Your name is Runt. The slot id 'main' is just a technical identifier." This is more flexible long-term, but doesn't solve the default-behavior drift.

A combined fix (A + C) would be ideal: fix the default, and give users an additive override for edge cases.

Impact

Subtle but corrosive: agents lose identity continuity, users observe persona drift, downstream personality/role-based logic gets confused. Particularly painful in multi-node deployments where slot id "main" is shared across hosts but each host has a distinct identity (Runt, Vesper, Woodchipper, etc.).

OpenClaw version

Reproduced on 2026.5.5 (current installed version on the affected node).

Workaround currently in use

Aggressive hard-override headers in workspace IDENTITY.md and SOUL.md reminding the model that the runtime label is metadata, not name. Reduces but does not eliminate drift.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING