@unoverse-platform/studio 0.1.79 → 0.1.81
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/package.json
CHANGED
|
@@ -137,6 +137,13 @@ export function applyServerMessage(store: ComponentStore, msg: ServerMessage, se
|
|
|
137
137
|
else if (m.state === "ERROR" || m.state === "WORKFLOW_ERROR") store.setLifecycle("error");
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
|
+
case "COMPONENTS_CLEAR": {
|
|
141
|
+
// Rule 5's empty delivery (STATE_MODEL §5, amended 2026-08-26): a turn that ran
|
|
142
|
+
// with cards enabled and surfaced none owns the screen — earlier-turn components
|
|
143
|
+
// in public states retract to inline. The sender decided WHEN; this only obeys.
|
|
144
|
+
store.supersedeAll(typeof msg.chatId === "string" ? msg.chatId : undefined);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
140
147
|
// No AUDIO_STATE case: voice is a NATIVE SERVICE on the WS audio lane (useVoiceService),
|
|
141
148
|
// NOT the structured MCP stream. Audio-state events never arrive here (UNOVERSE_SPEC §2e-1).
|
|
142
149
|
case "APP_DATA": {
|
|
@@ -182,6 +182,31 @@ export class ComponentStore {
|
|
|
182
182
|
* refresh is VISIBLE is the ladder's business alone (rule 4: higher states mask;
|
|
183
183
|
* lower states update silently underneath).
|
|
184
184
|
*/
|
|
185
|
+
/**
|
|
186
|
+
* RULE 5's EMPTY DELIVERY (STATE_MODEL §5 rule 5, amended 2026-08-26): a discovery
|
|
187
|
+
* turn that ran with cards enabled and surfaced NONE still owns the screen — the
|
|
188
|
+
* server says so with COMPONENTS_CLEAR, and every earlier-turn component sitting in
|
|
189
|
+
* a public state retracts to inline. `supersede` for ALL states at once: same
|
|
190
|
+
* durable (`lifetime: "conversation"`) opt-out, same same-turn exemption. Rule 7
|
|
191
|
+
* stands — this rides the answer's delivery, never the guest's typing.
|
|
192
|
+
*/
|
|
193
|
+
supersedeAll(arrivingChatId?: string): void {
|
|
194
|
+
let changed = false;
|
|
195
|
+
for (const [key, slice] of this.data) {
|
|
196
|
+
if (arrivingChatId && key.startsWith(`${arrivingChatId}:`)) continue;
|
|
197
|
+
if (slice.lifetime === "conversation") continue;
|
|
198
|
+
const v = (slice.view ?? slice.defaultState) as unknown;
|
|
199
|
+
if (typeof v !== "string" || !v || v === "inline") continue;
|
|
200
|
+
const next: Record<string, unknown> = { ...slice };
|
|
201
|
+
if ("view" in next || !("defaultState" in next)) next.view = "inline";
|
|
202
|
+
if ("defaultState" in next) next.defaultState = "inline";
|
|
203
|
+
this.data.set(key, next);
|
|
204
|
+
this.touches.set(key, ++this.touchSeq);
|
|
205
|
+
changed = true;
|
|
206
|
+
}
|
|
207
|
+
if (changed) this.bump();
|
|
208
|
+
}
|
|
209
|
+
|
|
185
210
|
private supersede(arrivingChatId: string, view: unknown): void {
|
|
186
211
|
if (typeof view !== "string" || !view || view === "inline") return;
|
|
187
212
|
let changed = false;
|