@femtomc/mu-server 26.2.101 → 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
@@ -186,7 +186,9 @@ Operational fallbacks:
186
186
  - Missing Slack/Telegram bot tokens surface capability reason codes (`*_bot_token_missing`) and retry behavior.
187
187
 
188
188
  Server channel renderers consume canonical `hud_docs` metadata (`HudDoc`) for Slack/Telegram HUD
189
- rendering + actions. New features should extend the shared HUD contract path instead of bespoke
189
+ rendering + actions. Optional HUD presentation hints (`title_style`, `snapshot_style`, chip/item styles)
190
+ and metadata presets (`metadata.style_preset`) may be used by richer renderers and safely ignored by
191
+ plain-text channels. New features should extend the shared HUD contract path instead of bespoke
190
192
  channel-specific HUD payload formats.
191
193
 
192
194
  ## Running the Server
@@ -1,4 +1,4 @@
1
- import { normalizeHudDocs } from "@femtomc/mu-core";
1
+ import { applyHudStylePreset, normalizeHudDocs } from "@femtomc/mu-core";
2
2
  import { ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneRuntime, buildSlackHudActionId, getControlPlanePaths, TelegramControlPlaneAdapterSpec, } from "@femtomc/mu-control-plane";
3
3
  import { DEFAULT_MU_CONFIG } from "./config.js";
4
4
  import { buildMessagingOperatorRuntime, createOutboxDrainLoop } from "./control_plane_bootstrap_helpers.js";
@@ -148,6 +148,9 @@ const SLACK_ACTIONS_MAX_PER_BLOCK = 5;
148
148
  const SLACK_ACTIONS_MAX_TOTAL = 20;
149
149
  const SLACK_DOCS_MAX = 3;
150
150
  const SLACK_SECTION_LINE_MAX = 8;
