@space3-npm/cybersoul-client 1.4.23 → 1.4.25
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 +77 -4
- package/dist/types.d.ts +50 -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
|
@@ -479,10 +479,14 @@ ${isProactive
|
|
|
479
479
|
return `When generating a triggerEvent, you MUST provide a suitable 'triggerEvent.outfitId' if the VERY LAST USER MESSAGE explicitly asks for an outfit change, OR if the new activity implies a context/location shift that conflicts with the current outfit (e.g., currently in SLEEPWEAR at home but going outside). Otherwise, keep it null. When changing outfits, match it to the event's activity, environment, and relationship stage (e.g., CASUAL, COSTUME, INTIMATE, SLEEPWEAR, etc.).`;
|
|
480
480
|
}
|
|
481
481
|
getTriggerEventPolicyPrompt() {
|
|
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.
|
|
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.
|
|
483
|
+
REPETITION GATE (hard): Prior assistant turns that already auto-triggered an event are tagged with a [Triggered Event: ...] marker in '[CHAT HISTORY]'. If such a marker already exists for the SAME activity the VERY LAST USER MESSAGE is referring to (e.g. it is just acknowledging, hurrying, confirming, or continuing an already-accepted outing), set 'triggerEvent' to null. Only emit a NEW 'triggerEvent' when the user proposes a genuinely DIFFERENT activity that has not already been triggered. Do NOT re-trigger the same event just because the conversation continues. ${this.getOutfitSelectionPrompt()}`;
|
|
483
484
|
}
|
|
484
485
|
getOutfitAcquisitionPolicyPrompt() {
|
|
485
|
-
return `- Outfit acquisition (
|
|
486
|
+
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:
|
|
487
|
+
(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").
|
|
488
|
+
(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).
|
|
489
|
+
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.`;
|
|
486
490
|
}
|
|
487
491
|
getEventSchemaParams(userName) {
|
|
488
492
|
const name = userName || "the user";
|
|
@@ -607,6 +611,45 @@ ${isProactive
|
|
|
607
611
|
affected,
|
|
608
612
|
};
|
|
609
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Shared giftOutfit handler for `interact()` / `proactiveInteract()`.
|
|
616
|
+
* Validates the LLM's `giftOutfit` intent, performs the wardrobe write,
|
|
617
|
+
* fires the `onOutfitGifted` callback, and resolves to the
|
|
618
|
+
* [OutfitGiftedPayload] (or `undefined` when there was nothing to gift
|
|
619
|
+
* or the write failed). Failures are swallowed (logged) so a wardrobe
|
|
620
|
+
* hiccup never aborts the chat turn.
|
|
621
|
+
*/
|
|
622
|
+
async processGiftOutfit(giftOutfitIntent, onOutfitGifted) {
|
|
623
|
+
if (!giftOutfitIntent ||
|
|
624
|
+
typeof giftOutfitIntent !== "object" ||
|
|
625
|
+
typeof giftOutfitIntent.descriptionText !== "string" ||
|
|
626
|
+
giftOutfitIntent.descriptionText.trim().length === 0) {
|
|
627
|
+
return undefined;
|
|
628
|
+
}
|
|
629
|
+
const outfitDescription = giftOutfitIntent.descriptionText.trim();
|
|
630
|
+
try {
|
|
631
|
+
const count = await this.giftOutfit(outfitDescription);
|
|
632
|
+
const giftedOutfit = {
|
|
633
|
+
descriptionText: outfitDescription,
|
|
634
|
+
};
|
|
635
|
+
if (typeof count === "number") {
|
|
636
|
+
giftedOutfit.count = count;
|
|
637
|
+
}
|
|
638
|
+
if (onOutfitGifted) {
|
|
639
|
+
try {
|
|
640
|
+
onOutfitGifted(giftedOutfit);
|
|
641
|
+
}
|
|
642
|
+
catch (cbErr) {
|
|
643
|
+
console.warn("[CyberSoulClient] onOutfitGifted callback threw:", cbErr);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return giftedOutfit;
|
|
647
|
+
}
|
|
648
|
+
catch (e) {
|
|
649
|
+
console.error("[CyberSoulClient] giftOutfit failed:", e);
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
610
653
|
formatHistoryEntries(history, userName, agentName, promptDirective = "") {
|
|
611
654
|
const contextLines = [];
|
|
612
655
|
for (let i = 0; i < history.length; i++) {
|
|
@@ -623,7 +666,8 @@ ${isProactive
|
|
|
623
666
|
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
|
|
624
667
|
const action = msg.actionText ? ` (${msg.actionText})` : "";
|
|
625
668
|
const media = msg.mediaHint ? ` [${msg.mediaHint}]` : "";
|
|
626
|
-
|
|
669
|
+
const event = msg.eventHint ? ` [Triggered Event: ${msg.eventHint}]` : "";
|
|
670
|
+
contextLines.push(`${speaker}:${action} ${content}${media}${event}`);
|
|
627
671
|
}
|
|
628
672
|
return contextLines.join('\n');
|
|
629
673
|
}
|
|
@@ -846,6 +890,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
846
890
|
let finalAudioUrl = undefined;
|
|
847
891
|
let finalAudioMediaId = undefined;
|
|
848
892
|
let finalDurationSec = undefined;
|
|
893
|
+
let giftedOutfit = undefined;
|
|
849
894
|
// Partial-failure capture: text was already produced and emitted
|
|
850
895
|
// via [onTextReady], so a wallet / insufficient-points failure on
|
|
851
896
|
// image or voice MUST NOT abort the whole turn. We collect the
|
|
@@ -886,7 +931,10 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
886
931
|
typeof parsedIntent.giftOutfit === "object" &&
|
|
887
932
|
typeof parsedIntent.giftOutfit.descriptionText === "string" &&
|
|
888
933
|
parsedIntent.giftOutfit.descriptionText.trim().length > 0) {
|
|
889
|
-
mediaTasks.push(this.
|
|
934
|
+
mediaTasks.push(this.processGiftOutfit(parsedIntent.giftOutfit, params.onOutfitGifted).then((result) => {
|
|
935
|
+
if (result)
|
|
936
|
+
giftedOutfit = result;
|
|
937
|
+
}));
|
|
890
938
|
}
|
|
891
939
|
const shouldGenerateImage = types.includes(InteractRequestType.IMAGE) &&
|
|
892
940
|
(!isAuto || !!parsedIntent.imageParams);
|
|
@@ -989,6 +1037,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
989
1037
|
isEndTurn: parsedIntent.isEndTurn,
|
|
990
1038
|
persistedDynamicContext,
|
|
991
1039
|
mediaError,
|
|
1040
|
+
giftedOutfit,
|
|
992
1041
|
};
|
|
993
1042
|
}
|
|
994
1043
|
catch (error) {
|
|
@@ -1190,6 +1239,7 @@ Modalities:
|
|
|
1190
1239
|
? "'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."
|
|
1191
1240
|
: "ALWAYS set 'imageParams' to null."}
|
|
1192
1241
|
- ALWAYS set 'voiceArgs' to null.
|
|
1242
|
+
${this.getOutfitAcquisitionPolicyPrompt()}
|
|
1193
1243
|
|
|
1194
1244
|
Output ONLY a valid JSON object matching exactly this structure (no markdown wrappers).
|
|
1195
1245
|
If "shouldSkipProactive" is true, set "skipReason" to one short sentence and set every other field to null.
|
|
@@ -1200,6 +1250,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1200
1250
|
"actionText": "(Scene descriptions, physical actions, expressions, inner feelings) ONLY.",
|
|
1201
1251
|
"textResponse": "Spoken dialogue ONLY.",
|
|
1202
1252
|
"stateUpdate": { "temperatureDelta": 0, "ongoingScene": { "scene": "...", "outfit": "..." } },
|
|
1253
|
+
"giftOutfit": { "descriptionText": "Concise description of the newly acquired outfit to add into wardrobe." },
|
|
1203
1254
|
${this.getImageSchemaParams(imageAllowed)},
|
|
1204
1255
|
"voiceArgs": null
|
|
1205
1256
|
}`;
|
|
@@ -1265,6 +1316,11 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1265
1316
|
stateUpdate: parsedIntent.stateUpdate,
|
|
1266
1317
|
});
|
|
1267
1318
|
}
|
|
1319
|
+
// Outfit acquisition: the character may decide, on its own, to pick
|
|
1320
|
+
// up a brand-new outfit while reaching out. Fire-and-capture in
|
|
1321
|
+
// parallel with image generation; surface it via callback + the
|
|
1322
|
+
// final response so upstream can show "New outfit added".
|
|
1323
|
+
const giftOutfitPromise = this.processGiftOutfit(parsedIntent.giftOutfit, params.onOutfitGifted);
|
|
1268
1324
|
let finalImageUrl;
|
|
1269
1325
|
let finalImageMediaId;
|
|
1270
1326
|
let proactiveMediaError = null;
|
|
@@ -1300,6 +1356,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1300
1356
|
}
|
|
1301
1357
|
}
|
|
1302
1358
|
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
1359
|
+
const giftedOutfit = (await giftOutfitPromise) ?? undefined;
|
|
1303
1360
|
const proactiveMediaErrorEnv = proactiveMediaError
|
|
1304
1361
|
? this.buildMediaError(proactiveMediaError, proactiveAffected)
|
|
1305
1362
|
: undefined;
|
|
@@ -1312,6 +1369,7 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1312
1369
|
stateUpdate: parsedIntent.stateUpdate,
|
|
1313
1370
|
persistedDynamicContext,
|
|
1314
1371
|
mediaError: proactiveMediaErrorEnv,
|
|
1372
|
+
giftedOutfit,
|
|
1315
1373
|
};
|
|
1316
1374
|
}
|
|
1317
1375
|
catch (error) {
|
|
@@ -1435,6 +1493,9 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1435
1493
|
}
|
|
1436
1494
|
/**
|
|
1437
1495
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
1496
|
+
* Returns the number of wardrobe items the backend created (the
|
|
1497
|
+
* backend may expand a single description into multiple items), or
|
|
1498
|
+
* `undefined` when the server did not report a count.
|
|
1438
1499
|
*/
|
|
1439
1500
|
async giftOutfit(descriptionText) {
|
|
1440
1501
|
const res = await this.apiFetch("/api/v1/cyber-soul/characters/gift-outfit", {
|
|
@@ -1443,6 +1504,18 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1443
1504
|
});
|
|
1444
1505
|
if (!res.ok)
|
|
1445
1506
|
throw new Error("Failed to gift outfit");
|
|
1507
|
+
try {
|
|
1508
|
+
const body = (await res.json());
|
|
1509
|
+
return typeof body.count === "number" && Number.isFinite(body.count)
|
|
1510
|
+
? body.count
|
|
1511
|
+
: undefined;
|
|
1512
|
+
}
|
|
1513
|
+
catch {
|
|
1514
|
+
// The gift already succeeded server-side (res.ok); a missing/
|
|
1515
|
+
// unparseable count is non-fatal — report "unknown" rather than
|
|
1516
|
+
// fabricating a number.
|
|
1517
|
+
return undefined;
|
|
1518
|
+
}
|
|
1446
1519
|
}
|
|
1447
1520
|
/**
|
|
1448
1521
|
* Bootstrap character profile from OpenClaw workspace files.
|
package/dist/types.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ export interface HistoryEntry {
|
|
|
31
31
|
content: string;
|
|
32
32
|
actionText?: string;
|
|
33
33
|
mediaHint?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Marker for assistant turns that already auto-triggered an event
|
|
36
|
+
* (e.g. an outing/hangout the character accepted). Surfaced in the
|
|
37
|
+
* transcript as a `[Triggered Event: ...]` tag so the dispatcher's
|
|
38
|
+
* trigger-event repetition gate can avoid re-triggering the same
|
|
39
|
+
* activity on later turns. Mirrors how `mediaHint` tags past media.
|
|
40
|
+
*/
|
|
41
|
+
eventHint?: string;
|
|
34
42
|
isProactive?: boolean;
|
|
35
43
|
timestamp?: string | number | Date;
|
|
36
44
|
}
|
|
@@ -80,6 +88,26 @@ export interface MediaReadyPayload {
|
|
|
80
88
|
/** Voice only — TTS duration in seconds when known. */
|
|
81
89
|
durationSec?: number;
|
|
82
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Payload delivered by [InteractParams.onOutfitGifted] /
|
|
93
|
+
* [ProactiveParams.onOutfitGifted] when a new outfit is successfully
|
|
94
|
+
* added to the character's wardrobe during a turn. Fires for BOTH
|
|
95
|
+
* trigger paths: (a) the user explicitly gifts/buys an outfit, and
|
|
96
|
+
* (b) the conversation or an active event leads the character to
|
|
97
|
+
* acquire a brand-new outfit. Lets upstream consumers (e.g.
|
|
98
|
+
* cybersoul-chat) render a system message like
|
|
99
|
+
* "New outfit added to wardrobe".
|
|
100
|
+
*/
|
|
101
|
+
export interface OutfitGiftedPayload {
|
|
102
|
+
/** Human-readable description of the newly acquired outfit. */
|
|
103
|
+
descriptionText: string;
|
|
104
|
+
/**
|
|
105
|
+
* Number of wardrobe items the backend created for this gift, when
|
|
106
|
+
* the server reported it. Omitted when the count is unknown — never
|
|
107
|
+
* fabricated.
|
|
108
|
+
*/
|
|
109
|
+
count?: number;
|
|
110
|
+
}
|
|
83
111
|
export interface ProactiveParams {
|
|
84
112
|
history?: HistoryEntry[];
|
|
85
113
|
maxUnreplied?: number;
|
|
@@ -95,6 +123,13 @@ export interface ProactiveParams {
|
|
|
95
123
|
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
96
124
|
/** Fires per modality as each media task settles successfully. */
|
|
97
125
|
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
126
|
+
/**
|
|
127
|
+
* Fires when an outfit has been successfully added to the wardrobe
|
|
128
|
+
* during this turn (user-initiated gift OR character-initiated
|
|
129
|
+
* acquisition). Lets the UI render a system message like
|
|
130
|
+
* "New outfit added to wardrobe" in real time.
|
|
131
|
+
*/
|
|
132
|
+
onOutfitGifted?: (payload: OutfitGiftedPayload) => void;
|
|
98
133
|
}
|
|
99
134
|
export interface ProactiveResponse {
|
|
100
135
|
status: "success" | "skipped" | "error";
|
|
@@ -113,6 +148,10 @@ export interface ProactiveResponse {
|
|
|
113
148
|
* can still render the text reply and explain the missing media
|
|
114
149
|
* without losing the conversation. See [InteractMediaError]. */
|
|
115
150
|
mediaError?: InteractMediaError;
|
|
151
|
+
/** Set when an outfit was successfully added to the wardrobe this turn.
|
|
152
|
+
* Mirrors the [ProactiveParams.onOutfitGifted] callback for consumers
|
|
153
|
+
* that only read the final response. See [OutfitGiftedPayload]. */
|
|
154
|
+
giftedOutfit?: OutfitGiftedPayload;
|
|
116
155
|
error?: string;
|
|
117
156
|
}
|
|
118
157
|
export interface InteractParams {
|
|
@@ -132,6 +171,13 @@ export interface InteractParams {
|
|
|
132
171
|
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
133
172
|
/** Fires per modality as each media task settles successfully. */
|
|
134
173
|
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
174
|
+
/**
|
|
175
|
+
* Fires when an outfit has been successfully added to the wardrobe
|
|
176
|
+
* during this turn (user-initiated gift OR character-initiated
|
|
177
|
+
* acquisition). Lets the UI render a system message like
|
|
178
|
+
* "New outfit added to wardrobe" in real time.
|
|
179
|
+
*/
|
|
180
|
+
onOutfitGifted?: (payload: OutfitGiftedPayload) => void;
|
|
135
181
|
}
|
|
136
182
|
export interface OndemandEventParams {
|
|
137
183
|
eventDescription: string;
|
|
@@ -173,6 +219,10 @@ export interface InteractResponse {
|
|
|
173
219
|
imageMediaId?: string;
|
|
174
220
|
audioUrl?: string;
|
|
175
221
|
audioMediaId?: string;
|
|
222
|
+
/** Set when an outfit was successfully added to the wardrobe this turn.
|
|
223
|
+
* Mirrors the [InteractParams.onOutfitGifted] callback for consumers
|
|
224
|
+
* that only read the final response. See [OutfitGiftedPayload]. */
|
|
225
|
+
giftedOutfit?: OutfitGiftedPayload;
|
|
176
226
|
likePreviousPicture?: boolean;
|
|
177
227
|
durationSec?: number;
|
|
178
228
|
triggeredEvent?: {
|