@femtomc/mu-core 26.2.106 → 26.2.108
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 +17 -10
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/ui.d.ts +298 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +332 -0
- package/package.json +32 -32
- package/dist/hud.d.ts +0 -402
- package/dist/hud.d.ts.map +0 -1
- package/dist/hud.js +0 -488
- package/dist/hud_runtime.d.ts +0 -36
- package/dist/hud_runtime.d.ts.map +0 -1
- package/dist/hud_runtime.js +0 -105
package/dist/ui.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const UiToneSchema = z.enum(["info", "success", "warning", "error", "muted", "accent", "dim"]);
|
|
3
|
+
export const UI_CONTRACT_VERSION = 1;
|
|
4
|
+
const UI_DOC_TITLE_MAX_LENGTH = 256;
|
|
5
|
+
const UI_DOC_SUBTITLE_MAX_LENGTH = 512;
|
|
6
|
+
const UI_DOC_SUMMARY_MAX_LENGTH = 1024;
|
|
7
|
+
const UI_COMPONENT_LIMIT = 64;
|
|
8
|
+
const UI_TEXT_COMPONENT_MAX_LENGTH = 2048;
|
|
9
|
+
const UI_LIST_ITEM_LIMIT = 32;
|
|
10
|
+
const UI_LIST_ITEM_DETAIL_MAX_LENGTH = 1024;
|
|
11
|
+
const UI_KEY_VALUE_ROW_LIMIT = 32;
|
|
12
|
+
const UI_KEY_MAX_LENGTH = 64;
|
|
13
|
+
const UI_VALUE_MAX_LENGTH = 256;
|
|
14
|
+
const UI_ACTION_LIMIT = 32;
|
|
15
|
+
const UI_ACTION_LABEL_MAX_LENGTH = 128;
|
|
16
|
+
const UI_ACTION_DESCRIPTION_MAX_LENGTH = 512;
|
|
17
|
+
const UI_CALLBACK_TOKEN_MAX_LENGTH = 128;
|
|
18
|
+
const UI_REVISION_ID_MAX_LENGTH = 64;
|
|
19
|
+
const NonEmptyText = (max) => z.string().trim().min(1).max(max);
|
|
20
|
+
const NonEmptyId = z.string().trim().min(1).max(64);
|
|
21
|
+
const CallbackTokenSchema = z.string().trim().min(1).max(UI_CALLBACK_TOKEN_MAX_LENGTH);
|
|
22
|
+
export const UiListItemSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
id: NonEmptyId,
|
|
25
|
+
label: NonEmptyText(UI_ACTION_LABEL_MAX_LENGTH),
|
|
26
|
+
detail: NonEmptyText(UI_LIST_ITEM_DETAIL_MAX_LENGTH).optional(),
|
|
27
|
+
tone: UiToneSchema.optional(),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
export const UiKeyValueRowSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
key: NonEmptyText(UI_KEY_MAX_LENGTH),
|
|
33
|
+
value: NonEmptyText(UI_VALUE_MAX_LENGTH),
|
|
34
|
+
tone: UiToneSchema.optional(),
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
export const UiComponentTextSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
kind: z.literal("text"),
|
|
40
|
+
id: NonEmptyId,
|
|
41
|
+
text: NonEmptyText(UI_TEXT_COMPONENT_MAX_LENGTH),
|
|
42
|
+
tone: UiToneSchema.optional(),
|
|
43
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
export const UiComponentListSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
kind: z.literal("list"),
|
|
49
|
+
id: NonEmptyId,
|
|
50
|
+
title: NonEmptyText(UI_DOC_SUBTITLE_MAX_LENGTH).optional(),
|
|
51
|
+
items: z.array(UiListItemSchema).min(1).max(UI_LIST_ITEM_LIMIT),
|
|
52
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
55
|
+
export const UiComponentKeyValueSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
kind: z.literal("key_value"),
|
|
58
|
+
id: NonEmptyId,
|
|
59
|
+
title: NonEmptyText(UI_DOC_SUBTITLE_MAX_LENGTH).optional(),
|
|
60
|
+
rows: z.array(UiKeyValueRowSchema).min(1).max(UI_KEY_VALUE_ROW_LIMIT),
|
|
61
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
62
|
+
})
|
|
63
|
+
.strict();
|
|
64
|
+
export const UiComponentDividerSchema = z
|
|
65
|
+
.object({
|
|
66
|
+
kind: z.literal("divider"),
|
|
67
|
+
id: NonEmptyId,
|
|
68
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
71
|
+
export const UiComponentSchema = z.discriminatedUnion("kind", [
|
|
72
|
+
UiComponentTextSchema,
|
|
73
|
+
UiComponentListSchema,
|
|
74
|
+
UiComponentKeyValueSchema,
|
|
75
|
+
UiComponentDividerSchema,
|
|
76
|
+
]);
|
|
77
|
+
export const UiActionKindSchema = z.enum(["primary", "secondary", "danger", "link"]);
|
|
78
|
+
export const UiActionSchema = z
|
|
79
|
+
.object({
|
|
80
|
+
id: NonEmptyId,
|
|
81
|
+
label: NonEmptyText(UI_ACTION_LABEL_MAX_LENGTH),
|
|
82
|
+
kind: UiActionKindSchema.optional(),
|
|
83
|
+
description: NonEmptyText(UI_ACTION_DESCRIPTION_MAX_LENGTH).optional(),
|
|
84
|
+
component_id: NonEmptyId.optional(),
|
|
85
|
+
callback_token: CallbackTokenSchema.optional(),
|
|
86
|
+
payload: z.record(z.string(), z.unknown()).default({}),
|
|
87
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
export const UiRevisionSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
id: NonEmptyText(UI_REVISION_ID_MAX_LENGTH),
|
|
93
|
+
version: z.number().int().nonnegative(),
|
|
94
|
+
})
|
|
95
|
+
.strict();
|
|
96
|
+
export const UiDocSchema = z
|
|
97
|
+
.object({
|
|
98
|
+
v: z.literal(UI_CONTRACT_VERSION).default(UI_CONTRACT_VERSION),
|
|
99
|
+
ui_id: NonEmptyId,
|
|
100
|
+
title: NonEmptyText(UI_DOC_TITLE_MAX_LENGTH),
|
|
101
|
+
summary: NonEmptyText(UI_DOC_SUMMARY_MAX_LENGTH).optional(),
|
|
102
|
+
components: z.array(UiComponentSchema).min(1).max(UI_COMPONENT_LIMIT),
|
|
103
|
+
actions: z.array(UiActionSchema).max(UI_ACTION_LIMIT).default([]),
|
|
104
|
+
revision: UiRevisionSchema,
|
|
105
|
+
updated_at_ms: z.number().int().nonnegative(),
|
|
106
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
107
|
+
})
|
|
108
|
+
.strict();
|
|
109
|
+
export const UI_STATUS_PROFILE_NAMES = ["planning", "subagents", "control-flow", "model-routing"];
|
|
110
|
+
export const UiEventSchema = z
|
|
111
|
+
.object({
|
|
112
|
+
ui_id: NonEmptyId,
|
|
113
|
+
action_id: NonEmptyId,
|
|
114
|
+
component_id: NonEmptyId.optional().nullable(),
|
|
115
|
+
revision: UiRevisionSchema,
|
|
116
|
+
callback_token: CallbackTokenSchema.optional(),
|
|
117
|
+
payload: z.record(z.string(), z.unknown()).default({}),
|
|
118
|
+
created_at_ms: z.number().int().nonnegative(),
|
|
119
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
120
|
+
})
|
|
121
|
+
.strict();
|
|
122
|
+
function isPlainObject(value) {
|
|
123
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
124
|
+
}
|
|
125
|
+
function parseUiDocCandidate(value) {
|
|
126
|
+
const parsed = UiDocSchema.safeParse(value);
|
|
127
|
+
if (!parsed.success) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return parsed.data;
|
|
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
|
+
function isUiStatusProfileName(value) {
|
|
146
|
+
return UI_STATUS_PROFILE_NAMES.includes(value);
|
|
147
|
+
}
|
|
148
|
+
function isUiProfileVariant(value) {
|
|
149
|
+
return UI_PROFILE_VARIANTS.includes(value);
|
|
150
|
+
}
|
|
151
|
+
function uiProfileMetadata(doc) {
|
|
152
|
+
const raw = doc.metadata.profile;
|
|
153
|
+
if (!isPlainObject(raw)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return raw;
|
|
157
|
+
}
|
|
158
|
+
function uiStatusProfileNameFromDoc(doc) {
|
|
159
|
+
const profile = uiProfileMetadata(doc);
|
|
160
|
+
if (!profile) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const raw = profile.id;
|
|
164
|
+
if (typeof raw !== "string") {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const normalized = raw.trim().toLowerCase();
|
|
168
|
+
if (!normalized || !isUiStatusProfileName(normalized)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return normalized;
|
|
172
|
+
}
|
|
173
|
+
function hasUiComponentKind(doc, kind) {
|
|
174
|
+
return doc.components.some((component) => component.kind === kind);
|
|
175
|
+
}
|
|
176
|
+
function hasProfileSnapshotCompact(profile) {
|
|
177
|
+
const snapshot = profile.snapshot;
|
|
178
|
+
if (!isPlainObject(snapshot)) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const compact = snapshot.compact;
|
|
182
|
+
return typeof compact === "string" && compact.trim().length > 0;
|
|
183
|
+
}
|
|
184
|
+
function uiDocCandidates(input) {
|
|
185
|
+
if (Array.isArray(input)) {
|
|
186
|
+
return input;
|
|
187
|
+
}
|
|
188
|
+
if (isPlainObject(input)) {
|
|
189
|
+
return [input];
|
|
190
|
+
}
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
function normalizedUiDocLimit(limit) {
|
|
194
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
195
|
+
return 12;
|
|
196
|
+
}
|
|
197
|
+
const parsed = Math.trunc(limit);
|
|
198
|
+
if (parsed < 1) {
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
if (parsed > 64) {
|
|
202
|
+
return 64;
|
|
203
|
+
}
|
|
204
|
+
return parsed;
|
|
205
|
+
}
|
|
206
|
+
function canonicalizeJson(value) {
|
|
207
|
+
if (Array.isArray(value)) {
|
|
208
|
+
return value.map((entry) => canonicalizeJson(entry));
|
|
209
|
+
}
|
|
210
|
+
if (!isPlainObject(value)) {
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
const out = {};
|
|
214
|
+
for (const key of Object.keys(value).sort()) {
|
|
215
|
+
const nextValue = value[key];
|
|
216
|
+
if (nextValue === undefined) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
out[key] = canonicalizeJson(nextValue);
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
export function stableSerializeJson(value, opts = {}) {
|
|
224
|
+
const normalized = canonicalizeJson(value);
|
|
225
|
+
const text = JSON.stringify(normalized, null, opts.pretty ? 2 : undefined);
|
|
226
|
+
return text ?? "null";
|
|
227
|
+
}
|
|
228
|
+
function deterministicUiDocChoice(left, right) {
|
|
229
|
+
if (left.revision.version !== right.revision.version) {
|
|
230
|
+
return left.revision.version > right.revision.version ? left : right;
|
|
231
|
+
}
|
|
232
|
+
if (left.updated_at_ms !== right.updated_at_ms) {
|
|
233
|
+
return left.updated_at_ms > right.updated_at_ms ? left : right;
|
|
234
|
+
}
|
|
235
|
+
const leftText = stableSerializeJson(left);
|
|
236
|
+
const rightText = stableSerializeJson(right);
|
|
237
|
+
return leftText <= rightText ? left : right;
|
|
238
|
+
}
|
|
239
|
+
export function parseUiDoc(input) {
|
|
240
|
+
return parseUiDocCandidate(input);
|
|
241
|
+
}
|
|
242
|
+
export function resolveUiStatusProfileName(input) {
|
|
243
|
+
const doc = parseUiDocCandidate(input);
|
|
244
|
+
if (!doc) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return uiStatusProfileNameFromDoc(doc);
|
|
248
|
+
}
|
|
249
|
+
export function uiStatusProfileWarnings(input) {
|
|
250
|
+
const doc = parseUiDocCandidate(input);
|
|
251
|
+
if (!doc) {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
const profileName = uiStatusProfileNameFromDoc(doc);
|
|
255
|
+
if (!profileName) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
const warnings = [];
|
|
259
|
+
const profile = uiProfileMetadata(doc);
|
|
260
|
+
if (!profile) {
|
|
261
|
+
return warnings;
|
|
262
|
+
}
|
|
263
|
+
const rawVariant = typeof profile.variant === "string" ? profile.variant.trim().toLowerCase() : "";
|
|
264
|
+
let variant = "status";
|
|
265
|
+
if (rawVariant.length > 0) {
|
|
266
|
+
if (isUiProfileVariant(rawVariant)) {
|
|
267
|
+
variant = rawVariant;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
warnings.push(`profile.id=${profileName} has unsupported metadata.profile.variant=${rawVariant}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (variant !== "status") {
|
|
274
|
+
warnings.push(`profile.id=${profileName} status validation expects metadata.profile.variant=status (got ${variant})`);
|
|
275
|
+
}
|
|
276
|
+
const expectedUiId = UI_STATUS_PROFILE_UI_IDS[profileName];
|
|
277
|
+
if (doc.ui_id !== expectedUiId) {
|
|
278
|
+
warnings.push(`profile.id=${profileName} expects ui_id=${expectedUiId} (got ${doc.ui_id})`);
|
|
279
|
+
}
|
|
280
|
+
if (!doc.summary) {
|
|
281
|
+
warnings.push(`profile.id=${profileName} recommends summary for deterministic status fallback`);
|
|
282
|
+
}
|
|
283
|
+
if (!hasProfileSnapshotCompact(profile)) {
|
|
284
|
+
warnings.push(`profile.id=${profileName} recommends metadata.profile.snapshot.compact`);
|
|
285
|
+
}
|
|
286
|
+
if (doc.actions.length > 0) {
|
|
287
|
+
warnings.push(`profile.id=${profileName} status docs should omit actions (got ${doc.actions.length})`);
|
|
288
|
+
}
|
|
289
|
+
for (const kind of UI_STATUS_PROFILE_COMPONENT_RECOMMENDATIONS[profileName]) {
|
|
290
|
+
if (!hasUiComponentKind(doc, kind)) {
|
|
291
|
+
warnings.push(`profile.id=${profileName} recommends a ${kind} component`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return warnings;
|
|
295
|
+
}
|
|
296
|
+
export function normalizeUiDocs(input, opts = {}) {
|
|
297
|
+
const maxDocs = normalizedUiDocLimit(opts.maxDocs);
|
|
298
|
+
const byId = new Map();
|
|
299
|
+
for (const candidate of uiDocCandidates(input)) {
|
|
300
|
+
const parsed = parseUiDocCandidate(candidate);
|
|
301
|
+
if (!parsed) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const current = byId.get(parsed.ui_id);
|
|
305
|
+
if (!current) {
|
|
306
|
+
byId.set(parsed.ui_id, parsed);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
byId.set(parsed.ui_id, deterministicUiDocChoice(current, parsed));
|
|
310
|
+
}
|
|
311
|
+
const docs = [...byId.values()].sort((a, b) => a.ui_id.localeCompare(b.ui_id));
|
|
312
|
+
if (docs.length <= maxDocs) {
|
|
313
|
+
return docs;
|
|
314
|
+
}
|
|
315
|
+
return docs.slice(0, maxDocs);
|
|
316
|
+
}
|
|
317
|
+
export function uiDocRevisionConflict(left, right) {
|
|
318
|
+
if (left.ui_id !== right.ui_id) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
if (left.revision.version !== right.revision.version) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
return stableSerializeJson(left) !== stableSerializeJson(right);
|
|
325
|
+
}
|
|
326
|
+
export function parseUiEvent(input) {
|
|
327
|
+
const parsed = UiEventSchema.safeParse(input);
|
|
328
|
+
if (!parsed.success) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return parsed.data;
|
|
332
|
+
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
"name": "@femtomc/mu-core",
|
|
3
|
+
"version": "26.2.108",
|
|
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
|
}
|