151
+ function hudDocsForPresentation(input, maxDocs) {
152
+ return normalizeHudDocs(input, { maxDocs }).map((doc) => applyHudStylePreset(doc) ?? doc);
153
+ }
151
154
  function truncateSlackText(text, maxLen = SLACK_BLOCK_TEXT_MAX_LEN) {
152
155
  if (text.length <= maxLen) {
153
156
  return text;
@@ -176,11 +179,33 @@ function hudTonePrefix(tone) {
176
179
  return "ℹ️";
177
180
  }
178
181
  }
182
+ function applySlackHudTextStyle(text, style, opts = {}) {
183
+ const weight = style?.weight ?? opts.defaultWeight;
184
+ const italic = style?.italic ?? opts.defaultItalic ?? false;
185
+ const code = style?.code ?? opts.defaultCode ?? false;
186
+ let out = text;
187
+ if (code) {
188
+ out = `\`${out}\``;
189
+ }
190
+ if (italic) {
191
+ out = `_${out}_`;
192
+ }
193
+ if (weight === "strong") {
194
+ out = `*${out}*`;
195
+ }
196
+ return out;
197
+ }
198
+ function stripSlackMrkdwn(text) {
199
+ return text.replace(/[*_`~]/g, "");
200
+ }
179
201
  function hudDocSectionLines(doc) {
180
202
  const lines = [];
181
203
  const chipsLine = doc.chips
182
204
  .slice(0, 8)
183
- .map((chip) => `${hudTonePrefix(chip.tone)} *${chip.label}*`)
205
+ .map((chip) => {
206
+ const label = applySlackHudTextStyle(chip.label, chip.style, { defaultWeight: "strong" });
207
+ return `${hudTonePrefix(chip.tone)} ${label}`;
208
+ })
184
209
  .join(" · ");
185
210
  if (chipsLine.length > 0) {
186
211
  lines.push(chipsLine);
@@ -188,25 +213,28 @@ function hudDocSectionLines(doc) {
188
213
  for (const section of doc.sections) {
189
214
  switch (section.kind) {
190
215
  case "kv": {
191
- const title = section.title ? `*${section.title}*` : "*Details*";
192
- const items = section.items.slice(0, SLACK_SECTION_LINE_MAX).map((item) => `• *${item.label}:* ${item.value}`);
216
+ const title = applySlackHudTextStyle(section.title ?? "Details", section.title_style, { defaultWeight: "strong" });
217
+ const items = section.items.slice(0, SLACK_SECTION_LINE_MAX).map((item) => {
218
+ const value = applySlackHudTextStyle(item.value, item.value_style);
219
+ return `• *${item.label}:* ${value}`;
220
+ });
193
221
  if (items.length > 0) {
194
222
  lines.push([title, ...items].join("\n"));
195
223
  }
196
224
  break;
197
225
  }
198
226
  case "checklist": {
199
- const title = section.title ? `*${section.title}*` : "*Checklist*";
227
+ const title = applySlackHudTextStyle(section.title ?? "Checklist", section.title_style, { defaultWeight: "strong" });
200
228
  const items = section.items
201
229
  .slice(0, SLACK_SECTION_LINE_MAX)
202
- .map((item) => `${item.done ? "✅" : "⬜"} ${item.label}`);
230
+ .map((item) => `${item.done ? "✅" : "⬜"} ${applySlackHudTextStyle(item.label, item.style)}`);
203
231
  if (items.length > 0) {
204
232
  lines.push([title, ...items].join("\n"));
205
233
  }
206
234
  break;
207
235
  }
208
236
  case "activity": {
209
- const title = section.title ? `*${section.title}*` : "*Activity*";
237
+ const title = applySlackHudTextStyle(section.title ?? "Activity", section.title_style, { defaultWeight: "strong" });
210
238
  const items = section.lines.slice(0, SLACK_SECTION_LINE_MAX).map((line) => `• ${line}`);
211
239
  if (items.length > 0) {
212
240
  lines.push([title, ...items].join("\n"));
@@ -214,14 +242,18 @@ function hudDocSectionLines(doc) {
214
242
  break;
215
243
  }
216
244
  case "text": {
217
- const title = section.title ? `*${section.title}*\n` : "";
218
- lines.push(`${title}${section.text}`);
245
+ const title = section.title
246
+ ? `${applySlackHudTextStyle(section.title, section.title_style, { defaultWeight: "strong" })}\n`
247
+ : "";
248
+ const text = applySlackHudTextStyle(section.text, section.style);
249
+ lines.push(`${title}${text}`);
219
250
  break;
220
251
  }
221
252
  }
222
253
  }
223
254
  if (lines.length === 0) {
224
- lines.push(`*Snapshot*\n${doc.snapshot_compact}`);
255
+ const snapshot = applySlackHudTextStyle(doc.snapshot_compact, doc.snapshot_style);
256
+ lines.push(`${applySlackHudTextStyle("Snapshot", undefined, { defaultWeight: "strong" })}\n${snapshot}`);
225
257
  }
226
258
  return lines;
227
259
  }
@@ -257,7 +289,7 @@ export function splitSlackMessageText(text, maxLen = SLACK_MESSAGE_MAX_LEN) {
257
289
  return chunks;
258
290
  }
259
291
  function slackBlocksForOutboxRecord(record, body) {
260
- const hudDocs = normalizeHudDocs(record.envelope.metadata?.hud_docs, { maxDocs: SLACK_DOCS_MAX });
292
+ const hudDocs = hudDocsForPresentation(record.envelope.metadata?.hud_docs, SLACK_DOCS_MAX);
261
293
  if (hudDocs.length === 0) {
262
294
  return undefined;
263
295
  }
@@ -270,9 +302,10 @@ function slackBlocksForOutboxRecord(record, body) {
270
302
  if (blocks.length >= SLACK_BLOCKS_MAX) {
271
303
  break;
272
304
  }
305
+ const styledTitle = applySlackHudTextStyle(doc.title, doc.title_style, { defaultWeight: "strong" });
273
306
  blocks.push({
274
307
  type: "context",
275
- elements: [{ type: "mrkdwn", text: truncateSlackText(`*HUD · ${doc.title}*`) }],
308
+ elements: [{ type: "mrkdwn", text: truncateSlackText(`*HUD* · ${styledTitle}`) }],
276
309
  });
277
310
  for (const line of hudDocSectionLines(doc)) {
278
311
  if (blocks.length >= SLACK_BLOCKS_MAX) {
@@ -321,7 +354,7 @@ function utf8ByteLength(value) {
321
354
  return new TextEncoder().encode(value).length;
322
355
  }
323
356
  function telegramTextForOutboxRecord(record, body) {
324
- const hudDocs = normalizeHudDocs(record.envelope.metadata?.hud_docs, { maxDocs: TELEGRAM_DOCS_MAX });
357
+ const hudDocs = hudDocsForPresentation(record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
325
358
  if (hudDocs.length === 0) {
326
359
  return body;
327
360
  }
@@ -329,13 +362,13 @@ function telegramTextForOutboxRecord(record, body) {
329
362
  for (const doc of hudDocs) {
330
363
  lines.push("", `HUD · ${doc.title}`);
331
364
  for (const sectionLine of hudDocSectionLines(doc)) {
332
- lines.push(sectionLine.replace(/\*/g, ""));
365
+ lines.push(stripSlackMrkdwn(sectionLine));
333
366
  }
334
367
  }
335
368
  return lines.join("\n").trim();
336
369
  }
337
370
  async function compileTelegramHudActions(opts) {
338
- const hudDocs = normalizeHudDocs(opts.record.envelope.metadata?.hud_docs, { maxDocs: TELEGRAM_DOCS_MAX });
371
+ const hudDocs = hudDocsForPresentation(opts.record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
339
372
  if (hudDocs.length === 0) {
340
373
  return { overflowText: "" };
341
374
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-server",
3
- "version": "26.2.101",
3
+ "version": "26.2.102",
4
4
  "description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
5
5
  "keywords": [
6
6
  "mu",
@@ -30,8 +30,8 @@
30
30
  "start": "bun run dist/cli.js"
31
31
  },
32
32
  "dependencies": {
33
- "@femtomc/mu-agent": "26.2.101",
34
- "@femtomc/mu-control-plane": "26.2.101",
35
- "@femtomc/mu-core": "26.2.101"
33
+ "@femtomc/mu-agent": "26.2.102",
34
+ "@femtomc/mu-control-plane": "26.2.102",
35
+ "@femtomc/mu-core": "26.2.102"
36
36
  }
37
37
  }