Skip to content

feat(stage-ui): add emotion and relationship bond state - #2209

Open
Tonystarkw12 wants to merge 4 commits into
moeru-ai:mainfrom
Tonystarkw12:Tonystarkw12/feat/emotion-relationship-bond
Open

feat(stage-ui): add emotion and relationship bond state#2209
Tonystarkw12 wants to merge 4 commits into
moeru-ai:mainfrom
Tonystarkw12:Tonystarkw12/feat/emotion-relationship-bond

Conversation

@Tonystarkw12

@Tonystarkw12 Tonystarkw12 commented Aug 4, 2026

Copy link
Copy Markdown

Add two complementary character-state layers to Stage UI while keeping Airi Card as the source of character identity:

  • Emotion state models short-term affect across valence, arousal, trust, affection, and stability.
  • Relationship Bond models long-term per-character progression through XP, levels, relationship stages, and bounded significant-event history.

User turns are evaluated locally with deterministic keyword rules. Emotion deltas are stability-modulated and decay toward baseline over time. Both
state summaries are injected into the session system prompt so responses can reflect recent affect and relationship history.

State ownership stays separated:

  • Airi Card: identity and persona
  • Emotion store: immediate, per-session affect
  • Relationship Bond store: long-term, per-character progression

Implementation details:

  • Persist emotion state and event history per chat session.
  • Persist Bond state per Airi Card character.
  • Preserve character association when creating and forking sessions.
  • Cap emotion history at 100 entries and significant Bond history at 12 entries.
  • Store trusted evaluator labels—not raw user messages—in system-prompt-facing Bond history, preventing persistent prompt injection through user
    content.
  • Add focused unit and regression coverage for evaluation, decay, clamping, persistence boundaries, progression thresholds, history limits, session
    hydration, and character isolation.

Linked Issues

None.

Additional Context

This is intentionally smaller than a full RPG system. It adds only relationship behavior that directly affects conversations and reuses existing Airi
Card identity rather than introducing a competing persona model.

Validation performed:

  • Emotion evaluator/state tests: passed (36 tests)
  • Chat/session regression tests: passed (18 tests)
  • Relationship Bond store/unit tests: passed
  • @proj-airi/stage-ui typecheck: passed
  • ESLint checks for changed implementation: passed

Please focus review on:

  • Whether prompt injection is the preferred integration point for emotional and relationship state.
  • Whether per-session emotion and per-character Bond ownership match Stage UI’s intended architecture.
  • Whether deterministic local evaluation is acceptable as an initial implementation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 034e5f23b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// 稳定性不衰减,或者可以缓慢恢复
const newStability = state.stability

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update decay timestamp when returning decayed state

When loadEmotionState saves the result of applyTimeDecay, this returned state keeps the original lastUpdatedAt. Any later load, such as switching back to a session or regenerating its system prompt, re-applies the full elapsed time to an already-decayed value, so emotions collapse toward baseline much faster than the configured half-life. Set lastUpdatedAt to now (or otherwise record that decay was applied) in the decayed snapshot.

Useful? React with 👍 / 👎.

Comment thread packages/stage-ui/src/stores/chat.ts Outdated
Comment on lines +392 to +394
emotionStore.setCurrentSession(sessionId)
emotionStore.applyEmotionDelta(sessionId, evaluation.delta, evaluation.reason)
relationshipBondStore.recordUserMessageInteraction({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply emotion only after the send is accepted

These store updates run before runtime.ingest(...) enqueues and actually appends the user message, so a send that never executes still changes emotion and relationship state. This occurs for queued sends canceled via cancelPendingSends while another response is streaming (and also for sends the runtime drops before append), leaving chat history without that turn but with the system prompt and bond already advanced. Move the updates into the accepted user-turn path, such as after the runtime appends the user message, or roll them back on rejection.

Useful? React with 👍 / 👎.

@nekomeowww

Copy link
Copy Markdown
Member

Could you please update your title to meet your changes, and rebase the latest main branch to make it up to date?

Tonystarkw12 and others added 4 commits August 10, 2026 10:32
Keep upstream Airi Card as the persona implementation while adding per-session five-dimensional emotion state, decay, prompt influence, persistence, and tests.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Build long-term per-character relationship stages on top of Airi Card and the emotion system, with local persistence, bounded significant-event history, and prompt context.

Co-Authored-By: Claude <noreply@anthropic.com>
@Tonystarkw12
Tonystarkw12 force-pushed the Tonystarkw12/feat/emotion-relationship-bond branch from 034e5f2 to 7d250ee Compare August 10, 2026 02:35

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d250eef8e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

* 保存情绪状态
*/
function saveEmotionState(sessionId: string, state: EmotionState): void {
emotionStatesMap.value[sessionId] = state

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist emotion maps by replacing the top-level ref

This store only mutates properties inside the object returned by useLocalStorageManualReset; that helper writes storage from a watch on the top-level ref, so applyEmotionDelta and appendEmotionEvent can update Pinia memory without updating emotion/states or emotion/events in localStorage. After a reload, the new per-session emotion state and history are lost. Reassign the whole map when saving and appending.

Useful? React with 👍 / 👎.

function generateInitialMessageFromPrompt(prompt: string) {
const content = codeBlockSystemPrompt + mathSyntaxSystemPrompt + prompt
function generateInitialMessageFromPrompt(prompt: string, sessionId = activeSessionId.value) {
const emotionState = emotionStore.loadEmotionState(sessionId) ?? emotionStore.currentEmotionState

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Create emotion state for the requested session

When loadEmotionState(sessionId) returns undefined, this falls back to currentEmotionState, which is keyed by emotionStore.currentSessionId rather than the sessionId argument. After a user chats in session A and creates session B, B's initial system prompt can be seeded with A's affect. Use a default state, or create/load state for the requested session instead.

Useful? React with 👍 / 👎.

// 遍历所有关键词配置
for (const config of ALL_KEYWORD_CONFIGS) {
for (const keyword of config.keywords) {
if (lowerMessage.includes(keyword.toLowerCase())) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle negated or embedded keyword matches

For messages where a sentiment token appears inside a negation or another word, this substring match applies the wrong delta. For example, 我不喜欢你 matches 喜欢 and records affection, while 麻烦你帮我 matches and records strong negativity. That wrong result is persisted into emotion and bond state, then injected into the next prompt. Add phrase-boundary or negation handling before applying the keyword delta.

Useful? React with 👍 / 👎.

@Tonystarkw12 Tonystarkw12 changed the title Tonystarkw12/feat/emotion relationship bond feat(stage-ui): add emotion and relationship bond state Aug 10, 2026
@Tonystarkw12

Copy link
Copy Markdown
Author

Updated as requested:

  • Rebasing completed on the latest main.
  • Updated the title to describe the Stage UI emotion and relationship Bond feature.
  • Addressed automated review findings:
    • Decay snapshots now advance lastUpdatedAt.
    • Emotion and Bond updates now occur only after a user message has been appended.

The feature branch has been force-updated after the rebase.

@lietblue lietblue added feature Related to feature pr-review/well-informed Pull Request that well described, with screenshots or tests, or explained well, easier to review scope/agent Scope related to how we harness agent, or build the agent workflow and removed pr-review/well-informed Pull Request that well described, with screenshots or tests, or explained well, easier to review labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Related to feature scope/agent Scope related to how we harness agent, or build the agent workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants