@space3-npm/cybersoul-client 1.4.22 → 1.4.24
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 +13 -1
- package/dist/client.js +85 -3
- package/dist/types.d.ts +42 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -83,6 +83,15 @@ export declare class CyberSoulClient {
|
|
|
83
83
|
* reply is already in flight.
|
|
84
84
|
*/
|
|
85
85
|
private buildMediaError;
|
|
86
|
+
/**
|
|
87
|
+
* Shared giftOutfit handler for `interact()` / `proactiveInteract()`.
|
|
88
|
+
* Validates the LLM's `giftOutfit` intent, performs the wardrobe write,
|
|
89
|
+
* fires the `onOutfitGifted` callback, and resolves to the
|
|
90
|
+
* [OutfitGiftedPayload] (or `undefined` when there was nothing to gift
|
|
91
|
+
* or the write failed). Failures are swallowed (logged) so a wardrobe
|
|
92
|
+
* hiccup never aborts the chat turn.
|
|
93
|
+
*/
|
|
94
|
+
private processGiftOutfit;
|
|
86
95
|
private formatHistoryEntries;
|
|
87
96
|
private buildHistoryTranscript;
|
|
88
97
|
interact(params: InteractParams): Promise<InteractResponse>;
|
|
@@ -144,8 +153,11 @@ export declare class CyberSoulClient {
|
|
|
144
153
|
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<PersistedDynamicContext | null>;
|
|
145
154
|
/**
|
|
146
155
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
156
|
+
* Returns the number of wardrobe items the backend created (the
|
|
157
|
+
* backend may expand a single description into multiple items), or
|
|
158
|
+
* `undefined` when the server did not report a count.
|
|
147
159
|
*/
|
|
148
|
-
giftOutfit(descriptionText: string): Promise<
|
|
160
|
+
giftOutfit(descriptionText: string): Promise<number | undefined>;
|
|
149
161
|
/**
|
|
150
162
|
* Bootstrap character profile from OpenClaw workspace files.
|
|
151
163
|
*/
|
package/dist/client.js
CHANGED
|
@@ -311,7 +311,18 @@ Interaction Boundaries: ${state.interaction_boundaries || "None"}`);
|
|
|
311
311
|
// [2] SITUATIONAL CONTEXT
|
|
312
312
|
const currentTimeMs = state.current_time ? new Date(state.current_time).getTime() : Date.now();
|
|
313
313
|
const timePeriod = this.getTimePeriodInfo(currentTimeMs);
|
|
314
|
-
const
|
|
314
|
+
const currentDate = new Date(currentTimeMs);
|
|
315
|
+
const timeStr = currentDate.toLocaleString("en-US", {
|
|
316
|
+
timeZone: "Asia/Shanghai",
|
|
317
|
+
weekday: "long",
|
|
318
|
+
year: "numeric",
|
|
319
|
+
month: "long",
|
|
320
|
+
day: "numeric",
|
|
321
|
+
hour: "2-digit",
|
|
322
|
+
minute: "2-digit",
|
|
323
|
+
second: "2-digit",
|
|
324
|
+
hour12: false,
|
|
325
|
+
});
|
|
315
326
|
contextParts.push(`\n[SITUATIONAL CONTEXT]
|
|
316
327
|
Current time: ${timeStr} (${timePeriod.period})`);
|
|
317
328
|
if (dyn.lastInteractionAt) {
|
|
@@ -471,7 +482,10 @@ ${isProactive
|
|
|
471
482
|
return `- Include 'triggerEvent' only if the VERY LAST USER MESSAGE proposes a new activity/hangout AND you accept the invitation, explicitly requests an outfit change AND you agree, or proposes intimate/romantic actions AND you agree; ignore older history. DO NOT include it if you decline or reject the proposal. ${this.getOutfitSelectionPrompt()}`;
|
|
472
483
|
}
|
|
473
484
|
getOutfitAcquisitionPolicyPrompt() {
|
|
474
|
-
return `- Outfit acquisition (
|
|
485
|
+
return `- Outfit acquisition (giftOutfit): set 'giftOutfit' to { "descriptionText": "short outfit description" } when a genuinely NEW outfit (one that is NOT already in the Available Wardrobe) is obtained THIS turn, triggered by EITHER:
|
|
486
|
+
(a) USER-GIFTED: the VERY LAST USER MESSAGE expresses gift/buy/add-clothes intent for you (e.g. "I bought you a dress", "here, wear this new outfit", "adding some lingerie to your closet").
|
|
487
|
+
(b) CHARACTER-ACQUIRED: the conversation or active event naturally leads YOU to acquire a new outfit you don't already own (e.g. you went shopping, received/made clothes, or the scene requires changing into a brand-new outfit that is absent from your Available Wardrobe).
|
|
488
|
+
Keep 'descriptionText' to a concise English-or-matching-language description of the single new outfit. Otherwise set 'giftOutfit' to null. Do NOT fire it for outfits already present in the Available Wardrobe, and do NOT fire it just because you changed into an existing outfit.`;
|
|
475
489
|
}
|
|
476
490
|
getEventSchemaParams(userName) {
|
|
477
491
|
const name = userName || "the user";
|
|
@@ -596,6 +610,45 @@ ${isProactive
|
|
|
596
610
|
affected,
|
|
597
611
|
};
|
|
598
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Shared giftOutfit handler for `interact()` / `proactiveInteract()`.
|
|
615
|
+
* Validates the LLM's `giftOutfit` intent, performs the wardrobe write,
|
|
616
|
+
* fires the `onOutfitGifted` callback, and resolves to the
|
|
617
|
+
* [OutfitGiftedPayload] (or `undefined` when there was nothing to gift
|
|
618
|
+
* or the write failed). Failures are swallowed (logged) so a wardrobe
|
|
619
|
+
* hiccup never aborts the chat turn.
|
|
620
|
+
*/
|
|
621
|
+
async processGiftOutfit(giftOutfitIntent, onOutfitGifted) {
|
|
622
|
+
if (!giftOutfitIntent ||
|
|
623
|
+
typeof giftOutfitIntent !== "object" ||
|
|
624
|
+
typeof giftOutfitIntent.descriptionText !== "string" ||
|
|
625
|
+
giftOutfitIntent.descriptionText.trim().length === 0) {
|
|
626
|
+
return undefined;
|
|
627
|
+
}
|
|
628
|
+
const outfitDescription = giftOutfitIntent.descriptionText.trim();
|
|
629
|
+
try {
|
|
630
|
+
const count = await this.giftOutfit(outfitDescription);
|
|
631
|
+
const giftedOutfit = {
|
|
632
|
+
descriptionText: outfitDescription,
|
|
633
|
+
};
|
|
634
|
+
if (typeof count === "number") {
|
|
635
|
+
giftedOutfit.count = count;
|
|
636
|
+
}
|
|
637
|
+
if (onOutfitGifted) {
|
|
638
|
+
try {
|
|
639
|
+
onOutfitGifted(giftedOutfit);
|
|
640
|
+
}
|
|
641
|
+
catch (cbErr) {
|
|
642
|
+
console.warn("[CyberSoulClient] onOutfitGifted callback threw:", cbErr);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return giftedOutfit;
|
|
646
|
+
}
|
|
647
|
+
catch (e) {
|
|
648
|
+
console.error("[CyberSoulClient] giftOutfit failed:", e);
|
|
649
|
+
return undefined;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
599
652
|
formatHistoryEntries(history, userName, agentName, promptDirective = "") {
|
|
600
653
|
const contextLines = [];
|
|
601
654
|
for (let i = 0; i < history.length; i++) {
|
|
@@ -835,6 +888,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
835
888
|
let finalAudioUrl = undefined;
|
|
836
889
|
let finalAudioMediaId = undefined;
|
|
837
890
|
let finalDurationSec = undefined;
|
|
891
|
+
let giftedOutfit = undefined;
|
|
838
892
|
// Partial-failure capture: text was already produced and emitted
|
|
839
893
|
// via [onTextReady], so a wallet / insufficient-points failure on
|
|
840
894
|
// image or voice MUST NOT abort the whole turn. We collect the
|
|
@@ -875,7 +929,10 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
875
929
|
typeof parsedIntent.giftOutfit === "object" &&
|
|
876
930
|
typeof parsedIntent.giftOutfit.descriptionText === "string" &&
|
|
877
931
|
parsedIntent.giftOutfit.descriptionText.trim().length > 0) {
|
|
878
|
-
mediaTasks.push(this.
|
|
932
|
+
mediaTasks.push(this.processGiftOutfit(parsedIntent.giftOutfit, params.onOutfitGifted).then((result) => {
|
|
933
|
+
if (result)
|
|
934
|
+
giftedOutfit = result;
|
|
935
|
+
}));
|
|
879
936
|
}
|
|
880
937
|
const shouldGenerateImage = types.includes(InteractRequestType.IMAGE) &&
|
|
881
938
|
(!isAuto || !!parsedIntent.imageParams);
|
|
@@ -978,6 +1035,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
978
1035
|
isEndTurn: parsedIntent.isEndTurn,
|
|
979
1036
|
persistedDynamicContext,
|
|
980
1037
|
mediaError,
|
|
1038
|
+
giftedOutfit,
|
|
981
1039
|
};
|
|
982
1040
|
}
|
|
983
1041
|
catch (error) {
|
|
@@ -1179,6 +1237,7 @@ Modalities:
|
|
|
1179
1237
|
? "'imageParams' may be included only if sending a photo right now would feel natural for this character in this relationship — otherwise set null. Do not attach a photo just because you can."
|
|
1180
1238
|
: "ALWAYS set 'imageParams' to null."}
|
|
1181
1239
|
- ALWAYS set 'voiceArgs' to null.
|
|
1240
|
+
${this.getOutfitAcquisitionPolicyPrompt()}
|
|
1182
1241
|
|
|
1183
1242
|
Output ONLY a valid JSON object matching exactly this structure (no markdown wrappers).
|
|
1184
1243
|
If "shouldSkipProactive" is true, set "skipReason" to one short sentence and set every other field to null.
|
|
@@ -1189,6 +1248,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1189
1248
|
"actionText": "(Scene descriptions, physical actions, expressions, inner feelings) ONLY.",
|
|
1190
1249
|
"textResponse": "Spoken dialogue ONLY.",
|
|
1191
1250
|
"stateUpdate": { "temperatureDelta": 0, "ongoingScene": { "scene": "...", "outfit": "..." } },
|
|
1251
|
+
"giftOutfit": { "descriptionText": "Concise description of the newly acquired outfit to add into wardrobe." },
|
|
1192
1252
|
${this.getImageSchemaParams(imageAllowed)},
|
|
1193
1253
|
"voiceArgs": null
|
|
1194
1254
|
}`;
|
|
@@ -1254,6 +1314,11 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1254
1314
|
stateUpdate: parsedIntent.stateUpdate,
|
|
1255
1315
|
});
|
|
1256
1316
|
}
|
|
1317
|
+
// Outfit acquisition: the character may decide, on its own, to pick
|
|
1318
|
+
// up a brand-new outfit while reaching out. Fire-and-capture in
|
|
1319
|
+
// parallel with image generation; surface it via callback + the
|
|
1320
|
+
// final response so upstream can show "New outfit added".
|
|
1321
|
+
const giftOutfitPromise = this.processGiftOutfit(parsedIntent.giftOutfit, params.onOutfitGifted);
|
|
1257
1322
|
let finalImageUrl;
|
|
1258
1323
|
let finalImageMediaId;
|
|
1259
1324
|
let proactiveMediaError = null;
|
|
@@ -1289,6 +1354,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1289
1354
|
}
|
|
1290
1355
|
}
|
|
1291
1356
|
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
1357
|
+
const giftedOutfit = (await giftOutfitPromise) ?? undefined;
|
|
1292
1358
|
const proactiveMediaErrorEnv = proactiveMediaError
|
|
1293
1359
|
? this.buildMediaError(proactiveMediaError, proactiveAffected)
|
|
1294
1360
|
: undefined;
|
|
@@ -1301,6 +1367,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1301
1367
|
stateUpdate: parsedIntent.stateUpdate,
|
|
1302
1368
|
persistedDynamicContext,
|
|
1303
1369
|
mediaError: proactiveMediaErrorEnv,
|
|
1370
|
+
giftedOutfit,
|
|
1304
1371
|
};
|
|
1305
1372
|
}
|
|
1306
1373
|
catch (error) {
|
|
@@ -1424,6 +1491,9 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1424
1491
|
}
|
|
1425
1492
|
/**
|
|
1426
1493
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
1494
|
+
* Returns the number of wardrobe items the backend created (the
|
|
1495
|
+
* backend may expand a single description into multiple items), or
|
|
1496
|
+
* `undefined` when the server did not report a count.
|
|
1427
1497
|
*/
|
|
1428
1498
|
async giftOutfit(descriptionText) {
|
|
1429
1499
|
const res = await this.apiFetch("/api/v1/cyber-soul/characters/gift-outfit", {
|
|
@@ -1432,6 +1502,18 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1432
1502
|
});
|
|
1433
1503
|
if (!res.ok)
|
|
1434
1504
|
throw new Error("Failed to gift outfit");
|
|
1505
|
+
try {
|
|
1506
|
+
const body = (await res.json());
|
|
1507
|
+
return typeof body.count === "number" && Number.isFinite(body.count)
|
|
1508
|
+
? body.count
|
|
1509
|
+
: undefined;
|
|
1510
|
+
}
|
|
1511
|
+
catch {
|
|
1512
|
+
// The gift already succeeded server-side (res.ok); a missing/
|
|
1513
|
+
// unparseable count is non-fatal — report "unknown" rather than
|
|
1514
|
+
// fabricating a number.
|
|
1515
|
+
return undefined;
|
|
1516
|
+
}
|
|
1435
1517
|
}
|
|
1436
1518
|
/**
|
|
1437
1519
|
* Bootstrap character profile from OpenClaw workspace files.
|
package/dist/types.d.ts
CHANGED
|
@@ -80,6 +80,26 @@ export interface MediaReadyPayload {
|
|
|
80
80
|
/** Voice only — TTS duration in seconds when known. */
|
|
81
81
|
durationSec?: number;
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Payload delivered by [InteractParams.onOutfitGifted] /
|
|
85
|
+
* [ProactiveParams.onOutfitGifted] when a new outfit is successfully
|
|
86
|
+
* added to the character's wardrobe during a turn. Fires for BOTH
|
|
87
|
+
* trigger paths: (a) the user explicitly gifts/buys an outfit, and
|
|
88
|
+
* (b) the conversation or an active event leads the character to
|
|
89
|
+
* acquire a brand-new outfit. Lets upstream consumers (e.g.
|
|
90
|
+
* cybersoul-chat) render a system message like
|
|
91
|
+
* "New outfit added to wardrobe".
|
|
92
|
+
*/
|
|
93
|
+
export interface OutfitGiftedPayload {
|
|
94
|
+
/** Human-readable description of the newly acquired outfit. */
|
|
95
|
+
descriptionText: string;
|
|
96
|
+
/**
|
|
97
|
+
* Number of wardrobe items the backend created for this gift, when
|
|
98
|
+
* the server reported it. Omitted when the count is unknown — never
|
|
99
|
+
* fabricated.
|
|
100
|
+
*/
|
|
101
|
+
count?: number;
|
|
102
|
+
}
|
|
83
103
|
export interface ProactiveParams {
|
|
84
104
|
history?: HistoryEntry[];
|
|
85
105
|
maxUnreplied?: number;
|
|
@@ -95,6 +115,13 @@ export interface ProactiveParams {
|
|
|
95
115
|
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
96
116
|
/** Fires per modality as each media task settles successfully. */
|
|
97
117
|
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
118
|
+
/**
|
|
119
|
+
* Fires when an outfit has been successfully added to the wardrobe
|
|
120
|
+
* during this turn (user-initiated gift OR character-initiated
|
|
121
|
+
* acquisition). Lets the UI render a system message like
|
|
122
|
+
* "New outfit added to wardrobe" in real time.
|
|
123
|
+
*/
|
|
124
|
+
onOutfitGifted?: (payload: OutfitGiftedPayload) => void;
|
|
98
125
|
}
|
|
99
126
|
export interface ProactiveResponse {
|
|
100
127
|
status: "success" | "skipped" | "error";
|
|
@@ -113,6 +140,10 @@ export interface ProactiveResponse {
|
|
|
113
140
|
* can still render the text reply and explain the missing media
|
|
114
141
|
* without losing the conversation. See [InteractMediaError]. */
|
|
115
142
|
mediaError?: InteractMediaError;
|
|
143
|
+
/** Set when an outfit was successfully added to the wardrobe this turn.
|
|
144
|
+
* Mirrors the [ProactiveParams.onOutfitGifted] callback for consumers
|
|
145
|
+
* that only read the final response. See [OutfitGiftedPayload]. */
|
|
146
|
+
giftedOutfit?: OutfitGiftedPayload;
|
|
116
147
|
error?: string;
|
|
117
148
|
}
|
|
118
149
|
export interface InteractParams {
|
|
@@ -132,6 +163,13 @@ export interface InteractParams {
|
|
|
132
163
|
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
133
164
|
/** Fires per modality as each media task settles successfully. */
|
|
134
165
|
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
166
|
+
/**
|
|
167
|
+
* Fires when an outfit has been successfully added to the wardrobe
|
|
168
|
+
* during this turn (user-initiated gift OR character-initiated
|
|
169
|
+
* acquisition). Lets the UI render a system message like
|
|
170
|
+
* "New outfit added to wardrobe" in real time.
|
|
171
|
+
*/
|
|
172
|
+
onOutfitGifted?: (payload: OutfitGiftedPayload) => void;
|
|
135
173
|
}
|
|
136
174
|
export interface OndemandEventParams {
|
|
137
175
|
eventDescription: string;
|
|
@@ -173,6 +211,10 @@ export interface InteractResponse {
|
|
|
173
211
|
imageMediaId?: string;
|
|
174
212
|
audioUrl?: string;
|
|
175
213
|
audioMediaId?: string;
|
|
214
|
+
/** Set when an outfit was successfully added to the wardrobe this turn.
|
|
215
|
+
* Mirrors the [InteractParams.onOutfitGifted] callback for consumers
|
|
216
|
+
* that only read the final response. See [OutfitGiftedPayload]. */
|
|
217
|
+
giftedOutfit?: OutfitGiftedPayload;
|
|
176
218
|
likePreviousPicture?: boolean;
|
|
177
219
|
durationSec?: number;
|
|
178
220
|
triggeredEvent?: {
|