@femtomc/mu-core 26.2.107 → 26.2.109

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/README.md CHANGED
@@ -34,28 +34,15 @@ await runContext({ runId: newRunId() }, async () => {
34
34
  console.log(await jsonl.read());
35
35
  ```
36
36
 
37
- ## HUD contract helpers
38
-
39
- `@femtomc/mu-core` exports a versioned HUD contract, deterministic JSON helper, and shared runtime loop:
40
-
41
- - `HudDocSchema`, `HUD_CONTRACT_VERSION`
42
- - `parseHudDoc(...)`, `normalizeHudDocs(...)`
43
- - `resolveHudStylePresetName(...)`, `applyHudStylePreset(...)`, `hudStylePresetWarnings(...)`
44
- - `serializeHudDocTextFallback(...)`, `serializeHudDocsTextFallback(...)`
45
- - `stableSerializeJson(...)`
46
- - `HudRuntime` + `HudProvider` for provider registration, reducer updates, ordered effect execution, and HUD snapshot emission
47
-
48
- `HudDoc` also supports optional presentation hints (`title_style`, `snapshot_style`, chip/item/section/action style objects) and metadata style presets (`metadata.style_preset` currently `planning|subagents`) so renderers can opt into richer emphasis while preserving deterministic plain-text fallbacks.
49
-
50
- These are runtime-agnostic primitives intended for shared HUD capture/render pipelines.
51
-
52
37
  ## UI contract helpers
53
38
 
54
39
  Interactive UI documents are modeled as first-class, versioned contracts that describe renderable components, actions, and revisions so any rendering surface can stay in sync with agents. `@femtomc/mu-core` exposes the following helpers:
55
40
 
56
41
  - `UI_CONTRACT_VERSION`, `UiDocSchema`, `UiRevisionSchema`, `UiComponentSchema`, and `UiActionSchema` for validating document payloads.
57
42
  - `parseUiDoc(...)`, `normalizeUiDocs(...)`, and `uiDocRevisionConflict(...)` for deterministic selection, conflict detection, and safe merging of candidate documents.
43
+ - `resolveUiStatusProfileName(...)` and `uiStatusProfileWarnings(...)` for advisory validation of profile-scoped status docs (`planning`, `subagents`, `control-flow`, `model-routing`) carried in `metadata.profile`.
58
44
  - `UiEventSchema` + `parseUiEvent(...)` for structured event payloads emitted by frontends (every event includes `ui_id`, `action_id`, the originating `revision`, optional `callback_token`, `payload`, and `created_at_ms`).
45
+ - `stableSerializeJson(...)` for deterministic metadata serialization and revision tie-breaking.
59
46
 
60
47
  ### Field limits
61
48
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  export * from "./events.js";
2
- export * from "./hud.js";
3
- export * from "./hud_runtime.js";
4
2
  export * from "./ids.js";
5
3
  export * from "./persistence.js";
6
4
  export * from "./spec.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,4 @@
1
1
  export * from "./events.js";
2
- export * from "./hud.js";
3
- export * from "./hud_runtime.js";
4
2
  export * from "./ids.js";
5
3
  export * from "./persistence.js";
6
4
  export * from "./spec.js";
package/dist/ui.d.ts CHANGED
@@ -1,4 +1,14 @@
1
1
  import { z } from "zod";
2
+ export declare const UiToneSchema: z.ZodEnum<{
3
+ error: "error";
4
+ success: "success";
5
+ info: "info";
6
+ warning: "warning";
7
+ muted: "muted";
8
+ accent: "accent";
9
+ dim: "dim";
10
+ }>;
11
+ export type UiTone = z.infer<typeof UiToneSchema>;
2
12
  export declare const UI_CONTRACT_VERSION: 1;
3
13
  export declare const UiListItemSchema: z.ZodObject<{
4
14
  id: z.ZodString;
@@ -6,8 +16,8 @@ export declare const UiListItemSchema: z.ZodObject<{
6
16
  detail: z.ZodOptional<z.ZodString>;
7
17
  tone: z.ZodOptional<z.ZodEnum<{
8
18
  error: "error";
9
- info: "info";
10
19
  success: "success";
20
+ info: "info";
11
21
  warning: "warning";
12
22
  muted: "muted";
13
23
  accent: "accent";
@@ -20,8 +30,8 @@ export declare const UiKeyValueRowSchema: z.ZodObject<{
20
30
  value: z.ZodString;
21
31
  tone: z.ZodOptional<z.ZodEnum<{
22
32
  error: "error";
23
- info: "info";
24
33
  success: "success";
34
+ info: "info";
25
35
  warning: "warning";
26
36
  muted: "muted";
27
37
  accent: "accent";
@@ -35,8 +45,8 @@ export declare const UiComponentTextSchema: z.ZodObject<{
35
45
  text: z.ZodString;
36
46
  tone: z.ZodOptional<z.ZodEnum<{
37
47
  error: "error";
38
- info: "info";
39
48
  success: "success";
49
+ info: "info";
40
50
  warning: "warning";
41
51
  muted: "muted";
42
52
  accent: "accent";
@@ -55,8 +65,8 @@ export declare const UiComponentListSchema: z.ZodObject<{
55
65
  detail: z.ZodOptional<z.ZodString>;
56
66
  tone: z.ZodOptional<z.ZodEnum<{
57
67
  error: "error";
58
- info: "info";
59
68
  success: "success";
69
+ info: "info";
60
70
  warning: "warning";
61
71
  muted: "muted";
62
72
  accent: "accent";
@@ -74,8 +84,8 @@ export declare const UiComponentKeyValueSchema: z.ZodObject<{
74
84
  value: z.ZodString;
75
85
  tone: z.ZodOptional<z.ZodEnum<{
76
86
  error: "error";
77
- info: "info";
78
87
  success: "success";
88
+ info: "info";
79
89
  warning: "warning";
80
90
  muted: "muted";
81
91
  accent: "accent";
@@ -95,8 +105,8 @@ export declare const UiComponentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
95
105
  text: z.ZodString;
96
106
  tone: z.ZodOptional<z.ZodEnum<{
97
107
  error: "error";
98
- info: "info";
99
108
  success: "success";
109
+ info: "info";
100
110
  warning: "warning";
101
111
  muted: "muted";
102
112
  accent: "accent";
@@ -113,8 +123,8 @@ export declare const UiComponentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
113
123
  detail: z.ZodOptional<z.ZodString>;
114
124
  tone: z.ZodOptional<z.ZodEnum<{
115
125
  error: "error";
116
- info: "info";
117
126
  success: "success";
127
+ info: "info";
118
128
  warning: "warning";
119
129
  muted: "muted";
120
130
  accent: "accent";
@@ -131,8 +141,8 @@ export declare const UiComponentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
131
141
  value: z.ZodString;
132
142
  tone: z.ZodOptional<z.ZodEnum<{
133
143
  error: "error";
134
- info: "info";
135
144
  success: "success";
145
+ info: "info";
136
146
  warning: "warning";
137
147
  muted: "muted";
138
148
  accent: "accent";
@@ -185,8 +195,8 @@ export declare const UiDocSchema: z.ZodObject<{
185
195
  text: z.ZodString;
186
196
  tone: z.ZodOptional<z.ZodEnum<{
187
197
  error: "error";
188
- info: "info";
189
198
  success: "success";
199
+ info: "info";
190
200
  warning: "warning";
191
201
  muted: "muted";
192
202
  accent: "accent";
@@ -203,8 +213,8 @@ export declare const UiDocSchema: z.ZodObject<{
203
213
  detail: z.ZodOptional<z.ZodString>;
204
214
  tone: z.ZodOptional<z.ZodEnum<{
205
215
  error: "error";
206
- info: "info";
207
216
  success: "success";
217
+ info: "info";
208
218
  warning: "warning";
209
219
  muted: "muted";
210
220
  accent: "accent";
@@ -221,8 +231,8 @@ export declare const UiDocSchema: z.ZodObject<{
221
231
  value: z.ZodString;
222
232
  tone: z.ZodOptional<z.ZodEnum<{
223
233
  error: "error";
224
- info: "info";
225
234
  success: "success";
235
+ info: "info";
226
236
  warning: "warning";
227
237
  muted: "muted";
228
238
  accent: "accent";
@@ -258,6 +268,8 @@ export declare const UiDocSchema: z.ZodObject<{
258
268
  metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
259
269
  }, z.core.$strict>;
260
270
  export type UiDoc = z.infer<typeof UiDocSchema>;
271
+ export declare const UI_STATUS_PROFILE_NAMES: readonly ["planning", "subagents", "control-flow", "model-routing"];
272
+ export type UiStatusProfileName = (typeof UI_STATUS_PROFILE_NAMES)[number];
261
273
  export declare const UiEventSchema: z.ZodObject<{
262
274
  ui_id: z.ZodString;
263
275
  action_id: z.ZodString;
@@ -272,7 +284,12 @@ export declare const UiEventSchema: z.ZodObject<{
272
284
  metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
273
285
  }, z.core.$strict>;
274
286
  export type UiEvent = z.infer<typeof UiEventSchema>;
287
+ export declare function stableSerializeJson(value: unknown, opts?: {
288
+ pretty?: boolean;
289
+ }): string;
275
290
  export declare function parseUiDoc(input: unknown): UiDoc | null;
291
+ export declare function resolveUiStatusProfileName(input: unknown): UiStatusProfileName | null;
292
+ export declare function uiStatusProfileWarnings(input: unknown): string[];
276
293
  export declare function normalizeUiDocs(input: unknown, opts?: {
277
294
  maxDocs?: number;
278
295
  }): UiDoc[];
package/dist/ui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,mBAAmB,EAAG,CAAU,CAAC;AAsB9C,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;kBAOnB,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;kBAMtB,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;kBAQxB,CAAC;AACX,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;kBAQxB,CAAC;AAEX,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;kBAQ5B,CAAC;AAEX,eAAO,MAAM,wBAAwB;;;;kBAM3B,CAAC;AAEX,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;;;EAAqD,CAAC;AACrF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,cAAc;;;;;;;;;;;;;;kBAWjB,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,gBAAgB;;;kBAKnB,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAYd,CAAC;AACX,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,aAAa;;;;;;;;;;;;kBAWhB,CAAC;AACX,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAkDpD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAEvD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,KAAK,EAAE,CAoBxF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAQxE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAM3D"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
package/dist/ui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { HudToneSchema, stableSerializeJson } from "./hud.js";
2
+ export const UiToneSchema = z.enum(["info", "success", "warning", "error", "muted", "accent", "dim"]);
3
3
  export const UI_CONTRACT_VERSION = 1;
4
4
  const UI_DOC_TITLE_MAX_LENGTH = 256;
5
5
  const UI_DOC_SUBTITLE_MAX_LENGTH = 512;
@@ -24,14 +24,14 @@ export const UiListItemSchema = z
24
24
  id: NonEmptyId,
25
25
  label: NonEmptyText(UI_ACTION_LABEL_MAX_LENGTH),
26
26
  detail: NonEmptyText(UI_LIST_ITEM_DETAIL_MAX_LENGTH).optional(),
27
- tone: HudToneSchema.optional(),
27
+ tone: UiToneSchema.optional(),
28
28
  })
29
29
  .strict();
30
30
  export const UiKeyValueRowSchema = z
31
31
  .object({
32
32
  key: NonEmptyText(UI_KEY_MAX_LENGTH),
33
33
  value: NonEmptyText(UI_VALUE_MAX_LENGTH),
34
- tone: HudToneSchema.optional(),
34
+ tone: UiToneSchema.optional(),
35
35
  })
36
36
  .strict();
37
37
  export const UiComponentTextSchema = z
@@ -39,7 +39,7 @@ export const UiComponentTextSchema = z
39
39
  kind: z.literal("text"),
40
40
  id: NonEmptyId,
41
41
  text: NonEmptyText(UI_TEXT_COMPONENT_MAX_LENGTH),
42
- tone: HudToneSchema.optional(),
42
+ tone: UiToneSchema.optional(),
43
43
  metadata: z.record(z.string(), z.unknown()).default({}),
44
44
  })
45
45
  .strict();
@@ -106,6 +106,7 @@ export const UiDocSchema = z
106
106
  metadata: z.record(z.string(), z.unknown()).default({}),
107
107
  })
108
108
  .strict();
109
+ export const UI_STATUS_PROFILE_NAMES = ["planning", "subagents", "control-flow", "model-routing"];
109
110
  export const UiEventSchema = z
110
111
  .object({
111
112
  ui_id: NonEmptyId,
@@ -128,6 +129,129 @@ function parseUiDocCandidate(value) {
128
129
  }
129
130
  return parsed.data;
130
131
  }
132
+ const UI_PROFILE_VARIANTS = ["status", "interactive"];
133
+ const UI_STATUS_PROFILE_UI_IDS = {
134
+ planning: "ui:planning",
135
+ subagents: "ui:subagents",
136
+ "control-flow": "ui:control-flow",
137
+ "model-routing": "ui:model-routing",
138
+ };
139
+ const UI_STATUS_PROFILE_COMPONENT_RECOMMENDATIONS = {
140
+ planning: ["key_value", "list"],
141
+ subagents: ["key_value", "list"],
142
+ "control-flow": ["key_value"],
143
+ "model-routing": ["key_value", "list"],
144
+ };
145
+ const UI_PLANNING_STATUS_ROW_KEY_ALIASES = {
146
+ phase: ["phase"],
147
+ waiting: ["waiting", "waiting_on_user"],
148
+ confidence: ["confidence", "conf"],
149
+ next: ["next", "next_action"],
150
+ blocker: ["blocker"],
151
+ };
152
+ const UI_PLANNING_CHECKLIST_MIN_ITEMS = 3;
153
+ function isUiStatusProfileName(value) {
154
+ return UI_STATUS_PROFILE_NAMES.includes(value);
155
+ }
156
+ function isUiProfileVariant(value) {
157
+ return UI_PROFILE_VARIANTS.includes(value);
158
+ }
159
+ function uiProfileMetadata(doc) {
160
+ const raw = doc.metadata.profile;
161
+ if (!isPlainObject(raw)) {
162
+ return null;
163
+ }
164
+ return raw;
165
+ }
166
+ function uiStatusProfileNameFromDoc(doc) {
167
+ const profile = uiProfileMetadata(doc);
168
+ if (!profile) {
169
+ return null;
170
+ }
171
+ const raw = profile.id;
172
+ if (typeof raw !== "string") {
173
+ return null;
174
+ }
175
+ const normalized = raw.trim().toLowerCase();
176
+ if (!normalized || !isUiStatusProfileName(normalized)) {
177
+ return null;
178
+ }
179
+ return normalized;
180
+ }
181
+ function hasUiComponentKind(doc, kind) {
182
+ return doc.components.some((component) => component.kind === kind);
183
+ }
184
+ function hasProfileSnapshotCompact(profile) {
185
+ const snapshot = profile.snapshot;
186
+ if (!isPlainObject(snapshot)) {
187
+ return false;
188
+ }
189
+ const compact = snapshot.compact;
190
+ return typeof compact === "string" && compact.trim().length > 0;
191
+ }
192
+ function normalizeStatusRowKey(key) {
193
+ return key
194
+ .trim()
195
+ .toLowerCase()
196
+ .replace(/[^a-z0-9]+/g, "_");
197
+ }
198
+ function keyValueStatusRows(doc) {
199
+ const rows = new Set();
200
+ for (const component of doc.components) {
201
+ if (component.kind !== "key_value") {
202
+ continue;
203
+ }
204
+ for (const row of component.rows) {
205
+ rows.add(normalizeStatusRowKey(row.key));
206
+ }
207
+ }
208
+ return rows;
209
+ }
210
+ function firstChecklistComponent(doc) {
211
+ for (const component of doc.components) {
212
+ if (component.kind === "list") {
213
+ return component;
214
+ }
215
+ }
216
+ return null;
217
+ }
218
+ function hasPlanningMetadataField(doc, key) {
219
+ const value = doc.metadata[key];
220
+ if (key === "waiting_on_user") {
221
+ return typeof value === "boolean";
222
+ }
223
+ return typeof value === "string" && value.trim().length > 0;
224
+ }
225
+ function planningStatusProfileWarnings(doc) {
226
+ const warnings = [];
227
+ const statusRows = keyValueStatusRows(doc);
228
+ for (const [field, aliases] of Object.entries(UI_PLANNING_STATUS_ROW_KEY_ALIASES)) {
229
+ const hasField = aliases.some((alias) => statusRows.has(alias));
230
+ if (!hasField) {
231
+ warnings.push(`profile.id=planning recommends key_value row key=${field}`);
232
+ }
233
+ }
234
+ const checklist = firstChecklistComponent(doc);
235
+ if (checklist) {
236
+ if (checklist.items.length < UI_PLANNING_CHECKLIST_MIN_ITEMS) {
237
+ warnings.push(`profile.id=planning recommends checklist lists with at least ${UI_PLANNING_CHECKLIST_MIN_ITEMS} items`);
238
+ }
239
+ const detailedItems = checklist.items.filter((item) => typeof item.detail === "string" && item.detail.trim().length > 0).length;
240
+ if (detailedItems === 0) {
241
+ warnings.push("profile.id=planning recommends checklist item detail values (for example done/pending)");
242
+ }
243
+ }
244
+ if (!hasPlanningMetadataField(doc, "phase")) {
245
+ warnings.push("profile.id=planning recommends metadata.phase");
246
+ }
247
+ if (!hasPlanningMetadataField(doc, "waiting_on_user")) {
248
+ warnings.push("profile.id=planning recommends metadata.waiting_on_user");
249
+ }
250
+ if (!hasPlanningMetadataField(doc, "confidence")) {
251
+ warnings.push("profile.id=planning recommends metadata.confidence");
252
+ }
253
+ return warnings;
254
+ }
131
255
  function uiDocCandidates(input) {
132
256
  if (Array.isArray(input)) {
133
257
  return input;
@@ -150,6 +274,28 @@ function normalizedUiDocLimit(limit) {
150
274
  }
151
275
  return parsed;
152
276
  }
277
+ function canonicalizeJson(value) {
278
+ if (Array.isArray(value)) {
279
+ return value.map((entry) => canonicalizeJson(entry));
280
+ }
281
+ if (!isPlainObject(value)) {
282
+ return value;
283
+ }
284
+ const out = {};
285
+ for (const key of Object.keys(value).sort()) {
286
+ const nextValue = value[key];
287
+ if (nextValue === undefined) {
288
+ continue;
289
+ }
290
+ out[key] = canonicalizeJson(nextValue);
291
+ }
292
+ return out;
293
+ }
294
+ export function stableSerializeJson(value, opts = {}) {
295
+ const normalized = canonicalizeJson(value);
296
+ const text = JSON.stringify(normalized, null, opts.pretty ? 2 : undefined);
297
+ return text ?? "null";
298
+ }
153
299
  function deterministicUiDocChoice(left, right) {
154
300
  if (left.revision.version !== right.revision.version) {
155
301
  return left.revision.version > right.revision.version ? left : right;
@@ -164,6 +310,63 @@ function deterministicUiDocChoice(left, right) {
164
310
  export function parseUiDoc(input) {
165
311
  return parseUiDocCandidate(input);
166
312
  }
313
+ export function resolveUiStatusProfileName(input) {
314
+ const doc = parseUiDocCandidate(input);
315
+ if (!doc) {
316
+ return null;
317
+ }
318
+ return uiStatusProfileNameFromDoc(doc);
319
+ }
320
+ export function uiStatusProfileWarnings(input) {
321
+ const doc = parseUiDocCandidate(input);
322
+ if (!doc) {
323
+ return [];
324
+ }
325
+ const profileName = uiStatusProfileNameFromDoc(doc);
326
+ if (!profileName) {
327
+ return [];
328
+ }
329
+ const warnings = [];
330
+ const profile = uiProfileMetadata(doc);
331
+ if (!profile) {
332
+ return warnings;
333
+ }
334
+ const rawVariant = typeof profile.variant === "string" ? profile.variant.trim().toLowerCase() : "";
335
+ let variant = "status";
336
+ if (rawVariant.length > 0) {
337
+ if (isUiProfileVariant(rawVariant)) {
338
+ variant = rawVariant;
339
+ }
340
+ else {
341
+ warnings.push(`profile.id=${profileName} has unsupported metadata.profile.variant=${rawVariant}`);
342
+ }
343
+ }
344
+ if (variant !== "status") {
345
+ warnings.push(`profile.id=${profileName} status validation expects metadata.profile.variant=status (got ${variant})`);
346
+ }
347
+ const expectedUiId = UI_STATUS_PROFILE_UI_IDS[profileName];
348
+ if (doc.ui_id !== expectedUiId) {
349
+ warnings.push(`profile.id=${profileName} expects ui_id=${expectedUiId} (got ${doc.ui_id})`);
350
+ }
351
+ if (!doc.summary) {
352
+ warnings.push(`profile.id=${profileName} recommends summary for deterministic status fallback`);
353
+ }
354
+ if (!hasProfileSnapshotCompact(profile)) {
355
+ warnings.push(`profile.id=${profileName} recommends metadata.profile.snapshot.compact`);
356
+ }
357
+ if (doc.actions.length > 0) {
358
+ warnings.push(`profile.id=${profileName} status docs should omit actions (got ${doc.actions.length})`);
359
+ }
360
+ for (const kind of UI_STATUS_PROFILE_COMPONENT_RECOMMENDATIONS[profileName]) {
361
+ if (!hasUiComponentKind(doc, kind)) {
362
+ warnings.push(`profile.id=${profileName} recommends a ${kind} component`);
363
+ }
364
+ }
365
+ if (profileName === "planning") {
366
+ warnings.push(...planningStatusProfileWarnings(doc));
367
+ }
368
+ return warnings;
369
+ }
167
370
  export function normalizeUiDocs(input, opts = {}) {
168
371
  const maxDocs = normalizedUiDocLimit(opts.maxDocs);
169
372
  const byId = new Map();
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
1
  {
2
- "name": "@femtomc/mu-core",
3
- "version": "26.2.107",
4
- "description": "Core primitives for mu: IDs, events, schemas, and persistence interfaces.",
5
- "keywords": [
6
- "mu",
7
- "agent",
8
- "core",
9
- "events"
10
- ],
11
- "type": "module",
12
- "main": "./dist/index.js",
13
- "types": "./dist/index.d.ts",
14
- "exports": {
15
- ".": {
16
- "types": "./dist/index.d.ts",
17
- "default": "./dist/index.js"
18
- },
19
- "./browser": {
20
- "types": "./dist/browser/index.d.ts",
21
- "default": "./dist/browser/index.js"
22
- },
23
- "./node": {
24
- "types": "./dist/node/index.d.ts",
25
- "default": "./dist/node/index.js"
26
- }
27
- },
28
- "files": [
29
- "dist/**"
30
- ],
31
- "dependencies": {
32
- "zod": "^4.1.9"
33
- }
2
+ "name": "@femtomc/mu-core",
3
+ "version": "26.2.109",
4
+ "description": "Core primitives for mu: IDs, events, schemas, and persistence interfaces.",
5
+ "keywords": [
6
+ "mu",
7
+ "agent",
8
+ "core",
9
+ "events"
10
+ ],
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./browser": {
20
+ "types": "./dist/browser/index.d.ts",
21
+ "default": "./dist/browser/index.js"
22
+ },
23
+ "./node": {
24
+ "types": "./dist/node/index.d.ts",
25
+ "default": "./dist/node/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/**"
30
+ ],
31
+ "dependencies": {
32
+ "zod": "^4.1.9"
33
+ }
34
34
  }