@femtomc/mu-core 26.2.100 → 26.2.102

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,6 +34,21 @@ 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
+
37
52
  ## Tests / Typecheck
38
53
 
39
54
  From the `mu/` repo root:
package/dist/hud.d.ts ADDED
@@ -0,0 +1,402 @@
1
+ import { z } from "zod";
2
+ export declare const HUD_CONTRACT_VERSION = 1;
3
+ export declare const HudToneSchema: z.ZodEnum<{
4
+ error: "error";
5
+ info: "info";
6
+ success: "success";
7
+ warning: "warning";
8
+ muted: "muted";
9
+ accent: "accent";
10
+ dim: "dim";
11
+ }>;
12
+ export type HudTone = z.infer<typeof HudToneSchema>;
13
+ export declare const HudActionKindSchema: z.ZodEnum<{
14
+ primary: "primary";
15
+ secondary: "secondary";
16
+ danger: "danger";
17
+ }>;
18
+ export type HudActionKind = z.infer<typeof HudActionKindSchema>;
19
+ export declare const HudTextWeightSchema: z.ZodEnum<{
20
+ normal: "normal";
21
+ strong: "strong";
22
+ }>;
23
+ export type HudTextWeight = z.infer<typeof HudTextWeightSchema>;
24
+ export declare const HudTextStyleSchema: z.ZodObject<{
25
+ weight: z.ZodOptional<z.ZodEnum<{
26
+ normal: "normal";
27
+ strong: "strong";
28
+ }>>;
29
+ italic: z.ZodOptional<z.ZodBoolean>;
30
+ code: z.ZodOptional<z.ZodBoolean>;
31
+ }, z.core.$strict>;
32
+ export type HudTextStyle = z.infer<typeof HudTextStyleSchema>;
33
+ export declare const HudChipSchema: z.ZodObject<{
34
+ key: z.ZodString;
35
+ label: z.ZodString;
36
+ tone: z.ZodOptional<z.ZodEnum<{
37
+ error: "error";
38
+ info: "info";
39
+ success: "success";
40
+ warning: "warning";
41
+ muted: "muted";
42
+ accent: "accent";
43
+ dim: "dim";
44
+ }>>;
45
+ style: z.ZodOptional<z.ZodObject<{
46
+ weight: z.ZodOptional<z.ZodEnum<{
47
+ normal: "normal";
48
+ strong: "strong";
49
+ }>>;
50
+ italic: z.ZodOptional<z.ZodBoolean>;
51
+ code: z.ZodOptional<z.ZodBoolean>;
52
+ }, z.core.$strict>>;
53
+ }, z.core.$strict>;
54
+ export type HudChip = z.infer<typeof HudChipSchema>;
55
+ export declare const HudKvItemSchema: z.ZodObject<{
56
+ key: z.ZodString;
57
+ label: z.ZodString;
58
+ value: z.ZodString;
59
+ tone: z.ZodOptional<z.ZodEnum<{
60
+ error: "error";
61
+ info: "info";
62
+ success: "success";
63
+ warning: "warning";
64
+ muted: "muted";
65
+ accent: "accent";
66
+ dim: "dim";
67
+ }>>;
68
+ value_style: z.ZodOptional<z.ZodObject<{
69
+ weight: z.ZodOptional<z.ZodEnum<{
70
+ normal: "normal";
71
+ strong: "strong";
72
+ }>>;
73
+ italic: z.ZodOptional<z.ZodBoolean>;
74
+ code: z.ZodOptional<z.ZodBoolean>;
75
+ }, z.core.$strict>>;
76
+ }, z.core.$strict>;
77
+ export type HudKvItem = z.infer<typeof HudKvItemSchema>;
78
+ export declare const HudChecklistItemSchema: z.ZodObject<{
79
+ id: z.ZodString;
80
+ label: z.ZodString;
81
+ done: z.ZodBoolean;
82
+ style: z.ZodOptional<z.ZodObject<{
83
+ weight: z.ZodOptional<z.ZodEnum<{
84
+ normal: "normal";
85
+ strong: "strong";
86
+ }>>;
87
+ italic: z.ZodOptional<z.ZodBoolean>;
88
+ code: z.ZodOptional<z.ZodBoolean>;
89
+ }, z.core.$strict>>;
90
+ }, z.core.$strict>;
91
+ export type HudChecklistItem = z.infer<typeof HudChecklistItemSchema>;
92
+ export declare const HudSectionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
93
+ kind: z.ZodLiteral<"kv">;
94
+ title: z.ZodOptional<z.ZodString>;
95
+ title_style: z.ZodOptional<z.ZodObject<{
96
+ weight: z.ZodOptional<z.ZodEnum<{
97
+ normal: "normal";
98
+ strong: "strong";
99
+ }>>;
100
+ italic: z.ZodOptional<z.ZodBoolean>;
101
+ code: z.ZodOptional<z.ZodBoolean>;
102
+ }, z.core.$strict>>;
103
+ items: z.ZodDefault<z.ZodArray<z.ZodObject<{
104
+ key: z.ZodString;
105
+ label: z.ZodString;
106
+ value: z.ZodString;
107
+ tone: z.ZodOptional<z.ZodEnum<{
108
+ error: "error";
109
+ info: "info";
110
+ success: "success";
111
+ warning: "warning";
112
+ muted: "muted";
113
+ accent: "accent";
114
+ dim: "dim";
115
+ }>>;
116
+ value_style: z.ZodOptional<z.ZodObject<{
117
+ weight: z.ZodOptional<z.ZodEnum<{
118
+ normal: "normal";
119
+ strong: "strong";
120
+ }>>;
121
+ italic: z.ZodOptional<z.ZodBoolean>;
122
+ code: z.ZodOptional<z.ZodBoolean>;
123
+ }, z.core.$strict>>;
124
+ }, z.core.$strict>>>;
125
+ }, z.core.$strict>, z.ZodObject<{
126
+ kind: z.ZodLiteral<"checklist">;
127
+ title: z.ZodOptional<z.ZodString>;
128
+ title_style: z.ZodOptional<z.ZodObject<{
129
+ weight: z.ZodOptional<z.ZodEnum<{
130
+ normal: "normal";
131
+ strong: "strong";
132
+ }>>;
133
+ italic: z.ZodOptional<z.ZodBoolean>;
134
+ code: z.ZodOptional<z.ZodBoolean>;
135
+ }, z.core.$strict>>;
136
+ items: z.ZodDefault<z.ZodArray<z.ZodObject<{
137
+ id: z.ZodString;
138
+ label: z.ZodString;
139
+ done: z.ZodBoolean;
140
+ style: z.ZodOptional<z.ZodObject<{
141
+ weight: z.ZodOptional<z.ZodEnum<{
142
+ normal: "normal";
143
+ strong: "strong";
144
+ }>>;
145
+ italic: z.ZodOptional<z.ZodBoolean>;
146
+ code: z.ZodOptional<z.ZodBoolean>;
147
+ }, z.core.$strict>>;
148
+ }, z.core.$strict>>>;
149
+ }, z.core.$strict>, z.ZodObject<{
150
+ kind: z.ZodLiteral<"activity">;
151
+ title: z.ZodOptional<z.ZodString>;
152
+ title_style: z.ZodOptional<z.ZodObject<{
153
+ weight: z.ZodOptional<z.ZodEnum<{
154
+ normal: "normal";
155
+ strong: "strong";
156
+ }>>;
157
+ italic: z.ZodOptional<z.ZodBoolean>;
158
+ code: z.ZodOptional<z.ZodBoolean>;
159
+ }, z.core.$strict>>;
160
+ lines: z.ZodDefault<z.ZodArray<z.ZodString>>;
161
+ }, z.core.$strict>, z.ZodObject<{
162
+ kind: z.ZodLiteral<"text">;
163
+ title: z.ZodOptional<z.ZodString>;
164
+ title_style: z.ZodOptional<z.ZodObject<{
165
+ weight: z.ZodOptional<z.ZodEnum<{
166
+ normal: "normal";
167
+ strong: "strong";
168
+ }>>;
169
+ italic: z.ZodOptional<z.ZodBoolean>;
170
+ code: z.ZodOptional<z.ZodBoolean>;
171
+ }, z.core.$strict>>;
172
+ text: z.ZodString;
173
+ tone: z.ZodOptional<z.ZodEnum<{
174
+ error: "error";
175
+ info: "info";
176
+ success: "success";
177
+ warning: "warning";
178
+ muted: "muted";
179
+ accent: "accent";
180
+ dim: "dim";
181
+ }>>;
182
+ style: z.ZodOptional<z.ZodObject<{
183
+ weight: z.ZodOptional<z.ZodEnum<{
184
+ normal: "normal";
185
+ strong: "strong";
186
+ }>>;
187
+ italic: z.ZodOptional<z.ZodBoolean>;
188
+ code: z.ZodOptional<z.ZodBoolean>;
189
+ }, z.core.$strict>>;
190
+ }, z.core.$strict>], "kind">;
191
+ export type HudSection = z.infer<typeof HudSectionSchema>;
192
+ export declare const HudActionSchema: z.ZodObject<{
193
+ id: z.ZodString;
194
+ label: z.ZodString;
195
+ command_text: z.ZodString;
196
+ kind: z.ZodOptional<z.ZodEnum<{
197
+ primary: "primary";
198
+ secondary: "secondary";
199
+ danger: "danger";
200
+ }>>;
201
+ style: z.ZodOptional<z.ZodObject<{
202
+ weight: z.ZodOptional<z.ZodEnum<{
203
+ normal: "normal";
204
+ strong: "strong";
205
+ }>>;
206
+ italic: z.ZodOptional<z.ZodBoolean>;
207
+ code: z.ZodOptional<z.ZodBoolean>;
208
+ }, z.core.$strict>>;
209
+ }, z.core.$strict>;
210
+ export type HudAction = z.infer<typeof HudActionSchema>;
211
+ export declare const HudDocSchema: z.ZodObject<{
212
+ v: z.ZodDefault<z.ZodLiteral<1>>;
213
+ hud_id: z.ZodString;
214
+ title: z.ZodString;
215
+ title_style: z.ZodOptional<z.ZodObject<{
216
+ weight: z.ZodOptional<z.ZodEnum<{
217
+ normal: "normal";
218
+ strong: "strong";
219
+ }>>;
220
+ italic: z.ZodOptional<z.ZodBoolean>;
221
+ code: z.ZodOptional<z.ZodBoolean>;
222
+ }, z.core.$strict>>;
223
+ scope: z.ZodDefault<z.ZodNullable<z.ZodString>>;
224
+ chips: z.ZodDefault<z.ZodArray<z.ZodObject<{
225
+ key: z.ZodString;
226
+ label: z.ZodString;
227
+ tone: z.ZodOptional<z.ZodEnum<{
228
+ error: "error";
229
+ info: "info";
230
+ success: "success";
231
+ warning: "warning";
232
+ muted: "muted";
233
+ accent: "accent";
234
+ dim: "dim";
235
+ }>>;
236
+ style: z.ZodOptional<z.ZodObject<{
237
+ weight: z.ZodOptional<z.ZodEnum<{
238
+ normal: "normal";
239
+ strong: "strong";
240
+ }>>;
241
+ italic: z.ZodOptional<z.ZodBoolean>;
242
+ code: z.ZodOptional<z.ZodBoolean>;
243
+ }, z.core.$strict>>;
244
+ }, z.core.$strict>>>;
245
+ sections: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
246
+ kind: z.ZodLiteral<"kv">;
247
+ title: z.ZodOptional<z.ZodString>;
248
+ title_style: z.ZodOptional<z.ZodObject<{
249
+ weight: z.ZodOptional<z.ZodEnum<{
250
+ normal: "normal";
251
+ strong: "strong";
252
+ }>>;
253
+ italic: z.ZodOptional<z.ZodBoolean>;
254
+ code: z.ZodOptional<z.ZodBoolean>;
255
+ }, z.core.$strict>>;
256
+ items: z.ZodDefault<z.ZodArray<z.ZodObject<{
257
+ key: z.ZodString;
258
+ label: z.ZodString;
259
+ value: z.ZodString;
260
+ tone: z.ZodOptional<z.ZodEnum<{
261
+ error: "error";
262
+ info: "info";
263
+ success: "success";
264
+ warning: "warning";
265
+ muted: "muted";
266
+ accent: "accent";
267
+ dim: "dim";
268
+ }>>;
269
+ value_style: z.ZodOptional<z.ZodObject<{
270
+ weight: z.ZodOptional<z.ZodEnum<{
271
+ normal: "normal";
272
+ strong: "strong";
273
+ }>>;
274
+ italic: z.ZodOptional<z.ZodBoolean>;
275
+ code: z.ZodOptional<z.ZodBoolean>;
276
+ }, z.core.$strict>>;
277
+ }, z.core.$strict>>>;
278
+ }, z.core.$strict>, z.ZodObject<{
279
+ kind: z.ZodLiteral<"checklist">;
280
+ title: z.ZodOptional<z.ZodString>;
281
+ title_style: z.ZodOptional<z.ZodObject<{
282
+ weight: z.ZodOptional<z.ZodEnum<{
283
+ normal: "normal";
284
+ strong: "strong";
285
+ }>>;
286
+ italic: z.ZodOptional<z.ZodBoolean>;
287
+ code: z.ZodOptional<z.ZodBoolean>;
288
+ }, z.core.$strict>>;
289
+ items: z.ZodDefault<z.ZodArray<z.ZodObject<{
290
+ id: z.ZodString;
291
+ label: z.ZodString;
292
+ done: z.ZodBoolean;
293
+ style: z.ZodOptional<z.ZodObject<{
294
+ weight: z.ZodOptional<z.ZodEnum<{
295
+ normal: "normal";
296
+ strong: "strong";
297
+ }>>;
298
+ italic: z.ZodOptional<z.ZodBoolean>;
299
+ code: z.ZodOptional<z.ZodBoolean>;
300
+ }, z.core.$strict>>;
301
+ }, z.core.$strict>>>;
302
+ }, z.core.$strict>, z.ZodObject<{
303
+ kind: z.ZodLiteral<"activity">;
304
+ title: z.ZodOptional<z.ZodString>;
305
+ title_style: z.ZodOptional<z.ZodObject<{
306
+ weight: z.ZodOptional<z.ZodEnum<{
307
+ normal: "normal";
308
+ strong: "strong";
309
+ }>>;
310
+ italic: z.ZodOptional<z.ZodBoolean>;
311
+ code: z.ZodOptional<z.ZodBoolean>;
312
+ }, z.core.$strict>>;
313
+ lines: z.ZodDefault<z.ZodArray<z.ZodString>>;
314
+ }, z.core.$strict>, z.ZodObject<{
315
+ kind: z.ZodLiteral<"text">;
316
+ title: z.ZodOptional<z.ZodString>;
317
+ title_style: z.ZodOptional<z.ZodObject<{
318
+ weight: z.ZodOptional<z.ZodEnum<{
319
+ normal: "normal";
320
+ strong: "strong";
321
+ }>>;
322
+ italic: z.ZodOptional<z.ZodBoolean>;
323
+ code: z.ZodOptional<z.ZodBoolean>;
324
+ }, z.core.$strict>>;
325
+ text: z.ZodString;
326
+ tone: z.ZodOptional<z.ZodEnum<{
327
+ error: "error";
328
+ info: "info";
329
+ success: "success";
330
+ warning: "warning";
331
+ muted: "muted";
332
+ accent: "accent";
333
+ dim: "dim";
334
+ }>>;
335
+ style: z.ZodOptional<z.ZodObject<{
336
+ weight: z.ZodOptional<z.ZodEnum<{
337
+ normal: "normal";
338
+ strong: "strong";
339
+ }>>;
340
+ italic: z.ZodOptional<z.ZodBoolean>;
341
+ code: z.ZodOptional<z.ZodBoolean>;
342
+ }, z.core.$strict>>;
343
+ }, z.core.$strict>], "kind">>>;
344
+ actions: z.ZodDefault<z.ZodArray<z.ZodObject<{
345
+ id: z.ZodString;
346
+ label: z.ZodString;
347
+ command_text: z.ZodString;
348
+ kind: z.ZodOptional<z.ZodEnum<{
349
+ primary: "primary";
350
+ secondary: "secondary";
351
+ danger: "danger";
352
+ }>>;
353
+ style: z.ZodOptional<z.ZodObject<{
354
+ weight: z.ZodOptional<z.ZodEnum<{
355
+ normal: "normal";
356
+ strong: "strong";
357
+ }>>;
358
+ italic: z.ZodOptional<z.ZodBoolean>;
359
+ code: z.ZodOptional<z.ZodBoolean>;
360
+ }, z.core.$strict>>;
361
+ }, z.core.$strict>>>;
362
+ snapshot_compact: z.ZodString;
363
+ snapshot_style: z.ZodOptional<z.ZodObject<{
364
+ weight: z.ZodOptional<z.ZodEnum<{
365
+ normal: "normal";
366
+ strong: "strong";
367
+ }>>;
368
+ italic: z.ZodOptional<z.ZodBoolean>;
369
+ code: z.ZodOptional<z.ZodBoolean>;
370
+ }, z.core.$strict>>;
371
+ snapshot_multiline: z.ZodOptional<z.ZodString>;
372
+ updated_at_ms: z.ZodNumber;
373
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
374
+ }, z.core.$strict>;
375
+ export type HudDoc = z.infer<typeof HudDocSchema>;
376
+ export declare const HUD_STYLE_PRESET_NAMES: readonly ["planning", "subagents"];
377
+ export type HudStylePresetName = (typeof HUD_STYLE_PRESET_NAMES)[number];
378
+ export declare function hudStylePresetWarnings(input: unknown): string[];
379
+ export declare function resolveHudStylePresetName(input: unknown): HudStylePresetName | null;
380
+ export declare function applyHudStylePreset(input: unknown): HudDoc | null;
381
+ export declare function stableSerializeJson(value: unknown, opts?: {
382
+ pretty?: boolean;
383
+ }): string;
384
+ export type HudTextFallbackMode = "compact" | "multiline";
385
+ export declare function serializeHudDocTextFallback(input: unknown, opts?: {
386
+ mode?: HudTextFallbackMode;
387
+ maxChars?: number;
388
+ maxSectionItems?: number;
389
+ maxActions?: number;
390
+ }): string;
391
+ export declare function serializeHudDocsTextFallback(input: unknown, opts?: {
392
+ mode?: HudTextFallbackMode;
393
+ maxChars?: number;
394
+ maxDocs?: number;
395
+ maxSectionItems?: number;
396
+ maxActions?: number;
397
+ }): string;
398
+ export declare function parseHudDoc(input: unknown): HudDoc | null;
399
+ export declare function normalizeHudDocs(input: unknown, opts?: {
400
+ maxDocs?: number;
401
+ }): HudDoc[];
402
+ //# sourceMappingURL=hud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hud.d.ts","sourceRoot":"","sources":["../src/hud.ts"],"names":[],"mappings":"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"}
package/dist/hud.js ADDED
@@ -0,0 +1,488 @@
1
+ import { z } from "zod";
2
+ export const HUD_CONTRACT_VERSION = 1;
3
+ const NonEmptyTextSchema = z.string().trim().min(1);
4
+ export const HudToneSchema = z.enum(["info", "success", "warning", "error", "muted", "accent", "dim"]);
5
+ export const HudActionKindSchema = z.enum(["primary", "secondary", "danger"]);
6
+ export const HudTextWeightSchema = z.enum(["normal", "strong"]);
7
+ export const HudTextStyleSchema = z
8
+ .object({
9
+ weight: HudTextWeightSchema.optional(),
10
+ italic: z.boolean().optional(),
11
+ code: z.boolean().optional(),
12
+ })
13
+ .strict();
14
+ export const HudChipSchema = z
15
+ .object({
16
+ key: NonEmptyTextSchema,
17
+ label: NonEmptyTextSchema,
18
+ tone: HudToneSchema.optional(),
19
+ style: HudTextStyleSchema.optional(),
20
+ })
21
+ .strict();
22
+ export const HudKvItemSchema = z
23
+ .object({
24
+ key: NonEmptyTextSchema,
25
+ label: NonEmptyTextSchema,
26
+ value: NonEmptyTextSchema,
27
+ tone: HudToneSchema.optional(),
28
+ value_style: HudTextStyleSchema.optional(),
29
+ })
30
+ .strict();
31
+ export const HudChecklistItemSchema = z
32
+ .object({
33
+ id: NonEmptyTextSchema,
34
+ label: NonEmptyTextSchema,
35
+ done: z.boolean(),
36
+ style: HudTextStyleSchema.optional(),
37
+ })
38
+ .strict();
39
+ export const HudSectionSchema = z.discriminatedUnion("kind", [
40
+ z
41
+ .object({
42
+ kind: z.literal("kv"),
43
+ title: NonEmptyTextSchema.optional(),
44
+ title_style: HudTextStyleSchema.optional(),
45
+ items: z.array(HudKvItemSchema).default([]),
46
+ })
47
+ .strict(),
48
+ z
49
+ .object({
50
+ kind: z.literal("checklist"),
51
+ title: NonEmptyTextSchema.optional(),
52
+ title_style: HudTextStyleSchema.optional(),
53
+ items: z.array(HudChecklistItemSchema).default([]),
54
+ })
55
+ .strict(),
56
+ z
57
+ .object({
58
+ kind: z.literal("activity"),
59
+ title: NonEmptyTextSchema.optional(),
60
+ title_style: HudTextStyleSchema.optional(),
61
+ lines: z.array(NonEmptyTextSchema).default([]),
62
+ })
63
+ .strict(),
64
+ z
65
+ .object({
66
+ kind: z.literal("text"),
67
+ title: NonEmptyTextSchema.optional(),
68
+ title_style: HudTextStyleSchema.optional(),
69
+ text: NonEmptyTextSchema,
70
+ tone: HudToneSchema.optional(),
71
+ style: HudTextStyleSchema.optional(),
72
+ })
73
+ .strict(),
74
+ ]);
75
+ export const HudActionSchema = z
76
+ .object({
77
+ id: NonEmptyTextSchema,
78
+ label: NonEmptyTextSchema,
79
+ command_text: NonEmptyTextSchema,
80
+ kind: HudActionKindSchema.optional(),
81
+ style: HudTextStyleSchema.optional(),
82
+ })
83
+ .strict();
84
+ export const HudDocSchema = z
85
+ .object({
86
+ v: z.literal(HUD_CONTRACT_VERSION).default(HUD_CONTRACT_VERSION),
87
+ hud_id: NonEmptyTextSchema,
88
+ title: NonEmptyTextSchema,
89
+ title_style: HudTextStyleSchema.optional(),
90
+ scope: NonEmptyTextSchema.nullable().default(null),
91
+ chips: z.array(HudChipSchema).default([]),
92
+ sections: z.array(HudSectionSchema).default([]),
93
+ actions: z.array(HudActionSchema).default([]),
94
+ snapshot_compact: NonEmptyTextSchema,
95
+ snapshot_style: HudTextStyleSchema.optional(),
96
+ snapshot_multiline: NonEmptyTextSchema.optional(),
97
+ updated_at_ms: z.number().int().nonnegative(),
98
+ metadata: z.record(z.string(), z.unknown()).default({}),
99
+ })
100
+ .strict();
101
+ function isPlainObject(value) {
102
+ return typeof value === "object" && value !== null && !Array.isArray(value);
103
+ }
104
+ function parseHudDocCandidate(value) {
105
+ const parsed = HudDocSchema.safeParse(value);
106
+ if (!parsed.success) {
107
+ return null;
108
+ }
109
+ return parsed.data;
110
+ }
111
+ export const HUD_STYLE_PRESET_NAMES = ["planning", "subagents"];
112
+ function isHudStylePresetName(value) {
113
+ return HUD_STYLE_PRESET_NAMES.includes(value);
114
+ }
115
+ function hudStylePresetNameFromMetadata(metadata) {
116
+ const raw = metadata.style_preset;
117
+ if (typeof raw !== "string") {
118
+ return null;
119
+ }
120
+ const normalized = raw.trim().toLowerCase();
121
+ if (!normalized || !isHudStylePresetName(normalized)) {
122
+ return null;
123
+ }
124
+ return normalized;
125
+ }
126
+ function mergeHudTextStyle(preferred, fallback) {
127
+ if (!preferred && !fallback) {
128
+ return undefined;
129
+ }
130
+ const merged = {
131
+ ...(fallback ?? {}),
132
+ ...(preferred ?? {}),
133
+ };
134
+ if (merged.weight === undefined && merged.italic === undefined && merged.code === undefined) {
135
+ return undefined;
136
+ }
137
+ return merged;
138
+ }
139
+ function defaultChipStyle(chip) {
140
+ if (chip.tone === "dim" || chip.tone === "muted") {
141
+ return { weight: "normal" };
142
+ }
143
+ return { weight: "strong" };
144
+ }
145
+ function planningKvValueStyle(item) {
146
+ switch (item.key) {
147
+ case "root":
148
+ case "next":
149
+ case "next_action":
150
+ return { code: true };
151
+ default:
152
+ return undefined;
153
+ }
154
+ }
155
+ function applyPlanningPreset(doc) {
156
+ return {
157
+ ...doc,
158
+ title_style: mergeHudTextStyle(doc.title_style, { weight: "strong" }),
159
+ snapshot_style: mergeHudTextStyle(doc.snapshot_style, { italic: true }),
160
+ chips: doc.chips.map((chip) => ({
161
+ ...chip,
162
+ style: mergeHudTextStyle(chip.style, defaultChipStyle(chip)),
163
+ })),
164
+ sections: doc.sections.map((section) => {
165
+ switch (section.kind) {
166
+ case "kv":
167
+ return {
168
+ ...section,
169
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
170
+ items: section.items.map((item) => ({
171
+ ...item,
172
+ value_style: mergeHudTextStyle(item.value_style, planningKvValueStyle(item)),
173
+ })),
174
+ };
175
+ case "checklist":
176
+ return {
177
+ ...section,
178
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
179
+ };
180
+ case "activity":
181
+ return {
182
+ ...section,
183
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
184
+ };
185
+ case "text":
186
+ return {
187
+ ...section,
188
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
189
+ };
190
+ }
191
+ }),
192
+ actions: doc.actions.map((action) => ({
193
+ ...action,
194
+ style: mergeHudTextStyle(action.style, { italic: true }),
195
+ })),
196
+ };
197
+ }
198
+ function applySubagentsPreset(doc) {
199
+ return {
200
+ ...doc,
201
+ title_style: mergeHudTextStyle(doc.title_style, { weight: "strong" }),
202
+ snapshot_style: mergeHudTextStyle(doc.snapshot_style, { italic: true }),
203
+ chips: doc.chips.map((chip) => ({
204
+ ...chip,
205
+ style: mergeHudTextStyle(chip.style, defaultChipStyle(chip)),
206
+ })),
207
+ sections: doc.sections.map((section) => {
208
+ switch (section.kind) {
209
+ case "kv":
210
+ return {
211
+ ...section,
212
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
213
+ };
214
+ case "checklist":
215
+ return {
216
+ ...section,
217
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
218
+ };
219
+ case "activity":
220
+ return {
221
+ ...section,
222
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
223
+ };
224
+ case "text":
225
+ return {
226
+ ...section,
227
+ title_style: mergeHudTextStyle(section.title_style, { weight: "strong" }),
228
+ };
229
+ }
230
+ }),
231
+ actions: doc.actions.map((action) => ({
232
+ ...action,
233
+ style: mergeHudTextStyle(action.style, { weight: "strong" }),
234
+ })),
235
+ };
236
+ }
237
+ function hasChipKey(doc, key) {
238
+ return doc.chips.some((chip) => chip.key === key);
239
+ }
240
+ function hasSectionKind(doc, kind) {
241
+ return doc.sections.some((section) => section.kind === kind);
242
+ }
243
+ function hudStylePresetWarningsForDoc(doc, preset) {
244
+ const warnings = [];
245
+ switch (preset) {
246
+ case "planning":
247
+ if (doc.hud_id !== "planning") {
248
+ warnings.push(`style_preset=planning expects hud_id=planning (got ${doc.hud_id})`);
249
+ }
250
+ if (!hasChipKey(doc, "phase")) {
251
+ warnings.push("style_preset=planning recommends chip key 'phase'");
252
+ }
253
+ if (!hasSectionKind(doc, "kv")) {
254
+ warnings.push("style_preset=planning recommends a kv section");
255
+ }
256
+ if (!hasSectionKind(doc, "checklist")) {
257
+ warnings.push("style_preset=planning recommends a checklist section");
258
+ }
259
+ break;
260
+ case "subagents":
261
+ if (doc.hud_id !== "subagents") {
262
+ warnings.push(`style_preset=subagents expects hud_id=subagents (got ${doc.hud_id})`);
263
+ }
264
+ if (!hasChipKey(doc, "health")) {
265
+ warnings.push("style_preset=subagents recommends chip key 'health'");
266
+ }
267
+ if (!hasSectionKind(doc, "kv")) {
268
+ warnings.push("style_preset=subagents recommends a kv section");
269
+ }
270
+ if (!hasSectionKind(doc, "activity")) {
271
+ warnings.push("style_preset=subagents recommends an activity section");
272
+ }
273
+ break;
274
+ }
275
+ return warnings;
276
+ }
277
+ export function hudStylePresetWarnings(input) {
278
+ const doc = parseHudDocCandidate(input);
279
+ if (!doc) {
280
+ return [];
281
+ }
282
+ const preset = hudStylePresetNameFromMetadata(doc.metadata);
283
+ if (!preset) {
284
+ return [];
285
+ }
286
+ return hudStylePresetWarningsForDoc(doc, preset);
287
+ }
288
+ export function resolveHudStylePresetName(input) {
289
+ const doc = parseHudDocCandidate(input);
290
+ if (!doc) {
291
+ return null;
292
+ }
293
+ return hudStylePresetNameFromMetadata(doc.metadata);
294
+ }
295
+ export function applyHudStylePreset(input) {
296
+ const doc = parseHudDocCandidate(input);
297
+ if (!doc) {
298
+ return null;
299
+ }
300
+ const preset = hudStylePresetNameFromMetadata(doc.metadata);
301
+ if (!preset) {
302
+ return doc;
303
+ }
304
+ switch (preset) {
305
+ case "planning":
306
+ return applyPlanningPreset(doc);
307
+ case "subagents":
308
+ return applySubagentsPreset(doc);
309
+ }
310
+ }
311
+ function deterministicHudDocChoice(a, b) {
312
+ if (a.updated_at_ms !== b.updated_at_ms) {
313
+ return a.updated_at_ms > b.updated_at_ms ? a : b;
314
+ }
315
+ const left = stableSerializeJson(a);
316
+ const right = stableSerializeJson(b);
317
+ return left <= right ? a : b;
318
+ }
319
+ function hudDocCandidates(input) {
320
+ if (Array.isArray(input)) {
321
+ return input;
322
+ }
323
+ if (isPlainObject(input)) {
324
+ return [input];
325
+ }
326
+ return [];
327
+ }
328
+ function normalizedHudDocLimit(limit) {
329
+ if (typeof limit !== "number" || !Number.isFinite(limit)) {
330
+ return 12;
331
+ }
332
+ const parsed = Math.trunc(limit);
333
+ if (parsed < 1) {
334
+ return 1;
335
+ }
336
+ if (parsed > 64) {
337
+ return 64;
338
+ }
339
+ return parsed;
340
+ }
341
+ function canonicalizeJson(value) {
342
+ if (Array.isArray(value)) {
343
+ return value.map((entry) => canonicalizeJson(entry));
344
+ }
345
+ if (!isPlainObject(value)) {
346
+ return value;
347
+ }
348
+ const out = {};
349
+ for (const key of Object.keys(value).sort()) {
350
+ const nextValue = value[key];
351
+ if (nextValue === undefined) {
352
+ continue;
353
+ }
354
+ out[key] = canonicalizeJson(nextValue);
355
+ }
356
+ return out;
357
+ }
358
+ export function stableSerializeJson(value, opts = {}) {
359
+ const normalized = canonicalizeJson(value);
360
+ const text = JSON.stringify(normalized, null, opts.pretty ? 2 : undefined);
361
+ return text ?? "null";
362
+ }
363
+ function truncateText(value, maxChars) {
364
+ if (maxChars <= 0) {
365
+ return "";
366
+ }
367
+ if (value.length <= maxChars) {
368
+ return value;
369
+ }
370
+ if (maxChars <= 1) {
371
+ return "…";
372
+ }
373
+ return `${value.slice(0, maxChars - 1)}…`;
374
+ }
375
+ function appendOverflowLine(lines, hiddenCount) {
376
+ if (hiddenCount > 0) {
377
+ lines.push(`… (+${hiddenCount} more)`);
378
+ }
379
+ }
380
+ export function serializeHudDocTextFallback(input, opts = {}) {
381
+ const doc = parseHudDocCandidate(input);
382
+ if (!doc) {
383
+ return "";
384
+ }
385
+ const mode = opts.mode ?? "multiline";
386
+ const maxChars = typeof opts.maxChars === "number" && Number.isFinite(opts.maxChars) ? Math.max(32, Math.trunc(opts.maxChars)) : 8_192;
387
+ if (mode === "compact") {
388
+ const compact = `${doc.title} · ${doc.snapshot_compact}`;
389
+ return truncateText(compact, maxChars);
390
+ }
391
+ const maxSectionItems = typeof opts.maxSectionItems === "number" && Number.isFinite(opts.maxSectionItems)
392
+ ? Math.max(1, Math.trunc(opts.maxSectionItems))
393
+ : 8;
394
+ const maxActions = typeof opts.maxActions === "number" && Number.isFinite(opts.maxActions) ? Math.max(1, Math.trunc(opts.maxActions)) : 4;
395
+ const lines = [];
396
+ lines.push(`${doc.title} [${doc.hud_id}]`);
397
+ if (doc.scope) {
398
+ lines.push(`scope: ${doc.scope}`);
399
+ }
400
+ if (doc.chips.length > 0) {
401
+ lines.push(`chips: ${doc.chips.map((chip) => chip.label).join(" · ")}`);
402
+ }
403
+ for (const section of doc.sections) {
404
+ const title = section.title ? ` (${section.title})` : "";
405
+ switch (section.kind) {
406
+ case "kv": {
407
+ lines.push(`section: kv${title}`);
408
+ const visible = section.items.slice(0, maxSectionItems);
409
+ for (const item of visible) {
410
+ lines.push(`- ${item.label}: ${item.value}`);
411
+ }
412
+ appendOverflowLine(lines, section.items.length - visible.length);
413
+ break;
414
+ }
415
+ case "checklist": {
416
+ lines.push(`section: checklist${title}`);
417
+ const visible = section.items.slice(0, maxSectionItems);
418
+ for (const item of visible) {
419
+ lines.push(`- [${item.done ? "x" : " "}] ${item.label}`);
420
+ }
421
+ appendOverflowLine(lines, section.items.length - visible.length);
422
+ break;
423
+ }
424
+ case "activity": {
425
+ lines.push(`section: activity${title}`);
426
+ const visible = section.lines.slice(0, maxSectionItems);
427
+ for (const line of visible) {
428
+ lines.push(`- ${line}`);
429
+ }
430
+ appendOverflowLine(lines, section.lines.length - visible.length);
431
+ break;
432
+ }
433
+ case "text":
434
+ lines.push(`section: text${title}`);
435
+ lines.push(`- ${section.text}`);
436
+ break;
437
+ }
438
+ }
439
+ if (doc.actions.length > 0) {
440
+ lines.push("actions:");
441
+ const visible = doc.actions.slice(0, maxActions);
442
+ for (const action of visible) {
443
+ lines.push(`- ${action.label}: ${action.command_text}`);
444
+ }
445
+ appendOverflowLine(lines, doc.actions.length - visible.length);
446
+ }
447
+ return truncateText(lines.join("\n"), maxChars);
448
+ }
449
+ export function serializeHudDocsTextFallback(input, opts = {}) {
450
+ const docs = normalizeHudDocs(input, { maxDocs: opts.maxDocs });
451
+ if (docs.length === 0) {
452
+ return "";
453
+ }
454
+ const mode = opts.mode ?? "multiline";
455
+ const rendered = docs
456
+ .map((doc) => serializeHudDocTextFallback(doc, {
457
+ mode,
458
+ maxChars: opts.maxChars,
459
+ maxSectionItems: opts.maxSectionItems,
460
+ maxActions: opts.maxActions,
461
+ }))
462
+ .filter((value) => value.length > 0);
463
+ return rendered.join(mode === "compact" ? " | " : "\n\n");
464
+ }
465
+ export function parseHudDoc(input) {
466
+ return parseHudDocCandidate(input);
467
+ }
468
+ export function normalizeHudDocs(input, opts = {}) {
469
+ const maxDocs = normalizedHudDocLimit(opts.maxDocs);
470
+ const byId = new Map();
471
+ for (const candidate of hudDocCandidates(input)) {
472
+ const parsed = parseHudDocCandidate(candidate);
473
+ if (!parsed) {
474
+ continue;
475
+ }
476
+ const current = byId.get(parsed.hud_id);
477
+ if (!current) {
478
+ byId.set(parsed.hud_id, parsed);
479
+ continue;
480
+ }
481
+ byId.set(parsed.hud_id, deterministicHudDocChoice(current, parsed));
482
+ }
483
+ const docs = [...byId.values()].sort((left, right) => left.hud_id.localeCompare(right.hud_id));
484
+ if (docs.length <= maxDocs) {
485
+ return docs;
486
+ }
487
+ return docs.slice(0, maxDocs);
488
+ }
@@ -0,0 +1,36 @@
1
+ import { type HudDoc } from "./hud.js";
2
+ export type HudProviderRuntimeApi<Msg> = {
3
+ dispatch: (message: Msg) => void;
4
+ };
5
+ export type HudProviderReducerResult<State, Effect> = {
6
+ state: State;
7
+ effects?: Effect[];
8
+ };
9
+ export type HudProvider<State, Msg, Effect> = {
10
+ id: string;
11
+ initialState: () => State;
12
+ reduce: (state: State, message: Msg) => HudProviderReducerResult<State, Effect>;
13
+ runEffect?: (effect: Effect, api: HudProviderRuntimeApi<Msg>) => void | Promise<void>;
14
+ view: (state: State) => HudDoc | HudDoc[] | null;
15
+ };
16
+ export type HudRuntimeSnapshot = {
17
+ provider_id: string;
18
+ hud_docs: HudDoc[];
19
+ };
20
+ export type HudRuntimeListener = (snapshot: HudRuntimeSnapshot) => void;
21
+ export type HudRuntimeDispatchResult = {
22
+ provider_id: string;
23
+ messages_processed: number;
24
+ effects_processed: number;
25
+ hud_docs: HudDoc[];
26
+ };
27
+ export declare class HudRuntime {
28
+ #private;
29
+ register<State, Msg, Effect>(provider: HudProvider<State, Msg, Effect>): void;
30
+ unregister(providerId: string): boolean;
31
+ listProviders(): string[];
32
+ subscribe(listener: HudRuntimeListener): () => void;
33
+ snapshot(providerId: string): HudRuntimeSnapshot;
34
+ dispatch<Msg>(providerId: string, message: Msg): Promise<HudRuntimeDispatchResult>;
35
+ }
36
+ //# sourceMappingURL=hud_runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hud_runtime.d.ts","sourceRoot":"","sources":["../src/hud_runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,UAAU,CAAC;AAEzD,MAAM,MAAM,qBAAqB,CAAC,GAAG,IAAI;IACxC,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,wBAAwB,CAAC,KAAK,EAAE,MAAM,IAAI;IACrD,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,IAAI;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,KAAK,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAChF,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;CACjD,CAAC;AASF,MAAM,MAAM,kBAAkB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAAG;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,qBAAa,UAAU;;IAIf,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI;IAe7E,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAQvC,aAAa,IAAI,MAAM,EAAE;IAIzB,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAOnD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB;IAS1C,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAmE/F"}
@@ -0,0 +1,105 @@
1
+ import { normalizeHudDocs } from "./hud.js";
2
+ export class HudRuntime {
3
+ #providers = new Map();
4
+ #listeners = new Set();
5
+ register(provider) {
6
+ const providerId = provider.id.trim();
7
+ if (providerId.length === 0) {
8
+ throw new Error("provider id must be non-empty");
9
+ }
10
+ if (this.#providers.has(providerId)) {
11
+ throw new Error(`provider already registered: ${providerId}`);
12
+ }
13
+ this.#providers.set(providerId, {
14
+ provider: provider,
15
+ state: provider.initialState(),
16
+ });
17
+ this.#emit(providerId);
18
+ }
19
+ unregister(providerId) {
20
+ const normalized = providerId.trim();
21
+ if (normalized.length === 0) {
22
+ return false;
23
+ }
24
+ return this.#providers.delete(normalized);
25
+ }
26
+ listProviders() {
27
+ return [...this.#providers.keys()].sort((a, b) => a.localeCompare(b));
28
+ }
29
+ subscribe(listener) {
30
+ this.#listeners.add(listener);
31
+ return () => {
32
+ this.#listeners.delete(listener);
33
+ };
34
+ }
35
+ snapshot(providerId) {
36
+ const record = this.#provider(providerId);
37
+ const hudDocs = this.#hudDocs(record.provider, record.state);
38
+ return {
39
+ provider_id: providerId,
40
+ hud_docs: hudDocs,
41
+ };
42
+ }
43
+ async dispatch(providerId, message) {
44
+ const record = this.#provider(providerId);
45
+ const provider = record.provider;
46
+ const messageQueue = [message];
47
+ let messagesProcessed = 0;
48
+ let effectsProcessed = 0;
49
+ while (messageQueue.length > 0) {
50
+ const currentMessage = messageQueue.shift();
51
+ messagesProcessed += 1;
52
+ const reduced = provider.reduce(record.state, currentMessage);
53
+ record.state = reduced.state;
54
+ const effects = Array.isArray(reduced.effects) ? reduced.effects : [];
55
+ for (const effect of effects) {
56
+ effectsProcessed += 1;
57
+ if (!provider.runEffect) {
58
+ continue;
59
+ }
60
+ const api = {
61
+ dispatch: (nextMessage) => {
62
+ messageQueue.push(nextMessage);
63
+ },
64
+ };
65
+ await provider.runEffect(effect, api);
66
+ }
67
+ }
68
+ const snapshot = this.#emit(providerId);
69
+ return {
70
+ provider_id: providerId,
71
+ messages_processed: messagesProcessed,
72
+ effects_processed: effectsProcessed,
73
+ hud_docs: snapshot.hud_docs,
74
+ };
75
+ }
76
+ #provider(providerId) {
77
+ const normalized = providerId.trim();
78
+ if (normalized.length === 0) {
79
+ throw new Error("provider id must be non-empty");
80
+ }
81
+ const record = this.#providers.get(normalized);
82
+ if (!record) {
83
+ throw new Error(`unknown provider: ${normalized}`);
84
+ }
85
+ return record;
86
+ }
87
+ #hudDocs(provider, state) {
88
+ const viewed = provider.view(state);
89
+ if (viewed == null) {
90
+ return [];
91
+ }
92
+ return normalizeHudDocs(Array.isArray(viewed) ? viewed : [viewed]);
93
+ }
94
+ #emit(providerId) {
95
+ const record = this.#provider(providerId);
96
+ const snapshot = {
97
+ provider_id: providerId,
98
+ hud_docs: this.#hudDocs(record.provider, record.state),
99
+ };
100
+ for (const listener of this.#listeners) {
101
+ listener(snapshot);
102
+ }
103
+ return snapshot;
104
+ }
105
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./events.js";
2
+ export * from "./hud.js";
3
+ export * from "./hud_runtime.js";
2
4
  export * from "./ids.js";
3
5
  export * from "./persistence.js";
4
6
  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,WAAW,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,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./events.js";
2
+ export * from "./hud.js";
3
+ export * from "./hud_runtime.js";
2
4
  export * from "./ids.js";
3
5
  export * from "./persistence.js";
4
6
  export * from "./spec.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-core",
3
- "version": "26.2.100",
3
+ "version": "26.2.102",
4
4
  "description": "Core primitives for mu: IDs, events, schemas, and persistence interfaces.",
5
5
  "keywords": [
6
6
  "mu",