@space3-npm/cybersoul-client 1.3.6 → 1.3.8
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 +4 -5
- package/dist/client.js +65 -31
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, LikedPicture } from "./types.js";
|
|
1
|
+
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, HistoryEntry, LikedPicture } from "./types.js";
|
|
2
2
|
export declare class CyberSoulClient {
|
|
3
3
|
private config;
|
|
4
4
|
private llm;
|
|
@@ -16,6 +16,7 @@ export declare class CyberSoulClient {
|
|
|
16
16
|
private generatePrimitive;
|
|
17
17
|
private _updateDynamicContextInternal;
|
|
18
18
|
private normalizeRequestTypes;
|
|
19
|
+
private getElapsedTimeInfo;
|
|
19
20
|
private buildStateContextPrompt;
|
|
20
21
|
private normalizeOngoingSceneState;
|
|
21
22
|
private getImageSchemaParams;
|
|
@@ -41,6 +42,7 @@ export declare class CyberSoulClient {
|
|
|
41
42
|
* If the payload is already the inner args object (no voiceArgs wrapper), uses it as-is.
|
|
42
43
|
*/
|
|
43
44
|
private extractVoiceArgsFromLlmResponse;
|
|
45
|
+
private formatHistoryEntries;
|
|
44
46
|
private buildHistoryTranscript;
|
|
45
47
|
interact(params: InteractParams): Promise<InteractResponse>;
|
|
46
48
|
/**
|
|
@@ -96,10 +98,7 @@ export declare class CyberSoulClient {
|
|
|
96
98
|
* Automatically detect and summarize the story from the current chat history.
|
|
97
99
|
* It takes raw message history and returns a narrative paragraph representing the current story segment.
|
|
98
100
|
*/
|
|
99
|
-
summarizeHistory(history:
|
|
100
|
-
role: string;
|
|
101
|
-
content: string;
|
|
102
|
-
}[]): Promise<string>;
|
|
101
|
+
summarizeHistory(history: HistoryEntry[]): Promise<string>;
|
|
103
102
|
/**
|
|
104
103
|
* Save the recent story moment to the character's backend database to be picked up by the core memory consolidation.
|
|
105
104
|
*/
|
package/dist/client.js
CHANGED
|
@@ -154,6 +154,23 @@ export class CyberSoulClient {
|
|
|
154
154
|
}
|
|
155
155
|
return normalized;
|
|
156
156
|
}
|
|
157
|
+
getElapsedTimeInfo(currentTimeMs, lastInteractionAt) {
|
|
158
|
+
const elapsedMs = Math.max(0, currentTimeMs - new Date(lastInteractionAt).getTime());
|
|
159
|
+
const elapsedMins = elapsedMs / (1000 * 60);
|
|
160
|
+
const elapsedHours = elapsedMins / 60;
|
|
161
|
+
const elapsedDays = elapsedHours / 24;
|
|
162
|
+
const elapsedYears = elapsedDays / 365;
|
|
163
|
+
let displayStr = "";
|
|
164
|
+
if (elapsedYears >= 1)
|
|
165
|
+
displayStr = `${elapsedYears.toFixed(1)} years`;
|
|
166
|
+
else if (elapsedDays >= 1)
|
|
167
|
+
displayStr = `${elapsedDays.toFixed(1)} days`;
|
|
168
|
+
else if (elapsedHours >= 1)
|
|
169
|
+
displayStr = `${elapsedHours.toFixed(1)} hours`;
|
|
170
|
+
else
|
|
171
|
+
displayStr = `${Math.floor(elapsedMins)} mins`;
|
|
172
|
+
return { elapsedMs, elapsedMins, elapsedHours, elapsedDays, elapsedYears, displayStr };
|
|
173
|
+
}
|
|
157
174
|
buildStateContextPrompt(state, isProactive = false) {
|
|
158
175
|
const dyn = state.dynamic_context || {};
|
|
159
176
|
const stage = state.relationship_stage || "NEUTRAL";
|
|
@@ -180,28 +197,18 @@ Current time: ${new Date(currentTimeMs).toLocaleString("zh-CN", { timeZone: "Asi
|
|
|
180
197
|
const scenePrefix = "Last Known Scene";
|
|
181
198
|
let timeAgoStr = scenePrefix;
|
|
182
199
|
let isOutdated = false;
|
|
183
|
-
let
|
|
200
|
+
let timeDisplayStr = "";
|
|
184
201
|
if (dyn.lastInteractionAt) {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const elapsedYears = elapsedDays / 365;
|
|
190
|
-
if (elapsedYears >= 1)
|
|
191
|
-
timeAgoStr = `${scenePrefix} ${elapsedYears.toFixed(1)} years ago`;
|
|
192
|
-
else if (elapsedDays >= 1)
|
|
193
|
-
timeAgoStr = `${scenePrefix} ${elapsedDays.toFixed(1)} days ago`;
|
|
194
|
-
else if (elapsedHours >= 1)
|
|
195
|
-
timeAgoStr = `${scenePrefix} ${elapsedHours.toFixed(1)} hours ago`;
|
|
196
|
-
else
|
|
197
|
-
timeAgoStr = `${scenePrefix} ${Math.floor(elapsedMins)} mins ago`;
|
|
198
|
-
if (elapsedHours > 1) {
|
|
202
|
+
const timeInfo = this.getElapsedTimeInfo(currentTimeMs, dyn.lastInteractionAt);
|
|
203
|
+
timeDisplayStr = timeInfo.displayStr;
|
|
204
|
+
timeAgoStr = `${scenePrefix} ${timeDisplayStr} ago`;
|
|
205
|
+
if (timeInfo.elapsedHours > 1) {
|
|
199
206
|
isOutdated = true;
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
const lastKnownSceneLine = `${timeAgoStr}: ${ongoingScene.scene} | Outfit: ${ongoingScene.outfit}`;
|
|
203
210
|
if (isOutdated) {
|
|
204
|
-
contextParts.push(
|
|
211
|
+
contextParts.push(`Previous Activity (Ended ${timeDisplayStr} ago): ${ongoingScene.scene}\n[SCENE RESET]: A significant amount of time has passed. The previous activity is completely over. You are now in a fresh, natural state based on your current Wardrobe and Time. Do NOT continue the previous actions.`);
|
|
205
212
|
}
|
|
206
213
|
else {
|
|
207
214
|
contextParts.push(`${lastKnownSceneLine} (Evaluate whether this scene is still valid based on how much time has passed since it was last updated.)`);
|
|
@@ -278,12 +285,13 @@ ${scenarioContext}
|
|
|
278
285
|
|
|
279
286
|
[CRITICAL ROLEPLAY RULES]
|
|
280
287
|
1. PROXIMITY & POV: Check the "Active Event". If you are doing an activity WITH the user, evaluate if you are physically in the same location. If you are together in person, communicate face-to-face in the first-person present tense natively (e.g. do not ask "what are you doing" if they are right in front of you, do not use texting tropes).
|
|
281
|
-
2.
|
|
282
|
-
3. CONVERSATIONAL VERBOSITY: If Temperature is low (< 40)
|
|
283
|
-
4. EMOTIONAL INERTIA: React strictly according to current Temperature. Deflect sudden
|
|
288
|
+
2. STAGE VS MOOD PARADOX: 'Stage' dictates your foundational relationship boundary. 'Temperature' is merely your current fleeting mood. You MUST interpret Temperature through the lens of Stage. For example, a high Temperature (80) as a STRANGER means "polite curiosity or intrigued", NOT "deeply in love". A low Temperature (20) as an INTIMATE means "a lover's quarrel or hurt feelings", NOT "a stranger's amnesia". Never act above your Stage.
|
|
289
|
+
3. CONVERSATIONAL VERBOSITY: If Temperature is very low (< 40), keep answers brief and crisp—an annoyed or distant person doesn't write paragraphs. Regardless of mood or stage, ALWAYS mirror the user's verbosity. If the user sends a short message, reply with a proportionately short message (1-2 sentences). Do not monologize unless the user writes one first.
|
|
290
|
+
4. EMOTIONAL INERTIA: React strictly according to current Temperature. Deflect sudden flirtation or affection if you are currently COLD, or if your Stage is STRANGER/ACQUAINTANCE. Mood shifts MUST be slow ('temperatureDelta' +/- 5 max per turn).
|
|
284
291
|
${isProactive
|
|
285
292
|
? "5. REAL-TIME PACING: You are initiating the conversation because the user hasn't replied recently. Transition naturally from your last message or start a new topic seamlessly. Ensure everything happens in a single real-time moment."
|
|
286
|
-
: "5. REAL-TIME PACING: Write ONLY your immediate, split-second reaction to the user's exact last message. Do NOT narrate actions over a span of time (e.g., waiting, hearing steps, then walking to the door). Ensure everything happens in a single real-time moment."}
|
|
293
|
+
: "5. REAL-TIME PACING: Write ONLY your immediate, split-second reaction to the user's exact last message. Do NOT narrate actions over a span of time (e.g., waiting, hearing steps, then walking to the door). Ensure everything happens in a single real-time moment."}
|
|
294
|
+
6. STRANGER BOUNDARY: Keep a polite, natural distance with strangers. If Familiarity is low or Stage is STRANGER, do not act overly warm, eager, or affectionate. Real humans are guarded with people they just met.`;
|
|
287
295
|
}
|
|
288
296
|
normalizeOngoingSceneState(raw, fallbackOutfit) {
|
|
289
297
|
if (raw === null || raw === undefined)
|
|
@@ -399,20 +407,44 @@ ${isProactive
|
|
|
399
407
|
}
|
|
400
408
|
return payload;
|
|
401
409
|
}
|
|
410
|
+
formatHistoryEntries(history, userName, agentName, promptDirective = "") {
|
|
411
|
+
const contextLines = [];
|
|
412
|
+
for (let i = 0; i < history.length; i++) {
|
|
413
|
+
const msg = history[i];
|
|
414
|
+
if (i > 0 && history[i - 1].timestamp && msg.timestamp) {
|
|
415
|
+
const prevTime = new Date(history[i - 1].timestamp).getTime();
|
|
416
|
+
const currTime = new Date(msg.timestamp).getTime();
|
|
417
|
+
const timeInfo = this.getElapsedTimeInfo(currTime, prevTime);
|
|
418
|
+
if (timeInfo.elapsedHours > 1) {
|
|
419
|
+
contextLines.push(`\n[--- ${timeInfo.displayStr} later ---${promptDirective ? " " + promptDirective : ""} ---]\n`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const speaker = msg.role === 'user' ? userName : (msg.role === 'assistant' || msg.role === 'agent' ? agentName : msg.role);
|
|
423
|
+
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
|
|
424
|
+
const action = msg.actionText ? ` (${msg.actionText})` : "";
|
|
425
|
+
const media = msg.mediaHint ? ` [${msg.mediaHint}]` : "";
|
|
426
|
+
contextLines.push(`${speaker}:${action} ${content}${media}`);
|
|
427
|
+
}
|
|
428
|
+
return contextLines.join('\n');
|
|
429
|
+
}
|
|
402
430
|
buildHistoryTranscript(history, state) {
|
|
403
431
|
if (!history || history.length === 0)
|
|
404
432
|
return "";
|
|
405
433
|
const recentHistory = history.slice(-20);
|
|
406
434
|
const agentName = state.dynamic_context?.agentNickname || state.name || "Agent";
|
|
407
435
|
const userName = state.dynamic_context?.userNickname || "User";
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
436
|
+
const directive = "The previous chat history is completely outdated by the time passage. Do not continue its immediate action flow.";
|
|
437
|
+
const transcript = this.formatHistoryEntries(recentHistory, userName, agentName, directive);
|
|
438
|
+
let historyContent = `[CHAT HISTORY]\n${transcript}\n`;
|
|
439
|
+
// If there is a massive time gap between the chat history and the VERY LAST USER MESSAGE
|
|
440
|
+
if (state.dynamic_context?.lastInteractionAt) {
|
|
441
|
+
const currentTimeMs = state.current_time ? new Date(state.current_time).getTime() : Date.now();
|
|
442
|
+
const timeInfo = this.getElapsedTimeInfo(currentTimeMs, state.dynamic_context.lastInteractionAt);
|
|
443
|
+
if (timeInfo.elapsedHours > 1) {
|
|
444
|
+
historyContent += `\n[--- ${timeInfo.displayStr} later --- The previous chat history is completely outdated by the time passage. Do not continue its immediate action flow. ---]\n`;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return historyContent + "\n";
|
|
416
448
|
}
|
|
417
449
|
async interact(params) {
|
|
418
450
|
try {
|
|
@@ -1015,7 +1047,7 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1015
1047
|
const state = await this.getState();
|
|
1016
1048
|
const userName = state.dynamic_context?.userNickname || "User";
|
|
1017
1049
|
const agentName = state.dynamic_context?.agentNickname || "Character";
|
|
1018
|
-
const transcript =
|
|
1050
|
+
const transcript = this.formatHistoryEntries(history, userName, agentName);
|
|
1019
1051
|
const promptMessages = [
|
|
1020
1052
|
{
|
|
1021
1053
|
role: "system",
|
|
@@ -1028,10 +1060,12 @@ Your job is faithful archival, not moderation:
|
|
|
1028
1060
|
- Do not add moral judgment, warnings, refusals, or policy commentary.
|
|
1029
1061
|
- If the transcript contains intense or explicit parts, record them directly as part of the memory.
|
|
1030
1062
|
|
|
1031
|
-
Write
|
|
1063
|
+
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.
|
|
1032
1064
|
|
|
1033
1065
|
Output requirements:
|
|
1034
|
-
- Return ONLY the
|
|
1066
|
+
- Return ONLY the post text.
|
|
1067
|
+
- Keep it to a vivid paragraph of 2-4 sentences.
|
|
1068
|
+
- Optional: You can use 1 or 2 emojis if they naturally fit the mood.
|
|
1035
1069
|
- No quotes, no labels, no markdown, no preface.
|
|
1036
1070
|
- Use the exact same language as the chat transcript (for example, if transcript is Chinese, output Chinese).`
|
|
1037
1071
|
},
|
package/dist/types.d.ts
CHANGED