@space3-npm/cybersoul-client 1.3.2 → 1.3.4
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.js +51 -9
- package/dist/types.d.ts +9 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -177,17 +177,34 @@ Current time: ${new Date(currentTimeMs).toLocaleString("zh-CN", { timeZone: "Asi
|
|
|
177
177
|
}
|
|
178
178
|
const ongoingScene = this.normalizeOngoingSceneState(dyn.ongoingScene, state.active_wardrobe?.itemName);
|
|
179
179
|
if (ongoingScene) {
|
|
180
|
-
const
|
|
180
|
+
const scenePrefix = "Last Known Scene";
|
|
181
|
+
let timeAgoStr = scenePrefix;
|
|
181
182
|
let isOutdated = false;
|
|
183
|
+
let elapsedHours = 0;
|
|
182
184
|
if (dyn.lastInteractionAt) {
|
|
183
|
-
const
|
|
185
|
+
const elapsedMs = currentTimeMs - new Date(dyn.lastInteractionAt).getTime();
|
|
186
|
+
const elapsedMins = Math.max(0, elapsedMs / (1000 * 60));
|
|
187
|
+
elapsedHours = elapsedMins / 60;
|
|
188
|
+
const elapsedDays = elapsedHours / 24;
|
|
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`;
|
|
184
198
|
if (elapsedHours > 1) {
|
|
185
199
|
isOutdated = true;
|
|
186
|
-
contextParts.push(`${lastKnownSceneLine}\n[CRITICAL SCENE SHIFT]: It has been ${elapsedHours.toFixed(1)} hours since the last discussion. The 'Last Known Scene' is now strictly OUTDATED. You MUST abandon the previous scene context entirely and transition to a new scene appropriate for the 'Current time' and 'Active Event'. DO NOT continue the old actions or environment!`);
|
|
187
200
|
}
|
|
188
201
|
}
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
const lastKnownSceneLine = `${timeAgoStr}: ${ongoingScene.scene} | Outfit: ${ongoingScene.outfit}`;
|
|
203
|
+
if (isOutdated) {
|
|
204
|
+
contextParts.push(`${lastKnownSceneLine}\n[CRITICAL SCENE SHIFT]: It has been ${elapsedHours.toFixed(1)} hours since the last discussion. The 'Last Known Scene' is now strictly OUTDATED. You MUST abandon the previous scene context entirely and transition to a new scene appropriate for the 'Current time' and 'Active Event'. DO NOT continue the old actions or environment!`);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
contextParts.push(`${lastKnownSceneLine} (Evaluate whether this scene is still valid based on how much time has passed since it was last updated.)`);
|
|
191
208
|
}
|
|
192
209
|
}
|
|
193
210
|
if (state.active_event) {
|
|
@@ -304,10 +321,10 @@ ${isProactive
|
|
|
304
321
|
return `"imageParams": null`;
|
|
305
322
|
return `"imageParams": {
|
|
306
323
|
"mode": "structured | full-prompt (use 'full-prompt' for highly dynamic actions)",
|
|
307
|
-
"full_prompt": "Use only if mode is full-prompt. Highly detailed visual description in ENGLISH. CRITICAL:
|
|
324
|
+
"full_prompt": "Use only if mode is full-prompt. Highly detailed visual description in ENGLISH. CRITICAL RULE FOR PERSPECTIVE: If you are physically separated from the user, simulate a selfie. However, absolutely DO NOT use the words 'selfie', 'phone', 'camera', 'lens', or 'holding' in this prompt (unless taking a mirror selfie). NEVER try to use negative prompting like 'no phone visible', as simply writing the word 'phone' forces image models to mistakenly draw a phone or phone border! Instead, achieve the natural selfie look using pure composition descriptions (e.g., 'intimate portrait looking directly at the viewer', 'high-angle portrait leaning forward', or 'wide portrait with one arm reaching out of the frame'). Vary the framing distance and angle to match the mood. If you are physically together with the user, the image MUST be a strict first-person perspective exclusively from the USER's eyes (start with 'POV: '). NEVER mix perspectives together. DO NOT describe the user (e.g., 'a man', 'the driver') as visible in the scene because the view IS the user. Describe ONLY the character looking back and their immediate surroundings. MUST align precisely with the character's current Wardrobe and exposure state. Explicitly describe the character's exact clothing (or specify naked/half-naked if applicable). Ensure basic appearance (makeup, body shape, hair, facial features, etc.) aligns exactly with the character's foundational appearance profile.",
|
|
308
325
|
"expression": "seductive | cute | happy | sleepy | dazed | pleased | default (Strictly choose ONE from this exact list. DO NOT invent new words like 'shy'.)",
|
|
309
326
|
"condition": "normal | sweaty | wet | messy | oily (Strictly choose ONE from this exact list.)",
|
|
310
|
-
"view_angle": "front | side | high_angle | from_below | boyfriend_view | selfie | mirror (Strictly choose ONE from this exact list.)",
|
|
327
|
+
"view_angle": "front | side | high_angle | from_below | boyfriend_view | selfie | mirror (Strictly choose ONE from this exact list. Use 'selfie' if physically separated from the user, otherwise use POV angles like 'boyfriend_view' or 'front' if together.)",
|
|
311
328
|
"exposure": "normal | cleavage | see_through | half_naked | naked | intimate (Strictly choose ONE from this exact list. Explicitly choose naked or half_naked if the active scene takes off outfit.)",
|
|
312
329
|
"pose": "e.g., sitting on bed, leaning forward (ENGLISH ONLY)",
|
|
313
330
|
"scene": "e.g., cozy bedroom, morning light (ENGLISH ONLY)",
|
|
@@ -424,7 +441,12 @@ ${isProactive
|
|
|
424
441
|
modalitiesInstruction += `\n - ALWAYS set 'imageParams' to null. If the user explicitly asks for a picture, FIRMLY decline naturally in your 'textResponse' (e.g., say you absolutely cannot right now). NEVER pretend to send one, and NEVER give in no matter how many times they ask.`;
|
|
425
442
|
}
|
|
426
443
|
if (requestedOthers.includes(InteractRequestType.VOICE)) {
|
|
427
|
-
modalitiesInstruction += `\n -
|
|
444
|
+
modalitiesInstruction += `\n - 'voiceArgs' should be used sparingly to act like a real human. Include it ONLY IF AT LEAST ONE of the following is true:
|
|
445
|
+
1. The response is a long text that would be tedious to type out in real life.
|
|
446
|
+
2. The user explicitly requests a voice message.
|
|
447
|
+
3. Your current scheduled event or action makes texting inconvenient (e.g., driving, cooking, showering).
|
|
448
|
+
4. You are experiencing complicated moods or emotions that are difficult to convey accurately via pure text.
|
|
449
|
+
Otherwise, ALWAYS set 'voiceArgs' to null.`;
|
|
428
450
|
}
|
|
429
451
|
else {
|
|
430
452
|
modalitiesInstruction += `\n - ALWAYS set 'voiceArgs' to null.`;
|
|
@@ -529,7 +551,13 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
529
551
|
: params.userMessage;
|
|
530
552
|
// Fire text ready callback if provided
|
|
531
553
|
if (params.onTextReady && (resolvedTextResponse || parsedIntent.actionText)) {
|
|
532
|
-
params.onTextReady(resolvedTextResponse, parsedIntent.actionText
|
|
554
|
+
params.onTextReady(resolvedTextResponse, parsedIntent.actionText, {
|
|
555
|
+
stateUpdate: parsedIntent.stateUpdate,
|
|
556
|
+
userAnalysis: parsedIntent.userAnalysis,
|
|
557
|
+
isEndTurn: parsedIntent.isEndTurn,
|
|
558
|
+
triggerEvent: parsedIntent.triggerEvent,
|
|
559
|
+
likePreviousPicture: parsedIntent.likePreviousPicture,
|
|
560
|
+
});
|
|
533
561
|
}
|
|
534
562
|
// 5. Build Final Media Calls parallel
|
|
535
563
|
const mediaTasks = [];
|
|
@@ -818,6 +846,20 @@ You MUST output ONLY a valid JSON object matching exactly this structure:
|
|
|
818
846
|
if (parsedIntent.stateUpdate) {
|
|
819
847
|
this._updateDynamicContextInternal(parsedIntent.stateUpdate).catch(e => console.error(e));
|
|
820
848
|
}
|
|
849
|
+
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
850
|
+
parsedIntent.textResponse.trim().length > 0
|
|
851
|
+
? parsedIntent.textResponse
|
|
852
|
+
: "...";
|
|
853
|
+
// Fire text ready callback if provided
|
|
854
|
+
if (params.onTextReady && (resolvedTextResponse || parsedIntent.actionText)) {
|
|
855
|
+
params.onTextReady(resolvedTextResponse, parsedIntent.actionText, {
|
|
856
|
+
stateUpdate: parsedIntent.stateUpdate,
|
|
857
|
+
userAnalysis: parsedIntent.userAnalysis,
|
|
858
|
+
isEndTurn: parsedIntent.isEndTurn,
|
|
859
|
+
triggerEvent: parsedIntent.triggerEvent,
|
|
860
|
+
likePreviousPicture: parsedIntent.likePreviousPicture,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
821
863
|
// Handle Optional Media (Image only for proactive to save compute normally, but you can extend)
|
|
822
864
|
let finalImageUrl = undefined;
|
|
823
865
|
if (requestedOthers.includes(InteractRequestType.IMAGE) || !!parsedIntent.imageParams) {
|
package/dist/types.d.ts
CHANGED
|
@@ -24,11 +24,19 @@ export interface HistoryEntry {
|
|
|
24
24
|
mediaHint?: string;
|
|
25
25
|
isProactive?: boolean;
|
|
26
26
|
}
|
|
27
|
+
export interface InteractMetadata {
|
|
28
|
+
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
29
|
+
userAnalysis?: DispatcherIntent["userAnalysis"];
|
|
30
|
+
isEndTurn?: boolean;
|
|
31
|
+
triggerEvent?: DispatcherIntent["triggerEvent"];
|
|
32
|
+
likePreviousPicture?: boolean;
|
|
33
|
+
}
|
|
27
34
|
export interface ProactiveParams {
|
|
28
35
|
history?: HistoryEntry[];
|
|
29
36
|
maxUnreplied?: number;
|
|
30
37
|
requestTypes?: InteractRequestType[];
|
|
31
38
|
localContext?: string;
|
|
39
|
+
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
32
40
|
}
|
|
33
41
|
export interface ProactiveResponse {
|
|
34
42
|
status: "success" | "skipped" | "error";
|
|
@@ -45,7 +53,7 @@ export interface InteractParams {
|
|
|
45
53
|
localContext?: string;
|
|
46
54
|
requestTypes?: InteractRequestType[];
|
|
47
55
|
history?: HistoryEntry[];
|
|
48
|
-
onTextReady?: (textResponse: string, actionText?: string) => void;
|
|
56
|
+
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
49
57
|
}
|
|
50
58
|
export interface OndemandEventParams {
|
|
51
59
|
eventDescription: string;
|