@space3-npm/cybersoul-client 1.4.15 → 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 CHANGED
@@ -724,6 +724,34 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
724
724
  if (parsedIntent && (parsedIntent.stateUpdate || parsedIntent.userAnalysis)) {
725
725
  persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
726
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
+ }
727
755
  const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
728
756
  parsedIntent.textResponse.trim().length > 0
729
757
  ? parsedIntent.textResponse
@@ -799,6 +827,18 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
799
827
  .then((res) => {
800
828
  finalImageUrl = res.image_url;
801
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
+ }
802
842
  })
803
843
  .catch((e) => {
804
844
  if (!(e instanceof CyberSoulInsufficientPointsError) &&
@@ -826,6 +866,19 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
826
866
  finalAudioUrl = res.audio_url;
827
867
  finalAudioMediaId = res.id;
828
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
+ }
829
882
  })
830
883
  .catch((e) => {
831
884
  if (!(e instanceof CyberSoulInsufficientPointsError) &&
@@ -1095,6 +1148,26 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
1095
1148
  if (parsedIntent.stateUpdate) {
1096
1149
  persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate);
1097
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
+ }
1098
1171
  if (params.onTextReady) {
1099
1172
  params.onTextReady(parsedIntent.textResponse, parsedIntent.actionText, {
1100
1173
  stateUpdate: parsedIntent.stateUpdate,
@@ -1109,6 +1182,18 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
1109
1182
  const res = await this.generatePrimitive("image", parsedIntent.imageParams);
1110
1183
  finalImageUrl = res.image_url;
1111
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
+ }
1112
1197
  }
1113
1198
  catch (e) {
1114
1199
  if (e instanceof CyberSoulInsufficientPointsError ||
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@space3-npm/cybersoul-client",
3
- "version": "1.4.15",
3
+ "version": "1.4.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",