@space3-npm/cybersoul-client 1.4.13 → 1.4.16
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 +101 -4
- package/dist/llm.provider.js +4 -1
- package/dist/types.d.ts +36 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -34,7 +34,11 @@ export class CyberSoulClient {
|
|
|
34
34
|
const controller = new AbortController();
|
|
35
35
|
const timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
36
36
|
try {
|
|
37
|
-
|
|
37
|
+
// NOTE: When no custom fetchImpl is provided, fall back to the global
|
|
38
|
+
// `fetch` bound to `globalThis`. Browsers throw "Illegal invocation"
|
|
39
|
+
// if the global `fetch` is invoked while detached from its Window
|
|
40
|
+
// receiver (e.g. via a captured reference).
|
|
41
|
+
const fetchFn = this.config.fetchImpl ?? fetch.bind(globalThis);
|
|
38
42
|
const response = await fetchFn(url, {
|
|
39
43
|
...options,
|
|
40
44
|
headers,
|
|
@@ -720,6 +724,34 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
720
724
|
if (parsedIntent && (parsedIntent.stateUpdate || parsedIntent.userAnalysis)) {
|
|
721
725
|
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
722
726
|
}
|
|
727
|
+
// Fire `onStateReady` the moment the dynamic-context PATCH resolves
|
|
728
|
+
// (or immediately, when no state update was emitted). This is
|
|
729
|
+
// independent of media generation, so the UI can stop showing
|
|
730
|
+
// "updating…" on temperature / relationship stage well before the
|
|
731
|
+
// (potentially slow) image task finishes. Errors are swallowed:
|
|
732
|
+
// an authoritative snapshot is best-effort, the optimistic delta
|
|
733
|
+
// already applied client-side is the fallback.
|
|
734
|
+
if (params.onStateReady) {
|
|
735
|
+
const stateReadyCb = params.onStateReady;
|
|
736
|
+
persistedStatePromise
|
|
737
|
+
.then((persisted) => {
|
|
738
|
+
try {
|
|
739
|
+
stateReadyCb(persisted ?? {});
|
|
740
|
+
}
|
|
741
|
+
catch (cbErr) {
|
|
742
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
743
|
+
}
|
|
744
|
+
})
|
|
745
|
+
.catch(() => {
|
|
746
|
+
// PATCH failed; still signal LLM-phase complete with an empty snapshot.
|
|
747
|
+
try {
|
|
748
|
+
stateReadyCb({});
|
|
749
|
+
}
|
|
750
|
+
catch (cbErr) {
|
|
751
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
723
755
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
724
756
|
parsedIntent.textResponse.trim().length > 0
|
|
725
757
|
? parsedIntent.textResponse
|
|
@@ -795,9 +827,24 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
795
827
|
.then((res) => {
|
|
796
828
|
finalImageUrl = res.image_url;
|
|
797
829
|
finalImageMediaId = res.id;
|
|
830
|
+
if (params.onMediaReady && finalImageUrl) {
|
|
831
|
+
try {
|
|
832
|
+
params.onMediaReady({
|
|
833
|
+
modality: "image",
|
|
834
|
+
url: finalImageUrl,
|
|
835
|
+
mediaId: finalImageMediaId,
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
catch (cbErr) {
|
|
839
|
+
console.warn("[CyberSoulClient] onMediaReady(image) threw:", cbErr);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
798
842
|
})
|
|
799
843
|
.catch((e) => {
|
|
800
|
-
|
|
844
|
+
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
845
|
+
!(e instanceof CyberSoulWalletError)) {
|
|
846
|
+
console.error("[CyberSoulClient] Image generation failed:", e);
|
|
847
|
+
}
|
|
801
848
|
captureMediaError("image", e);
|
|
802
849
|
}));
|
|
803
850
|
}
|
|
@@ -819,9 +866,25 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
819
866
|
finalAudioUrl = res.audio_url;
|
|
820
867
|
finalAudioMediaId = res.id;
|
|
821
868
|
finalDurationSec = res.duration_sec;
|
|
869
|
+
if (params.onMediaReady && finalAudioUrl) {
|
|
870
|
+
try {
|
|
871
|
+
params.onMediaReady({
|
|
872
|
+
modality: "voice",
|
|
873
|
+
url: finalAudioUrl,
|
|
874
|
+
mediaId: finalAudioMediaId,
|
|
875
|
+
durationSec: finalDurationSec,
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
catch (cbErr) {
|
|
879
|
+
console.warn("[CyberSoulClient] onMediaReady(voice) threw:", cbErr);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
822
882
|
})
|
|
823
883
|
.catch((e) => {
|
|
824
|
-
|
|
884
|
+
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
885
|
+
!(e instanceof CyberSoulWalletError)) {
|
|
886
|
+
console.error("[CyberSoulClient] Voice generation failed:", e);
|
|
887
|
+
}
|
|
825
888
|
captureMediaError("voice", e);
|
|
826
889
|
}));
|
|
827
890
|
}
|
|
@@ -1085,6 +1148,26 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1085
1148
|
if (parsedIntent.stateUpdate) {
|
|
1086
1149
|
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate);
|
|
1087
1150
|
}
|
|
1151
|
+
if (params.onStateReady) {
|
|
1152
|
+
const stateReadyCb = params.onStateReady;
|
|
1153
|
+
persistedStatePromise
|
|
1154
|
+
.then((persisted) => {
|
|
1155
|
+
try {
|
|
1156
|
+
stateReadyCb(persisted ?? {});
|
|
1157
|
+
}
|
|
1158
|
+
catch (cbErr) {
|
|
1159
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
1160
|
+
}
|
|
1161
|
+
})
|
|
1162
|
+
.catch(() => {
|
|
1163
|
+
try {
|
|
1164
|
+
stateReadyCb({});
|
|
1165
|
+
}
|
|
1166
|
+
catch (cbErr) {
|
|
1167
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1088
1171
|
if (params.onTextReady) {
|
|
1089
1172
|
params.onTextReady(parsedIntent.textResponse, parsedIntent.actionText, {
|
|
1090
1173
|
stateUpdate: parsedIntent.stateUpdate,
|
|
@@ -1099,14 +1182,28 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1099
1182
|
const res = await this.generatePrimitive("image", parsedIntent.imageParams);
|
|
1100
1183
|
finalImageUrl = res.image_url;
|
|
1101
1184
|
finalImageMediaId = res.id;
|
|
1185
|
+
if (params.onMediaReady && finalImageUrl) {
|
|
1186
|
+
try {
|
|
1187
|
+
params.onMediaReady({
|
|
1188
|
+
modality: "image",
|
|
1189
|
+
url: finalImageUrl,
|
|
1190
|
+
mediaId: finalImageMediaId,
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
catch (cbErr) {
|
|
1194
|
+
console.warn("[CyberSoulClient] onMediaReady(image) threw:", cbErr);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1102
1197
|
}
|
|
1103
1198
|
catch (e) {
|
|
1104
|
-
console.error("[CyberSoulClient] Proactive Image generation failed:", e);
|
|
1105
1199
|
if (e instanceof CyberSoulInsufficientPointsError ||
|
|
1106
1200
|
e instanceof CyberSoulWalletError) {
|
|
1107
1201
|
proactiveMediaError = e;
|
|
1108
1202
|
proactiveAffected.push("image");
|
|
1109
1203
|
}
|
|
1204
|
+
else {
|
|
1205
|
+
console.error("[CyberSoulClient] Proactive Image generation failed:", e);
|
|
1206
|
+
}
|
|
1110
1207
|
}
|
|
1111
1208
|
}
|
|
1112
1209
|
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
package/dist/llm.provider.js
CHANGED
|
@@ -11,7 +11,10 @@ export class GenericLLMProvider {
|
|
|
11
11
|
this.fetchImpl = fetchImpl;
|
|
12
12
|
}
|
|
13
13
|
get fetchFn() {
|
|
14
|
-
|
|
14
|
+
// Bind to `globalThis` so the global `fetch` is not invoked detached
|
|
15
|
+
// from its Window receiver (which throws "Illegal invocation" in
|
|
16
|
+
// Chromium-based browsers).
|
|
17
|
+
return this.fetchImpl ?? fetch.bind(globalThis);
|
|
15
18
|
}
|
|
16
19
|
async fetchTemplate() {
|
|
17
20
|
const cacheKey = `${this.config.provider}:${this.config.model}`;
|
package/dist/types.d.ts
CHANGED
|
@@ -53,12 +53,37 @@ export interface PersistedDynamicContext {
|
|
|
53
53
|
/** Persisted relationship stage label after re-evaluation. */
|
|
54
54
|
relationshipStage?: string;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Payload delivered by [InteractParams.onMediaReady] when an individual
|
|
58
|
+
* media task (image/voice) finishes. Fires from inside the SDK's
|
|
59
|
+
* per-modality `.then()` so callers can render the bubble the moment
|
|
60
|
+
* that modality is ready, instead of waiting for the slowest one to
|
|
61
|
+
* finish. The aggregated `InteractResponse` is still returned at the
|
|
62
|
+
* end and carries the same URLs (no double-render needed if the caller
|
|
63
|
+
* tracks per-modality state).
|
|
64
|
+
*/
|
|
65
|
+
export interface MediaReadyPayload {
|
|
66
|
+
modality: "image" | "voice";
|
|
67
|
+
url: string;
|
|
68
|
+
mediaId?: string;
|
|
69
|
+
/** Voice only — TTS duration in seconds when known. */
|
|
70
|
+
durationSec?: number;
|
|
71
|
+
}
|
|
56
72
|
export interface ProactiveParams {
|
|
57
73
|
history?: HistoryEntry[];
|
|
58
74
|
maxUnreplied?: number;
|
|
59
75
|
requestTypes?: InteractRequestType[];
|
|
60
76
|
localContext?: string;
|
|
61
77
|
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Fires when the server-authoritative PATCH /dynamic-context resolves,
|
|
80
|
+
* before media generation completes. Lets the UI update the live
|
|
81
|
+
* temperature / relationship stage immediately instead of waiting for
|
|
82
|
+
* the (potentially slow) image task.
|
|
83
|
+
*/
|
|
84
|
+
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
85
|
+
/** Fires per modality as each media task settles successfully. */
|
|
86
|
+
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
62
87
|
}
|
|
63
88
|
export interface ProactiveResponse {
|
|
64
89
|
status: "success" | "skipped" | "error";
|
|
@@ -85,6 +110,17 @@ export interface InteractParams {
|
|
|
85
110
|
requestTypes?: InteractRequestType[];
|
|
86
111
|
history?: HistoryEntry[];
|
|
87
112
|
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Fires when the server-authoritative PATCH /dynamic-context resolves,
|
|
115
|
+
* before media generation completes. Lets the UI update the live
|
|
116
|
+
* temperature / relationship stage immediately instead of waiting for
|
|
117
|
+
* the (potentially slow) image task. When the turn has no
|
|
118
|
+
* `stateUpdate`, this still fires with an empty object so callers can
|
|
119
|
+
* use it as a generic "LLM phase done" signal.
|
|
120
|
+
*/
|
|
121
|
+
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
122
|
+
/** Fires per modality as each media task settles successfully. */
|
|
123
|
+
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
88
124
|
}
|
|
89
125
|
export interface OndemandEventParams {
|
|
90
126
|
eventDescription: string;
|