@space3-npm/cybersoul-client 1.4.11 → 1.4.13
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 +8 -0
- package/dist/client.js +80 -5
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -70,6 +70,14 @@ export declare class CyberSoulClient {
|
|
|
70
70
|
* neutral placeholder (e.g. "...") so the TTS call still has valid input.
|
|
71
71
|
*/
|
|
72
72
|
private sanitizeTextForVoice;
|
|
73
|
+
/**
|
|
74
|
+
* Build the in-band `mediaError` envelope from the first typed media
|
|
75
|
+
* failure captured during `interact()` / `proactiveInteract()`. Keeps
|
|
76
|
+
* the conversion in one place so both call sites stay consistent and
|
|
77
|
+
* the SDK never re-throws on a partial media failure once the text
|
|
78
|
+
* reply is already in flight.
|
|
79
|
+
*/
|
|
80
|
+
private buildMediaError;
|
|
73
81
|
private formatHistoryEntries;
|
|
74
82
|
private buildHistoryTranscript;
|
|
75
83
|
interact(params: InteractParams): Promise<InteractResponse>;
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InteractRequestType, } from "./types.js";
|
|
2
2
|
import { robustJsonParse } from "./utils/json.utils.js";
|
|
3
3
|
import { GenericLLMProvider } from "./llm.provider.js";
|
|
4
|
-
import { CyberSoulApiError, CyberSoulAuthError, CyberSoulInsufficientPointsError, CyberSoulNetworkError, CyberSoulTimeoutError, CyberSoulWalletError, } from "./errors.js";
|
|
4
|
+
import { CyberSoulApiError, CyberSoulAuthError, CyberSoulError, CyberSoulInsufficientPointsError, CyberSoulNetworkError, CyberSoulTimeoutError, CyberSoulWalletError, } from "./errors.js";
|
|
5
5
|
export class CyberSoulClient {
|
|
6
6
|
config;
|
|
7
7
|
llm;
|
|
@@ -515,6 +515,35 @@ ${isProactive
|
|
|
515
515
|
.replace(/\s+/g, " ")
|
|
516
516
|
.trim();
|
|
517
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Build the in-band `mediaError` envelope from the first typed media
|
|
520
|
+
* failure captured during `interact()` / `proactiveInteract()`. Keeps
|
|
521
|
+
* the conversion in one place so both call sites stay consistent and
|
|
522
|
+
* the SDK never re-throws on a partial media failure once the text
|
|
523
|
+
* reply is already in flight.
|
|
524
|
+
*/
|
|
525
|
+
buildMediaError(err, affected) {
|
|
526
|
+
if (err instanceof CyberSoulInsufficientPointsError) {
|
|
527
|
+
return {
|
|
528
|
+
kind: "insufficient-points",
|
|
529
|
+
code: err.code,
|
|
530
|
+
message: err.message,
|
|
531
|
+
affected,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (err instanceof CyberSoulWalletError) {
|
|
535
|
+
return {
|
|
536
|
+
kind: "wallet",
|
|
537
|
+
message: err.message,
|
|
538
|
+
affected,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
kind: "unknown",
|
|
543
|
+
message: err.message,
|
|
544
|
+
affected,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
518
547
|
formatHistoryEntries(history, userName, agentName, promptDirective = "") {
|
|
519
548
|
const contextLines = [];
|
|
520
549
|
for (let i = 0; i < history.length; i++) {
|
|
@@ -712,6 +741,27 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
712
741
|
let finalAudioUrl = undefined;
|
|
713
742
|
let finalAudioMediaId = undefined;
|
|
714
743
|
let finalDurationSec = undefined;
|
|
744
|
+
// Partial-failure capture: text was already produced and emitted
|
|
745
|
+
// via [onTextReady], so a wallet / insufficient-points failure on
|
|
746
|
+
// image or voice MUST NOT abort the whole turn. We collect the
|
|
747
|
+
// affected modalities + first typed error and surface them in-band
|
|
748
|
+
// through `InteractResponse.mediaError`. The caller (MessageBus /
|
|
749
|
+
// UI) decides how to message the user without losing the reply.
|
|
750
|
+
const mediaErrorAffected = [];
|
|
751
|
+
let firstMediaError = null;
|
|
752
|
+
const captureMediaError = (modality, e) => {
|
|
753
|
+
if (!(e instanceof CyberSoulError))
|
|
754
|
+
return;
|
|
755
|
+
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
756
|
+
!(e instanceof CyberSoulWalletError)) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
if (!mediaErrorAffected.includes(modality)) {
|
|
760
|
+
mediaErrorAffected.push(modality);
|
|
761
|
+
}
|
|
762
|
+
if (!firstMediaError)
|
|
763
|
+
firstMediaError = e;
|
|
764
|
+
};
|
|
715
765
|
// Output Event Trigger
|
|
716
766
|
if (parsedIntent.triggerEvent) {
|
|
717
767
|
mediaTasks.push(this.apiFetch("/api/v1/cyber-soul/characters/ondemand-event", {
|
|
@@ -748,8 +798,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
748
798
|
})
|
|
749
799
|
.catch((e) => {
|
|
750
800
|
console.error("[CyberSoulClient] Image generation failed:", e);
|
|
751
|
-
|
|
752
|
-
throw e;
|
|
801
|
+
captureMediaError("image", e);
|
|
753
802
|
}));
|
|
754
803
|
}
|
|
755
804
|
const shouldGenerateVoice = types.includes(InteractRequestType.VOICE) &&
|
|
@@ -773,8 +822,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
773
822
|
})
|
|
774
823
|
.catch((e) => {
|
|
775
824
|
console.error("[CyberSoulClient] Voice generation failed:", e);
|
|
776
|
-
|
|
777
|
-
throw e;
|
|
825
|
+
captureMediaError("voice", e);
|
|
778
826
|
}));
|
|
779
827
|
}
|
|
780
828
|
// Wait for image/voice gens to return successfully
|
|
@@ -784,6 +832,9 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
784
832
|
// This adds at most ~1 small request to the critical path; in
|
|
785
833
|
// practice the PATCH usually resolves before media generation.
|
|
786
834
|
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
835
|
+
const mediaError = firstMediaError
|
|
836
|
+
? this.buildMediaError(firstMediaError, mediaErrorAffected)
|
|
837
|
+
: undefined;
|
|
787
838
|
return {
|
|
788
839
|
status: "success",
|
|
789
840
|
textResponse: resolvedTextResponse || "...",
|
|
@@ -799,9 +850,18 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
799
850
|
userAnalysis: parsedIntent.userAnalysis,
|
|
800
851
|
isEndTurn: parsedIntent.isEndTurn,
|
|
801
852
|
persistedDynamicContext,
|
|
853
|
+
mediaError,
|
|
802
854
|
};
|
|
803
855
|
}
|
|
804
856
|
catch (error) {
|
|
857
|
+
// Typed SDK errors (insufficient points, wallet failure, auth, etc.)
|
|
858
|
+
// are part of the public contract — let callers branch on
|
|
859
|
+
// `instanceof` instead of string-sniffing a generic status:"error"
|
|
860
|
+
// envelope. Only truly-unexpected throws fall back to the legacy
|
|
861
|
+
// envelope so we don't break callers that don't yet handle throws.
|
|
862
|
+
if (error instanceof CyberSoulError) {
|
|
863
|
+
throw error;
|
|
864
|
+
}
|
|
805
865
|
console.error("[CyberSoulClient] Interface Error: ", error);
|
|
806
866
|
return {
|
|
807
867
|
status: "error",
|
|
@@ -1032,6 +1092,8 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1032
1092
|
}
|
|
1033
1093
|
let finalImageUrl;
|
|
1034
1094
|
let finalImageMediaId;
|
|
1095
|
+
let proactiveMediaError = null;
|
|
1096
|
+
const proactiveAffected = [];
|
|
1035
1097
|
if (parsedIntent.imageParams) {
|
|
1036
1098
|
try {
|
|
1037
1099
|
const res = await this.generatePrimitive("image", parsedIntent.imageParams);
|
|
@@ -1040,9 +1102,17 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1040
1102
|
}
|
|
1041
1103
|
catch (e) {
|
|
1042
1104
|
console.error("[CyberSoulClient] Proactive Image generation failed:", e);
|
|
1105
|
+
if (e instanceof CyberSoulInsufficientPointsError ||
|
|
1106
|
+
e instanceof CyberSoulWalletError) {
|
|
1107
|
+
proactiveMediaError = e;
|
|
1108
|
+
proactiveAffected.push("image");
|
|
1109
|
+
}
|
|
1043
1110
|
}
|
|
1044
1111
|
}
|
|
1045
1112
|
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
1113
|
+
const proactiveMediaErrorEnv = proactiveMediaError
|
|
1114
|
+
? this.buildMediaError(proactiveMediaError, proactiveAffected)
|
|
1115
|
+
: undefined;
|
|
1046
1116
|
return {
|
|
1047
1117
|
status: "success",
|
|
1048
1118
|
textResponse: parsedIntent.textResponse,
|
|
@@ -1051,9 +1121,14 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1051
1121
|
imageMediaId: finalImageMediaId,
|
|
1052
1122
|
stateUpdate: parsedIntent.stateUpdate,
|
|
1053
1123
|
persistedDynamicContext,
|
|
1124
|
+
mediaError: proactiveMediaErrorEnv,
|
|
1054
1125
|
};
|
|
1055
1126
|
}
|
|
1056
1127
|
catch (error) {
|
|
1128
|
+
// Mirror `interact()`: preserve typed SDK errors for the caller.
|
|
1129
|
+
if (error instanceof CyberSoulError) {
|
|
1130
|
+
throw error;
|
|
1131
|
+
}
|
|
1057
1132
|
console.error("[CyberSoulClient] Proactive Interact Error: ", error);
|
|
1058
1133
|
return { status: "error", error: error.message };
|
|
1059
1134
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -72,6 +72,11 @@ export interface ProactiveResponse {
|
|
|
72
72
|
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
73
73
|
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
74
74
|
persistedDynamicContext?: PersistedDynamicContext;
|
|
75
|
+
/** Partial-failure descriptor: text was generated successfully but one or
|
|
76
|
+
* more media calls (image/voice) failed. Surfaced in-band so the caller
|
|
77
|
+
* can still render the text reply and explain the missing media
|
|
78
|
+
* without losing the conversation. See [InteractMediaError]. */
|
|
79
|
+
mediaError?: InteractMediaError;
|
|
75
80
|
error?: string;
|
|
76
81
|
}
|
|
77
82
|
export interface InteractParams {
|
|
@@ -134,8 +139,30 @@ export interface InteractResponse {
|
|
|
134
139
|
isEndTurn?: boolean;
|
|
135
140
|
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
136
141
|
persistedDynamicContext?: PersistedDynamicContext;
|
|
142
|
+
/** Partial-failure descriptor: text was generated successfully but one or
|
|
143
|
+
* more media calls (image/voice) failed. Surfaced in-band so the caller
|
|
144
|
+
* can still render the text reply and explain the missing media
|
|
145
|
+
* without losing the conversation. See [InteractMediaError]. */
|
|
146
|
+
mediaError?: InteractMediaError;
|
|
137
147
|
error?: string;
|
|
138
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Describes a partial-failure during an [interact] / [proactiveInteract]
|
|
151
|
+
* call: the text reply was generated and returned, but image and/or
|
|
152
|
+
* voice generation failed (usually because the user ran out of points
|
|
153
|
+
* mid-turn). Surfaced in-band on the success envelope so callers can
|
|
154
|
+
* render the text response without losing it to an exception.
|
|
155
|
+
*/
|
|
156
|
+
export interface InteractMediaError {
|
|
157
|
+
/** Coarse kind so UIs can map to a single user-facing message. */
|
|
158
|
+
kind: "insufficient-points" | "wallet" | "unknown";
|
|
159
|
+
/** Backend machine code when available (e.g. "INSUFFICIENT_POINTS"). */
|
|
160
|
+
code?: string;
|
|
161
|
+
/** Raw error message, for logs / diagnostics. */
|
|
162
|
+
message?: string;
|
|
163
|
+
/** Which media generation calls were affected. */
|
|
164
|
+
affected: Array<"image" | "voice">;
|
|
165
|
+
}
|
|
139
166
|
export interface OngoingSceneState {
|
|
140
167
|
scene: string;
|
|
141
168
|
outfit: string;
|