@space3-npm/cybersoul-client 1.4.3 → 1.4.5
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 +32 -2
- package/dist/client.js +101 -19
- package/dist/llm.provider.d.ts +3 -1
- package/dist/llm.provider.js +8 -3
- package/dist/types.d.ts +25 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, HistoryEntry, LikedPicture } from "./types.js";
|
|
1
|
+
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, HistoryEntry, LikedPicture, PersistedDynamicContext } from "./types.js";
|
|
2
2
|
export declare class CyberSoulClient {
|
|
3
3
|
private config;
|
|
4
4
|
private llm;
|
|
@@ -14,6 +14,18 @@ export declare class CyberSoulClient {
|
|
|
14
14
|
private fetchRemoteState;
|
|
15
15
|
private getWardrobePromptStr;
|
|
16
16
|
private generatePrimitive;
|
|
17
|
+
/**
|
|
18
|
+
* PATCH the backend dynamic context. The server applies stage-based
|
|
19
|
+
* dampening, familiarity soft-caps, hard floor, and stage re-evaluation,
|
|
20
|
+
* then returns the *authoritative* persisted `temperature` and
|
|
21
|
+
* `relationshipStage`. We surface those so callers (and ultimately the UI)
|
|
22
|
+
* can avoid recomputing the delta locally — local math would diverge from
|
|
23
|
+
* the server because the LLM-supplied `temperatureDelta` is just raw intent.
|
|
24
|
+
*
|
|
25
|
+
* Returns `null` when there's nothing to send, or when the request fails
|
|
26
|
+
* (failure is non-fatal for the chat turn; callers must treat `null` as
|
|
27
|
+
* "no fresh server snapshot available").
|
|
28
|
+
*/
|
|
17
29
|
private _updateDynamicContextInternal;
|
|
18
30
|
private normalizeRequestTypes;
|
|
19
31
|
private getElapsedTimeInfo;
|
|
@@ -42,6 +54,22 @@ export declare class CyberSoulClient {
|
|
|
42
54
|
* If the payload is already the inner args object (no voiceArgs wrapper), uses it as-is.
|
|
43
55
|
*/
|
|
44
56
|
private extractVoiceArgsFromLlmResponse;
|
|
57
|
+
/**
|
|
58
|
+
* Strip content the TTS engine can't speak naturally:
|
|
59
|
+
* - Stage-direction wrappers like (smiles), (挑眉), [pauses], 【动作】, *grins*
|
|
60
|
+
* — these slip through despite prompt instructions and the engine will
|
|
61
|
+
* literally read the brackets/asterisks if left in.
|
|
62
|
+
* - Emoji and emoji-component codepoints (Extended_Pictographic plus the
|
|
63
|
+
* ZWJ / variation-selector / skin-tone / regional-indicator scaffolding
|
|
64
|
+
* that builds composite emoji). TTS providers either read these aloud
|
|
65
|
+
* as the literal Unicode name ("face with tears of joy") or produce a
|
|
66
|
+
* glitchy artifact, both of which sound wrong.
|
|
67
|
+
*
|
|
68
|
+
* Collapses runs of whitespace introduced by removals and trims the result.
|
|
69
|
+
* Returns "" if everything gets stripped — callers should fall back to a
|
|
70
|
+
* neutral placeholder (e.g. "...") so the TTS call still has valid input.
|
|
71
|
+
*/
|
|
72
|
+
private sanitizeTextForVoice;
|
|
45
73
|
private formatHistoryEntries;
|
|
46
74
|
private buildHistoryTranscript;
|
|
47
75
|
interact(params: InteractParams): Promise<InteractResponse>;
|
|
@@ -79,8 +107,10 @@ export declare class CyberSoulClient {
|
|
|
79
107
|
getState(): Promise<CharacterState>;
|
|
80
108
|
/**
|
|
81
109
|
* Updates the character's relationship temperature or mood.
|
|
110
|
+
* Returns the server-authoritative post-write `{ temperature, relationshipStage }`
|
|
111
|
+
* snapshot (or `null` if there was nothing to send / the request failed).
|
|
82
112
|
*/
|
|
83
|
-
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<
|
|
113
|
+
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<PersistedDynamicContext | null>;
|
|
84
114
|
/**
|
|
85
115
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
86
116
|
*/
|
package/dist/client.js
CHANGED
|
@@ -13,7 +13,7 @@ export class CyberSoulClient {
|
|
|
13
13
|
this.requestTimeoutMs = config.requestTimeoutMs ?? 120000;
|
|
14
14
|
this.maxRetries = Math.max(0, config.maxRetries ?? 1);
|
|
15
15
|
// Setup Provider
|
|
16
|
-
this.llm = new GenericLLMProvider(config.llmConfig, config.backendUrl, config.characterKey);
|
|
16
|
+
this.llm = new GenericLLMProvider(config.llmConfig, config.backendUrl, config.characterKey, config.fetchImpl);
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Internal wrapper for fetch that automatically injects the backend URL and Character Auth token.
|
|
@@ -33,7 +33,8 @@ export class CyberSoulClient {
|
|
|
33
33
|
const controller = new AbortController();
|
|
34
34
|
const timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
35
35
|
try {
|
|
36
|
-
const
|
|
36
|
+
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
37
|
+
const response = await fetchFn(url, {
|
|
37
38
|
...options,
|
|
38
39
|
headers,
|
|
39
40
|
signal: controller.signal,
|
|
@@ -115,9 +116,21 @@ export class CyberSoulClient {
|
|
|
115
116
|
}
|
|
116
117
|
return res.json();
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* PATCH the backend dynamic context. The server applies stage-based
|
|
121
|
+
* dampening, familiarity soft-caps, hard floor, and stage re-evaluation,
|
|
122
|
+
* then returns the *authoritative* persisted `temperature` and
|
|
123
|
+
* `relationshipStage`. We surface those so callers (and ultimately the UI)
|
|
124
|
+
* can avoid recomputing the delta locally — local math would diverge from
|
|
125
|
+
* the server because the LLM-supplied `temperatureDelta` is just raw intent.
|
|
126
|
+
*
|
|
127
|
+
* Returns `null` when there's nothing to send, or when the request fails
|
|
128
|
+
* (failure is non-fatal for the chat turn; callers must treat `null` as
|
|
129
|
+
* "no fresh server snapshot available").
|
|
130
|
+
*/
|
|
118
131
|
async _updateDynamicContextInternal(stateUpdate, userAnalysis) {
|
|
119
132
|
if (!stateUpdate && !userAnalysis)
|
|
120
|
-
return;
|
|
133
|
+
return null;
|
|
121
134
|
// Map TS schema intent (temperatureDelta) to match Backend payload schema (temperature)
|
|
122
135
|
const payload = { ...stateUpdate };
|
|
123
136
|
if (userAnalysis) {
|
|
@@ -131,10 +144,39 @@ export class CyberSoulClient {
|
|
|
131
144
|
const normalizedOngoingScene = this.normalizeOngoingSceneState(payload.ongoingScene);
|
|
132
145
|
payload.ongoingScene = normalizedOngoingScene || null;
|
|
133
146
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
let res;
|
|
148
|
+
try {
|
|
149
|
+
res = await this.apiFetch("/api/v1/cyber-soul/characters/dynamic-context", {
|
|
150
|
+
method: "PATCH",
|
|
151
|
+
body: JSON.stringify(payload),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
console.error("Failed to update dynamic context", e);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
console.error(`Failed to update dynamic context: HTTP ${res.status}`);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const body = (await res.json());
|
|
164
|
+
const temperature = typeof body.dynamicContext?.temperature === "number" &&
|
|
165
|
+
Number.isFinite(body.dynamicContext.temperature)
|
|
166
|
+
? body.dynamicContext.temperature
|
|
167
|
+
: undefined;
|
|
168
|
+
const relationshipStage = typeof body.relationshipStage === "string"
|
|
169
|
+
? body.relationshipStage
|
|
170
|
+
: undefined;
|
|
171
|
+
if (temperature === undefined && relationshipStage === undefined) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return { temperature, relationshipStage };
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
console.error("Failed to parse dynamic-context PATCH response", e);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
138
180
|
}
|
|
139
181
|
normalizeRequestTypes(requestTypes) {
|
|
140
182
|
let normalized = requestTypes;
|
|
@@ -408,6 +450,32 @@ ${isProactive
|
|
|
408
450
|
}
|
|
409
451
|
return payload;
|
|
410
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Strip content the TTS engine can't speak naturally:
|
|
455
|
+
* - Stage-direction wrappers like (smiles), (挑眉), [pauses], 【动作】, *grins*
|
|
456
|
+
* — these slip through despite prompt instructions and the engine will
|
|
457
|
+
* literally read the brackets/asterisks if left in.
|
|
458
|
+
* - Emoji and emoji-component codepoints (Extended_Pictographic plus the
|
|
459
|
+
* ZWJ / variation-selector / skin-tone / regional-indicator scaffolding
|
|
460
|
+
* that builds composite emoji). TTS providers either read these aloud
|
|
461
|
+
* as the literal Unicode name ("face with tears of joy") or produce a
|
|
462
|
+
* glitchy artifact, both of which sound wrong.
|
|
463
|
+
*
|
|
464
|
+
* Collapses runs of whitespace introduced by removals and trims the result.
|
|
465
|
+
* Returns "" if everything gets stripped — callers should fall back to a
|
|
466
|
+
* neutral placeholder (e.g. "...") so the TTS call still has valid input.
|
|
467
|
+
*/
|
|
468
|
+
sanitizeTextForVoice(text) {
|
|
469
|
+
if (typeof text !== "string")
|
|
470
|
+
return "";
|
|
471
|
+
return text
|
|
472
|
+
// (parens), (全角), [brackets], 【全角】, *asterisks*
|
|
473
|
+
.replace(/[\((\[【\*].*?[\))\]】\*]/g, "")
|
|
474
|
+
// emoji + ZWJ + variation selectors + skin-tone modifiers + regional indicators
|
|
475
|
+
.replace(/[\p{Extended_Pictographic}\u200D\uFE0F\uFE0E\u{1F3FB}-\u{1F3FF}\u{1F1E6}-\u{1F1FF}]/gu, "")
|
|
476
|
+
.replace(/\s+/g, " ")
|
|
477
|
+
.trim();
|
|
478
|
+
}
|
|
411
479
|
formatHistoryEntries(history, userName, agentName, promptDirective = "") {
|
|
412
480
|
const contextLines = [];
|
|
413
481
|
for (let i = 0; i < history.length; i++) {
|
|
@@ -574,9 +642,15 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
574
642
|
};
|
|
575
643
|
}
|
|
576
644
|
// console.debug("[CyberSoulClient] Parsed Intent:", parsedIntent);
|
|
577
|
-
// 4. Update Backend State async
|
|
645
|
+
// 4. Update Backend State async (in parallel with media generation
|
|
646
|
+
// below). We keep the promise so we can resolve the
|
|
647
|
+
// server-authoritative `temperature` / `relationshipStage` and
|
|
648
|
+
// return it in the final response — clients cannot reproduce the
|
|
649
|
+
// server's stage dampening + soft caps locally, so this is the only
|
|
650
|
+
// reliable source of truth.
|
|
651
|
+
let persistedStatePromise = Promise.resolve(null);
|
|
578
652
|
if (parsedIntent && (parsedIntent.stateUpdate || parsedIntent.userAnalysis)) {
|
|
579
|
-
this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
653
|
+
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
580
654
|
}
|
|
581
655
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
582
656
|
parsedIntent.textResponse.trim().length > 0
|
|
@@ -642,12 +716,8 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
642
716
|
const normalizedVoiceArgs = parsedIntent.voiceArgs && typeof parsedIntent.voiceArgs === "object"
|
|
643
717
|
? parsedIntent.voiceArgs
|
|
644
718
|
: {};
|
|
645
|
-
let textForVoice = resolvedTextResponse;
|
|
646
|
-
|
|
647
|
-
if (typeof textForVoice === "string") {
|
|
648
|
-
textForVoice = textForVoice.replace(/[\((\[【\*].*?[\))\]】\*]/g, '').trim();
|
|
649
|
-
}
|
|
650
|
-
if (typeof textForVoice !== "string" || textForVoice.trim().length === 0) {
|
|
719
|
+
let textForVoice = this.sanitizeTextForVoice(resolvedTextResponse);
|
|
720
|
+
if (textForVoice.length === 0) {
|
|
651
721
|
textForVoice = "...";
|
|
652
722
|
}
|
|
653
723
|
mediaTasks.push(this.generatePrimitive("voice", {
|
|
@@ -666,6 +736,11 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
666
736
|
}
|
|
667
737
|
// Wait for image/voice gens to return successfully
|
|
668
738
|
await Promise.all(mediaTasks);
|
|
739
|
+
// Await the dynamic-context PATCH alongside media so the final
|
|
740
|
+
// response carries the server's authoritative temperature/stage.
|
|
741
|
+
// This adds at most ~1 small request to the critical path; in
|
|
742
|
+
// practice the PATCH usually resolves before media generation.
|
|
743
|
+
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
669
744
|
return {
|
|
670
745
|
status: "success",
|
|
671
746
|
textResponse: resolvedTextResponse || "...",
|
|
@@ -678,6 +753,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
678
753
|
stateUpdate: parsedIntent.stateUpdate,
|
|
679
754
|
userAnalysis: parsedIntent.userAnalysis,
|
|
680
755
|
isEndTurn: parsedIntent.isEndTurn,
|
|
756
|
+
persistedDynamicContext,
|
|
681
757
|
};
|
|
682
758
|
}
|
|
683
759
|
catch (error) {
|
|
@@ -886,9 +962,11 @@ You MUST output ONLY a valid JSON object matching exactly this structure:
|
|
|
886
962
|
reason: parsedIntent.skipReason || "Character decided to skip proactive message based on mood/stage."
|
|
887
963
|
};
|
|
888
964
|
}
|
|
889
|
-
// Update Remote state if needed
|
|
965
|
+
// Update Remote state if needed (capture promise for authoritative
|
|
966
|
+
// server snapshot — see notes in interact()).
|
|
967
|
+
let persistedStatePromise = Promise.resolve(null);
|
|
890
968
|
if (parsedIntent.stateUpdate) {
|
|
891
|
-
this._updateDynamicContextInternal(parsedIntent.stateUpdate)
|
|
969
|
+
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate);
|
|
892
970
|
}
|
|
893
971
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
894
972
|
parsedIntent.textResponse.trim().length > 0
|
|
@@ -915,12 +993,14 @@ You MUST output ONLY a valid JSON object matching exactly this structure:
|
|
|
915
993
|
console.error("[CyberSoulClient] Proactive Image generation failed:", e);
|
|
916
994
|
}
|
|
917
995
|
}
|
|
996
|
+
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
918
997
|
return {
|
|
919
998
|
status: "success",
|
|
920
999
|
textResponse: parsedIntent.textResponse,
|
|
921
1000
|
actionText: parsedIntent.actionText,
|
|
922
1001
|
imageUrl: finalImageUrl,
|
|
923
|
-
stateUpdate: parsedIntent.stateUpdate
|
|
1002
|
+
stateUpdate: parsedIntent.stateUpdate,
|
|
1003
|
+
persistedDynamicContext,
|
|
924
1004
|
};
|
|
925
1005
|
}
|
|
926
1006
|
catch (error) {
|
|
@@ -994,7 +1074,7 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
994
1074
|
dynamicArgs = {};
|
|
995
1075
|
}
|
|
996
1076
|
const res = await this.generatePrimitive("voice", {
|
|
997
|
-
text: params.text,
|
|
1077
|
+
text: this.sanitizeTextForVoice(params.text) || "...",
|
|
998
1078
|
dynamicArgs,
|
|
999
1079
|
});
|
|
1000
1080
|
return {
|
|
@@ -1010,6 +1090,8 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1010
1090
|
}
|
|
1011
1091
|
/**
|
|
1012
1092
|
* Updates the character's relationship temperature or mood.
|
|
1093
|
+
* Returns the server-authoritative post-write `{ temperature, relationshipStage }`
|
|
1094
|
+
* snapshot (or `null` if there was nothing to send / the request failed).
|
|
1013
1095
|
*/
|
|
1014
1096
|
async updateDynamicContext(stateUpdate, userAnalysis) {
|
|
1015
1097
|
return this._updateDynamicContextInternal(stateUpdate, userAnalysis);
|
package/dist/llm.provider.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export declare class GenericLLMProvider implements BaseLLMProvider {
|
|
|
3
3
|
private config;
|
|
4
4
|
private backendApiUrl;
|
|
5
5
|
private backendAuthToken?;
|
|
6
|
+
private fetchImpl?;
|
|
6
7
|
private static templateCache;
|
|
7
|
-
constructor(config: GenericLLMConfig, backendApiUrl: string, backendAuthToken?: string | undefined);
|
|
8
|
+
constructor(config: GenericLLMConfig, backendApiUrl: string, backendAuthToken?: string | undefined, fetchImpl?: typeof fetch | undefined);
|
|
9
|
+
private get fetchFn();
|
|
8
10
|
private fetchTemplate;
|
|
9
11
|
private extractResponse;
|
|
10
12
|
generate(messages: {
|
package/dist/llm.provider.js
CHANGED
|
@@ -2,11 +2,16 @@ export class GenericLLMProvider {
|
|
|
2
2
|
config;
|
|
3
3
|
backendApiUrl;
|
|
4
4
|
backendAuthToken;
|
|
5
|
+
fetchImpl;
|
|
5
6
|
static templateCache = new Map();
|
|
6
|
-
constructor(config, backendApiUrl, backendAuthToken) {
|
|
7
|
+
constructor(config, backendApiUrl, backendAuthToken, fetchImpl) {
|
|
7
8
|
this.config = config;
|
|
8
9
|
this.backendApiUrl = backendApiUrl;
|
|
9
10
|
this.backendAuthToken = backendAuthToken;
|
|
11
|
+
this.fetchImpl = fetchImpl;
|
|
12
|
+
}
|
|
13
|
+
get fetchFn() {
|
|
14
|
+
return this.fetchImpl ?? fetch;
|
|
10
15
|
}
|
|
11
16
|
async fetchTemplate() {
|
|
12
17
|
const cacheKey = `${this.config.provider}:${this.config.model}`;
|
|
@@ -24,7 +29,7 @@ export class GenericLLMProvider {
|
|
|
24
29
|
provider: this.config.provider,
|
|
25
30
|
model: this.config.model
|
|
26
31
|
});
|
|
27
|
-
const resp = await
|
|
32
|
+
const resp = await this.fetchFn(`${this.backendApiUrl}/api/v1/cyber-soul/llm-models/template?${qs.toString()}`, {
|
|
28
33
|
headers
|
|
29
34
|
});
|
|
30
35
|
if (!resp.ok) {
|
|
@@ -66,7 +71,7 @@ export class GenericLLMProvider {
|
|
|
66
71
|
if (!payload.messages || (Array.isArray(payload.messages) && payload.messages.length === 0)) {
|
|
67
72
|
payload.messages = messages;
|
|
68
73
|
}
|
|
69
|
-
const response = await
|
|
74
|
+
const response = await this.fetchFn(template.apiUrl, {
|
|
70
75
|
method: 'POST',
|
|
71
76
|
headers,
|
|
72
77
|
body: JSON.stringify(payload)
|
package/dist/types.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface CyberSoulClientConfig {
|
|
|
10
10
|
llmConfig: GenericLLMConfig;
|
|
11
11
|
requestTimeoutMs?: number;
|
|
12
12
|
maxRetries?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Optional fetch override. When provided, the client uses this in
|
|
15
|
+
* place of the global `fetch` for every HTTP call (backend + LLM
|
|
16
|
+
* provider). Intended for environments where the global fetch is
|
|
17
|
+
* suspended by the host platform — e.g. React Native on Samsung
|
|
18
|
+
* BBA / Doze — and a native HTTP path must be used instead. Must
|
|
19
|
+
* conform to the standard `fetch` signature.
|
|
20
|
+
*/
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
13
22
|
}
|
|
14
23
|
export declare enum InteractRequestType {
|
|
15
24
|
AUTO = "auto",
|
|
@@ -32,6 +41,18 @@ export interface InteractMetadata {
|
|
|
32
41
|
triggerEvent?: DispatcherIntent["triggerEvent"];
|
|
33
42
|
likePreviousPicture?: boolean;
|
|
34
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Server-authoritative snapshot returned by PATCH /characters/dynamic-context
|
|
46
|
+
* after the backend applies stage dampening, familiarity soft caps, hard
|
|
47
|
+
* floors, rounding, and stage re-evaluation. Use this instead of recomputing
|
|
48
|
+
* the delta on the client.
|
|
49
|
+
*/
|
|
50
|
+
export interface PersistedDynamicContext {
|
|
51
|
+
/** Persisted absolute temperature (0-100), post all server-side adjustments. */
|
|
52
|
+
temperature?: number;
|
|
53
|
+
/** Persisted relationship stage label after re-evaluation. */
|
|
54
|
+
relationshipStage?: string;
|
|
55
|
+
}
|
|
35
56
|
export interface ProactiveParams {
|
|
36
57
|
history?: HistoryEntry[];
|
|
37
58
|
maxUnreplied?: number;
|
|
@@ -47,6 +68,8 @@ export interface ProactiveResponse {
|
|
|
47
68
|
imageUrl?: string;
|
|
48
69
|
audioUrl?: string;
|
|
49
70
|
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
71
|
+
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
72
|
+
persistedDynamicContext?: PersistedDynamicContext;
|
|
50
73
|
error?: string;
|
|
51
74
|
}
|
|
52
75
|
export interface InteractParams {
|
|
@@ -105,6 +128,8 @@ export interface InteractResponse {
|
|
|
105
128
|
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
106
129
|
userAnalysis?: DispatcherIntent["userAnalysis"];
|
|
107
130
|
isEndTurn?: boolean;
|
|
131
|
+
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
132
|
+
persistedDynamicContext?: PersistedDynamicContext;
|
|
108
133
|
error?: string;
|
|
109
134
|
}
|
|
110
135
|
export interface OngoingSceneState {
|