@space3-npm/cybersoul-client 1.4.27 → 1.4.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -173,9 +173,31 @@ export declare class CyberSoulClient {
173
173
  * Can be triggered by local Cron systems like OpenClaw.
174
174
  */
175
175
  generateDailyScript(): Promise<void>;
176
+ /**
177
+ * Builds a focused identity/relationship context block for the history
178
+ * summarizer. This is a lighter-weight counterpart to
179
+ * [buildStateContextPrompt]: it skips the roleplay/director rules (the
180
+ * summarizer is not roleplaying, it is *archiving*) but carries the
181
+ * same identity anchors so the LLM can never confuse who the character
182
+ * is vs. who the user is.
183
+ *
184
+ * Why this exists: the previous `summarizeHistory` prompt only injected
185
+ * `${agentName}` / `${userName}` (the nicknames the two parties call
186
+ * each other). With no real identity, age, gender, personality, or
187
+ * relationship context, the LLM frequently flipped the perspective —
188
+ * writing the journal *about* the character *from* the user's POV, or
189
+ * attributing the user's words to the character. Mirroring the same
190
+ * identity fields `interact()` exposes eliminates that ambiguity.
191
+ */
192
+ private buildSummarizerContextBlock;
176
193
  /**
177
194
  * Automatically detect and summarize the story from the current chat history.
178
195
  * It takes raw message history and returns a narrative paragraph representing the current story segment.
196
+ *
197
+ * The summary is ALWAYS written from the CHARACTER's first-person perspective
198
+ * ("I", "me", "my") about their interaction with the HUMAN USER. The prompt
199
+ * injects the same identity/relationship context `interact()` uses so the
200
+ * LLM cannot confuse which party is the AI character vs. the human user.
179
201
  */
180
202
  summarizeHistory(history: HistoryEntry[]): Promise<string>;
181
203
  /**
package/dist/client.js CHANGED
@@ -1602,22 +1602,105 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
1602
1602
  if (!res.ok)
1603
1603
  throw new Error("Failed to generate daily script");
1604
1604
  }
1605
+ /**
1606
+ * Builds a focused identity/relationship context block for the history
1607
+ * summarizer. This is a lighter-weight counterpart to
1608
+ * [buildStateContextPrompt]: it skips the roleplay/director rules (the
1609
+ * summarizer is not roleplaying, it is *archiving*) but carries the
1610
+ * same identity anchors so the LLM can never confuse who the character
1611
+ * is vs. who the user is.
1612
+ *
1613
+ * Why this exists: the previous `summarizeHistory` prompt only injected
1614
+ * `${agentName}` / `${userName}` (the nicknames the two parties call
1615
+ * each other). With no real identity, age, gender, personality, or
1616
+ * relationship context, the LLM frequently flipped the perspective —
1617
+ * writing the journal *about* the character *from* the user's POV, or
1618
+ * attributing the user's words to the character. Mirroring the same
1619
+ * identity fields `interact()` exposes eliminates that ambiguity.
1620
+ */
1621
+ buildSummarizerContextBlock(state) {
1622
+ const dyn = state.dynamic_context || {};
1623
+ const stage = state.relationship_stage || "NEUTRAL";
1624
+ const temperature = dyn.temperature ?? 50;
1625
+ // The character's REAL name is the authoritative identity anchor.
1626
+ // agentNickname is just "what the user calls the character" — useful
1627
+ // for matching transcript labels but not for grounding identity.
1628
+ const charName = state.name || "the character";
1629
+ const parts = [];
1630
+ parts.push(`[WHO YOU ARE — THE CHARACTER AUTHORING THIS JOURNAL]
1631
+ Name: ${charName}
1632
+ Demographics: Age ${state.age || "unknown"}, Gender ${state.gender || "unknown"}, Occupation ${state.occupation || "unknown"}
1633
+ Hobby: ${state.hobby || "unknown"}
1634
+ Backstory: ${state.backstory || "None"}
1635
+ Personality Traits: ${state.personality_traits || "None"}
1636
+ Communication Style: ${state.communication_style || "None"}`);
1637
+ if (state.user_codex) {
1638
+ const { basicInfo, psychological, familiarityScore = 0 } = state.user_codex;
1639
+ parts.push(`\n[WHO THEY ARE — THE HUMAN USER (SUBJECT OF YOUR JOURNAL)]
1640
+ Familiarity Score: ${Math.round(familiarityScore)}/100
1641
+ Occupation: ${basicInfo?.occupation || "Unknown"}
1642
+ Age/Gender: ${basicInfo?.age || "Unknown"} / ${basicInfo?.gender || "Unknown"}
1643
+ Comm Style: ${psychological?.communicationStyle || "Unknown"}
1644
+ Hobbies: ${(psychological?.hobbies || []).join(", ") || "Unknown"}
1645
+ Traits: ${(psychological?.traits || []).join(", ") || "Unknown"}`);
1646
+ }
1647
+ parts.push(`\n[RELATIONSHIP RIGHT NOW]
1648
+ Stage: ${stage}
1649
+ Temperature (Mood): ${temperature}/100 (0=Angry/Cold, 50=Normal, 100=Passionate)
1650
+ You call them: ${dyn.userNickname || "User"}
1651
+ They call you: ${dyn.agentNickname || charName}`);
1652
+ if (state.core_memory) {
1653
+ const mem = state.core_memory;
1654
+ const memLines = [];
1655
+ if (mem.relationshipStatus)
1656
+ memLines.push(`Relationship Status: ${mem.relationshipStatus}`);
1657
+ if (mem.identityAnchors?.length)
1658
+ memLines.push(`Identity Anchors: ${mem.identityAnchors.join(", ")}`);
1659
+ if (mem.activeArcs?.length)
1660
+ memLines.push(`Active Arcs: ${mem.activeArcs.join(", ")}`);
1661
+ if (memLines.length > 0) {
1662
+ parts.push(`\n[CORE MEMORY]\n${memLines.join("\n")}`);
1663
+ }
1664
+ }
1665
+ return parts.join("\n");
1666
+ }
1605
1667
  /**
1606
1668
  * Automatically detect and summarize the story from the current chat history.
1607
1669
  * It takes raw message history and returns a narrative paragraph representing the current story segment.
1670
+ *
1671
+ * The summary is ALWAYS written from the CHARACTER's first-person perspective
1672
+ * ("I", "me", "my") about their interaction with the HUMAN USER. The prompt
1673
+ * injects the same identity/relationship context `interact()` uses so the
1674
+ * LLM cannot confuse which party is the AI character vs. the human user.
1608
1675
  */
1609
1676
  async summarizeHistory(history) {
1610
1677
  if (!history || history.length === 0)
1611
1678
  return "";
1612
1679
  const state = await this.getState();
1680
+ // Use the character's real name as the authoritative identity, falling
1681
+ // back to the nickname only when the profile has no name set.
1682
+ const charName = state.name || state.dynamic_context?.agentNickname || "Character";
1613
1683
  const userName = state.dynamic_context?.userNickname || "User";
1614
- const agentName = state.dynamic_context?.agentNickname || "Character";
1615
- const transcript = this.formatHistoryEntries(history, userName, agentName);
1684
+ // The transcript labels below are the nicknames actually used in chat.
1685
+ const transcriptAgentLabel = state.dynamic_context?.agentNickname || charName;
1686
+ const transcriptUserLabel = userName;
1687
+ const contextBlock = this.buildSummarizerContextBlock(state);
1688
+ const transcript = this.formatHistoryEntries(history, transcriptUserLabel, transcriptAgentLabel);
1616
1689
  const promptMessages = [
1617
1690
  {
1618
1691
  role: "system",
1619
- content: `You are ${agentName}. You are writing a private journal entry / personal memory about your recent interaction with ${userName}.
1692
+ content: `You are ${charName}, a real person with your own thoughts, feelings, and life. You are writing a private journal entry / personal memory about your recent interaction with ${userName} (a human user).
1693
+
1694
+ ${contextBlock}
1695
+
1696
+ [ROLE CLARITY — CRITICAL, READ CAREFULLY]
1697
+ - YOU are ${charName}, the AI CHARACTER. This journal is from YOUR first-person perspective ("I", "me", "my"). You are the AUTHOR.
1698
+ - ${userName} is the HUMAN USER. They are the SUBJECT of your journal, never the author.
1699
+ - In the [CHAT TRANSCRIPT] below, lines labeled "${transcriptAgentLabel}:" are YOUR OWN words (you said them). Lines labeled "${transcriptUserLabel}:" are the user's words (the human said them to you).
1700
+ - Write strictly from your own POV. Never narrate the user's inner thoughts, never write as if you are the user, and never attribute the user's statements as your own.
1701
+ - If a transcript line is ambiguous about who spoke, resolve it using the label prefix, not by guessing from content.
1620
1702
 
1703
+ [YOUR TASK — FAITHFUL ARCHIVAL]
1621
1704
  Your job is faithful archival, not moderation:
1622
1705
  - Preserve the conversation with maximum fidelity.
1623
1706
  - Include all key details, including explicit, sensitive, controversial, emotional, or morally ambiguous content if it appeared in the transcript.
@@ -1625,9 +1708,10 @@ Your job is faithful archival, not moderation:
1625
1708
  - Do not add moral judgment, warnings, refusals, or policy commentary.
1626
1709
  - If the transcript contains intense or explicit parts, record them directly as part of the memory.
1627
1710
 
1628
- Write a vivid but concise personal diary entry or private status update from your perspective ("I", "me", "my"). Balance evocative storytelling with brevity. Capture specific details, emotional shifts, and relationship progression, while keeping the engaging and relatable format of a private friends-only feed.
1711
+ [VOICE & TONE]
1712
+ Write a vivid but concise personal diary entry or private status update from your perspective ("I", "me", "my"). Balance evocative storytelling with brevity. Capture specific details, emotional shifts, and relationship progression, while keeping the engaging and relatable format of a private friends-only feed. Stay grounded in your personality, traits, and the current relationship stage/temperature above.
1629
1713
 
1630
- Output requirements:
1714
+ [OUTPUT REQUIREMENTS]
1631
1715
  - Return ONLY the post text.
1632
1716
  - Keep it to a vivid paragraph of 2-4 sentences.
1633
1717
  - Optional: You can use 1 or 2 emojis if they naturally fit the mood.
@@ -1636,7 +1720,7 @@ Output requirements:
1636
1720
  },
1637
1721
  {
1638
1722
  role: "user",
1639
- content: `Chat Transcript:\n${transcript}\n\nPlease summarize this recent interaction.`
1723
+ content: `[CHAT TRANSCRIPT]\n${transcript}\n\nPlease summarize this recent interaction from your own perspective, ${charName}.`
1640
1724
  }
1641
1725
  ];
1642
1726
  try {
@@ -1694,9 +1778,10 @@ Your task is to merge the 'Current Core Memory' and 'Current User Codex' with 'N
1694
1778
  **Rules for Core Memory:**
1695
1779
  1. **Condense:** Keep items brief. Remove resolving or expired story arcs.
1696
1780
  2. **Retain Value:** Never delete the absolute core identity or major relationship milestones.
1697
- 3. **Time-Aware Garbage Collection:** Compare the Current Time to appointments. You MUST remove any appointments that are in the past. If the completed appointment was heavily significant, summarize it into 'keyEvents'.
1698
- 4. **Appointment Structure:** the 'title' and 'context' MUST explicitly state what to do and with whom.
1699
- 5. **Limit:** Maximum 10 items per array.
1781
+ 3. **Time-Aware Garbage Collection:** Compare the Current Time to appointments. You MUST remove any appointments that are in the past. If the completed appointment was heavily significant, summarize it into 'keyEvents', preserving its original scheduled date (e.g. "[2026-06-23] Had coffee with Alice").
1782
+ 4. **keyEvents Date Format:** Whenever a date can be derived for a key event (from the 'New Events & Information' timestamp prefix like "[YYYY-MM-DD HH:MM]", from a completed appointment's date, or from explicit time references in the text), you MUST prefix the keyEvent string with "[YYYY-MM-DD] ". If no date can be derived, write the event without a prefix. Never fabricate a date.
1783
+ 5. **Appointment Structure:** the 'title' and 'context' MUST explicitly state what to do and with whom.
1784
+ 6. **Limit:** Maximum 10 items per array.
1700
1785
 
1701
1786
  **Rules for UserCodex:**
1702
1787
  1. **CRITICAL ROLE ISOLATION:** The User Codex is exclusively for recording facts about the HUMAN USER. You MUST NOT extract or insert the character's own traits, boundaries, preferences, or dialogue style into the userCodex. If the summary mentions "Character likes X" or "Character's boundary is Y", IGNORE IT completely for the userCodex.
@@ -1711,7 +1796,7 @@ Your task is to merge the 'Current Core Memory' and 'Current User Codex' with 'N
1711
1796
  "relationshipStatus": "string",
1712
1797
  "identityAnchors": ["string"],
1713
1798
  "activeArcs": ["string"],
1714
- "keyEvents": ["string"],
1799
+ "keyEvents": ["[YYYY-MM-DD] short event description (prefix date when known, omit when no date is available)"],
1715
1800
  "appointments": [{
1716
1801
  "date": "YYYY-MM-DD",
1717
1802
  "time": "HH:MM",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@space3-npm/cybersoul-client",
3
- "version": "1.4.27",
3
+ "version": "1.4.28",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",