@unoverse-platform/studio 0.1.35 → 0.1.36

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unoverse-platform/studio",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "description": "Unoverse Studio: the local authoring app. Reads your own rx/ folder, no backend.",
6
6
  "scripts": {
@@ -373,6 +373,16 @@ export function TemplatesView({
373
373
  [canConnect, rawConn],
374
374
  );
375
375
 
376
+ // The workflow can OVERRIDE the manual pick (WORKFLOW_STARTED metadata.template →
377
+ // store.activeTemplate) with a BARE name — so the org must be resolved for whichever
378
+ // template is actually loading, not the picked one, or the resources/read goes out
379
+ // org-less and the server refuses it ("template address needs its org").
380
+ const activeName = conn.activeTemplate ?? selected;
381
+ const activeDef = useMemo(
382
+ () => (activeName === selected ? selectedDef : (templates.find((t) => t.name === activeName) ?? selectedDef)),
383
+ [templates, activeName, selected, selectedDef],
384
+ );
385
+
376
386
  // The `voice` native service (§2e-1) now lives INSIDE the SDK renderer (shared by every
377
387
  // host — see StreamedUnoverseTemplate). The host only DECLARES the app is voice (off its
378
388
  // manifest `service`) and hands the renderer the connection identity it already holds;
@@ -1005,6 +1015,11 @@ export function TemplatesView({
1005
1015
  conversationId={session.conversationId}
1006
1016
  onStatus={setNativeStatus}
1007
1017
  />
1018
+ ) : !activeDef && templates.length === 0 ? (
1019
+ /* The workflow can announce its template BEFORE /dev/templates answers.
1020
+ Reading it org-less is a guaranteed server refusal, so hold one beat
1021
+ until the list arrives and the org can be resolved. */
1022
+ null
1008
1023
  ) : (
1009
1024
  <StreamedUnoverseTemplate
1010
1025
  client={client}
@@ -1012,7 +1027,7 @@ export function TemplatesView({
1012
1027
  // The workflow can SELECT the template (WORKFLOW_STARTED metadata.template →
1013
1028
  // store.activeTemplate); that overrides the manual pick. Loaded via the
1014
1029
  // official resources/read unoverse://templates/{name} (templates = MCP apps).
1015
- uri={`unoverse://templates/${selectedDef?.org ? `${selectedDef.org}/` : ""}${conn.activeTemplate ?? selected}`}
1030
+ uri={`unoverse://templates/${activeDef?.org ? `${activeDef.org}/` : ""}${activeName}`}
1016
1031
  theme={theme}
1017
1032
  // Voice state arrives via TEMPLATE STATE (the service is a producer inside
1018
1033
  // the renderer); startCall/endCall/toggleMute are answered there too.
@@ -154,8 +154,10 @@ export class UnoverseClient {
154
154
  })();
155
155
  this.inflight.set(uri, read);
156
156
  // Cleared either way: a failed read must not be remembered as pending forever, or the
157
- // next asker waits on a promise that will never resolve again.
158
- read.finally(() => this.inflight.delete(uri));
157
+ // next asker waits on a promise that will never resolve again. The catch is REQUIRED:
158
+ // a bare `read.finally()` forks a second promise chain that nobody awaits, so every
159
+ // failed read also surfaced as an Uncaught (in promise) even when the caller handled it.
160
+ read.catch(() => {}).finally(() => this.inflight.delete(uri));
159
161
  return read;
160
162
  }
161
163
 
@@ -177,7 +179,7 @@ export class UnoverseClient {
177
179
  return theme;
178
180
  })();
179
181
  this.themeInflight.set(name, read);
180
- read.finally(() => this.themeInflight.delete(name));
182
+ read.catch(() => {}).finally(() => this.themeInflight.delete(name));
181
183
  return read;
182
184
  }
183
185