@femtomc/mu-server 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 +1 -5
- package/dist/api/control_plane.js +5 -4
- package/dist/control_plane.js +48 -213
- package/package.json +35 -35
package/README.md
CHANGED
|
@@ -194,11 +194,7 @@ Operational fallbacks:
|
|
|
194
194
|
- `telegram_reply_to_message_id` metadata anchors replies when parseable.
|
|
195
195
|
- Missing Slack/Telegram bot tokens surface capability reason codes (`*_bot_token_missing`) and retry behavior.
|
|
196
196
|
|
|
197
|
-
Server channel renderers consume canonical `
|
|
198
|
-
rendering + actions. Optional HUD presentation hints (`title_style`, `snapshot_style`, chip/item styles)
|
|
199
|
-
and metadata presets (`metadata.style_preset`) may be used by richer renderers and safely ignored by
|
|
200
|
-
plain-text channels. New features should extend the shared HUD contract path instead of bespoke
|
|
201
|
-
channel-specific HUD payload formats.
|
|
197
|
+
Server channel renderers consume canonical `ui_docs` metadata (`UiDoc`) for Slack/Telegram/Discord/Neovim delivery. Interactive actions use callback tokens (`mu-ui:*`) with deterministic text fallbacks when tokens cannot be issued or rendered.
|
|
202
198
|
|
|
203
199
|
## Running the Server
|
|
204
200
|
|
|
@@ -99,6 +99,7 @@ const TEXT_ONLY_UI_COMPONENT_SUPPORT = {
|
|
|
99
99
|
key_value: false,
|
|
100
100
|
divider: false,
|
|
101
101
|
};
|
|
102
|
+
const STATUS_PROFILE_ACTIONS_FALLBACK_REASON = "status_profile_actions_degrade_to_text";
|
|
102
103
|
const CHANNEL_UI_CAPABILITIES = {
|
|
103
104
|
slack: {
|
|
104
105
|
supported: true,
|
|
@@ -106,7 +107,7 @@ const CHANNEL_UI_CAPABILITIES = {
|
|
|
106
107
|
components: UI_COMPONENT_SUPPORT,
|
|
107
108
|
actions: {
|
|
108
109
|
supported: true,
|
|
109
|
-
reason:
|
|
110
|
+
reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
|
|
110
111
|
},
|
|
111
112
|
},
|
|
112
113
|
discord: {
|
|
@@ -115,7 +116,7 @@ const CHANNEL_UI_CAPABILITIES = {
|
|
|
115
116
|
components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
|
|
116
117
|
actions: {
|
|
117
118
|
supported: true,
|
|
118
|
-
reason:
|
|
119
|
+
reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
|
|
119
120
|
},
|
|
120
121
|
},
|
|
121
122
|
telegram: {
|
|
@@ -124,7 +125,7 @@ const CHANNEL_UI_CAPABILITIES = {
|
|
|
124
125
|
components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
|
|
125
126
|
actions: {
|
|
126
127
|
supported: true,
|
|
127
|
-
reason:
|
|
128
|
+
reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
|
|
128
129
|
},
|
|
129
130
|
},
|
|
130
131
|
neovim: {
|
|
@@ -133,7 +134,7 @@ const CHANNEL_UI_CAPABILITIES = {
|
|
|
133
134
|
components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
|
|
134
135
|
actions: {
|
|
135
136
|
supported: true,
|
|
136
|
-
reason:
|
|
137
|
+
reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
|
|
137
138
|
},
|
|
138
139
|
},
|
|
139
140
|
terminal: {
|
package/dist/control_plane.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneRuntime, buildSanitizedUiEventForAction,
|
|
1
|
+
import { normalizeUiDocs, resolveUiStatusProfileName, } from "@femtomc/mu-core";
|
|
2
|
+
import { ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneRuntime, buildSanitizedUiEventForAction, buildSlackUiActionId, getControlPlanePaths, issueUiDocActionPayloads, TelegramControlPlaneAdapterSpec, uiActionPayloadContextFromOutboxRecord, uiDocActionPayloadKey, UiCallbackTokenStore, } 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";
|
|
5
5
|
import { buildWakeOutboundEnvelope, resolveWakeFanoutCapability, wakeDeliveryMetadataFromOutboxRecord, wakeDispatchReasonCode, wakeFanoutDedupeKey, } from "./control_plane_wake_delivery.js";
|
|
@@ -147,9 +147,7 @@ const SLACK_BLOCKS_MAX = 50;
|
|
|
147
147
|
const SLACK_ACTIONS_MAX_PER_BLOCK = 5;
|
|
148
148
|
const SLACK_ACTIONS_MAX_TOTAL = 20;
|
|
149
149
|
const SLACK_ACTION_VALUE_MAX_CHARS = 2_000;
|
|
150
|
-
const SLACK_UI_EVENT_ACTION_ID =
|
|
151
|
-
const SLACK_DOCS_MAX = 3;
|
|
152
|
-
const SLACK_SECTION_LINE_MAX = 8;
|
|
150
|
+
const SLACK_UI_EVENT_ACTION_ID = buildSlackUiActionId("ui_event");
|
|
153
151
|
const UI_DOCS_MAX = 3;
|
|
154
152
|
const UI_CALLBACK_TOKEN_TTL_MS = 15 * 60_000;
|
|
155
153
|
export const UI_COMPONENT_SUPPORT = {
|
|
@@ -159,9 +157,6 @@ export const UI_COMPONENT_SUPPORT = {
|
|
|
159
157
|
divider: true,
|
|
160
158
|
};
|
|
161
159
|
export const UI_ACTIONS_UNSUPPORTED_REASON = "ui_actions_not_implemented";
|
|
162
|
-
function hudDocsForPresentation(input, maxDocs) {
|
|
163
|
-
return normalizeHudDocs(input, { maxDocs }).map((doc) => applyHudStylePreset(doc) ?? doc);
|
|
164
|
-
}
|
|
165
160
|
function truncateSlackText(text, maxLen = SLACK_BLOCK_TEXT_MAX_LEN) {
|
|
166
161
|
if (text.length <= maxLen) {
|
|
167
162
|
return text;
|
|
@@ -171,103 +166,6 @@ function truncateSlackText(text, maxLen = SLACK_BLOCK_TEXT_MAX_LEN) {
|
|
|
171
166
|
}
|
|
172
167
|
return `${text.slice(0, maxLen - 1)}…`;
|
|
173
168
|
}
|
|
174
|
-
function hudTonePrefix(tone) {
|
|
175
|
-
switch (tone) {
|
|
176
|
-
case "success":
|
|
177
|
-
return "✅";
|
|
178
|
-
case "warning":
|
|
179
|
-
return "⚠️";
|
|
180
|
-
case "error":
|
|
181
|
-
return "⛔";
|
|
182
|
-
case "accent":
|
|
183
|
-
return "🔹";
|
|
184
|
-
case "muted":
|
|
185
|
-
return "▫️";
|
|
186
|
-
case "dim":
|
|
187
|
-
return "·";
|
|
188
|
-
case "info":
|
|
189
|
-
default:
|
|
190
|
-
return "ℹ️";
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function applySlackHudTextStyle(text, style, opts = {}) {
|
|
194
|
-
const weight = style?.weight ?? opts.defaultWeight;
|
|
195
|
-
const italic = style?.italic ?? opts.defaultItalic ?? false;
|
|
196
|
-
const code = style?.code ?? opts.defaultCode ?? false;
|
|
197
|
-
let out = text;
|
|
198
|
-
if (code) {
|
|
199
|
-
out = `\`${out}\``;
|
|
200
|
-
}
|
|
201
|
-
if (italic) {
|
|
202
|
-
out = `_${out}_`;
|
|
203
|
-
}
|
|
204
|
-
if (weight === "strong") {
|
|
205
|
-
out = `*${out}*`;
|
|
206
|
-
}
|
|
207
|
-
return out;
|
|
208
|
-
}
|
|
209
|
-
function stripSlackMrkdwn(text) {
|
|
210
|
-
return text.replace(/[*_`~]/g, "");
|
|
211
|
-
}
|
|
212
|
-
function hudDocSectionLines(doc) {
|
|
213
|
-
const lines = [];
|
|
214
|
-
const chipsLine = doc.chips
|
|
215
|
-
.slice(0, 8)
|
|
216
|
-
.map((chip) => {
|
|
217
|
-
const label = applySlackHudTextStyle(chip.label, chip.style, { defaultWeight: "strong" });
|
|
218
|
-
return `${hudTonePrefix(chip.tone)} ${label}`;
|
|
219
|
-
})
|
|
220
|
-
.join(" · ");
|
|
221
|
-
if (chipsLine.length > 0) {
|
|
222
|
-
lines.push(chipsLine);
|
|
223
|
-
}
|
|
224
|
-
for (const section of doc.sections) {
|
|
225
|
-
switch (section.kind) {
|
|
226
|
-
case "kv": {
|
|
227
|
-
const title = applySlackHudTextStyle(section.title ?? "Details", section.title_style, { defaultWeight: "strong" });
|
|
228
|
-
const items = section.items.slice(0, SLACK_SECTION_LINE_MAX).map((item) => {
|
|
229
|
-
const value = applySlackHudTextStyle(item.value, item.value_style);
|
|
230
|
-
return `• *${item.label}:* ${value}`;
|
|
231
|
-
});
|
|
232
|
-
if (items.length > 0) {
|
|
233
|
-
lines.push([title, ...items].join("\n"));
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
case "checklist": {
|
|
238
|
-
const title = applySlackHudTextStyle(section.title ?? "Checklist", section.title_style, { defaultWeight: "strong" });
|
|
239
|
-
const items = section.items
|
|
240
|
-
.slice(0, SLACK_SECTION_LINE_MAX)
|
|
241
|
-
.map((item) => `${item.done ? "✅" : "⬜"} ${applySlackHudTextStyle(item.label, item.style)}`);
|
|
242
|
-
if (items.length > 0) {
|
|
243
|
-
lines.push([title, ...items].join("\n"));
|
|
244
|
-
}
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
case "activity": {
|
|
248
|
-
const title = applySlackHudTextStyle(section.title ?? "Activity", section.title_style, { defaultWeight: "strong" });
|
|
249
|
-
const items = section.lines.slice(0, SLACK_SECTION_LINE_MAX).map((line) => `• ${line}`);
|
|
250
|
-
if (items.length > 0) {
|
|
251
|
-
lines.push([title, ...items].join("\n"));
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
case "text": {
|
|
256
|
-
const title = section.title
|
|
257
|
-
? `${applySlackHudTextStyle(section.title, section.title_style, { defaultWeight: "strong" })}\n`
|
|
258
|
-
: "";
|
|
259
|
-
const text = applySlackHudTextStyle(section.text, section.style);
|
|
260
|
-
lines.push(`${title}${text}`);
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (lines.length === 0) {
|
|
266
|
-
const snapshot = applySlackHudTextStyle(doc.snapshot_compact, doc.snapshot_style);
|
|
267
|
-
lines.push(`${applySlackHudTextStyle("Snapshot", undefined, { defaultWeight: "strong" })}\n${snapshot}`);
|
|
268
|
-
}
|
|
269
|
-
return lines;
|
|
270
|
-
}
|
|
271
169
|
function uiDocComponentLines(doc) {
|
|
272
170
|
const lines = [];
|
|
273
171
|
const components = [...doc.components].sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -350,17 +248,6 @@ function appendUiDocText(body, uiDocs) {
|
|
|
350
248
|
}
|
|
351
249
|
return `${trimmed}\n\n${fallback}`;
|
|
352
250
|
}
|
|
353
|
-
function hudActionButtons(doc) {
|
|
354
|
-
return doc.actions.slice(0, SLACK_ACTIONS_MAX_TOTAL).map((action) => ({
|
|
355
|
-
type: "button",
|
|
356
|
-
text: {
|
|
357
|
-
type: "plain_text",
|
|
358
|
-
text: truncateSlackText(action.label, 75),
|
|
359
|
-
},
|
|
360
|
-
value: truncateSlackText(action.command_text, SLACK_ACTION_VALUE_MAX_CHARS),
|
|
361
|
-
action_id: buildSlackHudActionId(action.id),
|
|
362
|
-
}));
|
|
363
|
-
}
|
|
364
251
|
function uiDocActionTextLine(action, opts = {}) {
|
|
365
252
|
const parts = [`• ${action.label}`];
|
|
366
253
|
if (action.description) {
|
|
@@ -369,11 +256,36 @@ function uiDocActionTextLine(action, opts = {}) {
|
|
|
369
256
|
parts.push(`(id=${action.id}${opts.suffix ? `; ${opts.suffix}` : ""})`);
|
|
370
257
|
return parts.join(" ");
|
|
371
258
|
}
|
|
259
|
+
function statusProfileVariant(doc) {
|
|
260
|
+
const profile = doc.metadata.profile;
|
|
261
|
+
if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
|
|
262
|
+
return "status";
|
|
263
|
+
}
|
|
264
|
+
const rawVariant = typeof profile.variant === "string"
|
|
265
|
+
? profile.variant.trim().toLowerCase()
|
|
266
|
+
: "";
|
|
267
|
+
return rawVariant.length > 0 ? rawVariant : "status";
|
|
268
|
+
}
|
|
269
|
+
function isStatusProfileStatusVariant(doc) {
|
|
270
|
+
return resolveUiStatusProfileName(doc) !== null && statusProfileVariant(doc) === "status";
|
|
271
|
+
}
|
|
272
|
+
function uiDocDeterministicActionFallbackLine(action) {
|
|
273
|
+
const commandText = commandTextForUiDocAction(action);
|
|
274
|
+
if (commandText) {
|
|
275
|
+
return `• ${action.label}: ${commandText}`;
|
|
276
|
+
}
|
|
277
|
+
return `• ${action.label}: interactive unavailable (missing command_text)`;
|
|
278
|
+
}
|
|
372
279
|
function uiDocActionButtons(doc, actionPayloadsByKey) {
|
|
373
280
|
const buttons = [];
|
|
374
281
|
const fallbackLines = [];
|
|
282
|
+
const statusProfile = isStatusProfileStatusVariant(doc);
|
|
375
283
|
const actions = [...doc.actions].sort((a, b) => a.id.localeCompare(b.id)).slice(0, SLACK_ACTIONS_MAX_TOTAL);
|
|
376
284
|
for (const action of actions) {
|
|
285
|
+
if (statusProfile) {
|
|
286
|
+
fallbackLines.push(uiDocDeterministicActionFallbackLine(action));
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
377
289
|
const payload = actionPayloadsByKey.get(uiDocActionPayloadKey(doc.ui_id, action.id));
|
|
378
290
|
if (!payload) {
|
|
379
291
|
fallbackLines.push(uiDocActionTextLine(action, { suffix: "interactive unavailable" }));
|
|
@@ -415,9 +327,8 @@ export function splitSlackMessageText(text, maxLen = SLACK_MESSAGE_MAX_LEN) {
|
|
|
415
327
|
}
|
|
416
328
|
return chunks;
|
|
417
329
|
}
|
|
418
|
-
function slackBlocksForOutboxRecord(
|
|
419
|
-
|
|
420
|
-
if (hudDocs.length === 0 && uiDocs.length === 0) {
|
|
330
|
+
function slackBlocksForOutboxRecord(body, uiDocs, opts = {}) {
|
|
331
|
+
if (uiDocs.length === 0) {
|
|
421
332
|
return undefined;
|
|
422
333
|
}
|
|
423
334
|
const uiDocActionPayloadsByKey = opts.uiDocActionPayloadsByKey ?? new Map();
|
|
@@ -427,35 +338,6 @@ function slackBlocksForOutboxRecord(record, body, uiDocs, opts = {}) {
|
|
|
427
338
|
type: "section",
|
|
428
339
|
text: { type: "mrkdwn", text: truncateSlackText(headerText) },
|
|
429
340
|
});
|
|
430
|
-
for (const doc of hudDocs) {
|
|
431
|
-
if (blocks.length >= SLACK_BLOCKS_MAX) {
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
const styledTitle = applySlackHudTextStyle(doc.title, doc.title_style, { defaultWeight: "strong" });
|
|
435
|
-
blocks.push({
|
|
436
|
-
type: "context",
|
|
437
|
-
elements: [{ type: "mrkdwn", text: truncateSlackText(`*HUD* · ${styledTitle}`) }],
|
|
438
|
-
});
|
|
439
|
-
for (const line of hudDocSectionLines(doc)) {
|
|
440
|
-
if (blocks.length >= SLACK_BLOCKS_MAX) {
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
blocks.push({
|
|
444
|
-
type: "section",
|
|
445
|
-
text: { type: "mrkdwn", text: truncateSlackText(line) },
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
const buttons = hudActionButtons(doc);
|
|
449
|
-
for (let idx = 0; idx < buttons.length; idx += SLACK_ACTIONS_MAX_PER_BLOCK) {
|
|
450
|
-
if (blocks.length >= SLACK_BLOCKS_MAX) {
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
blocks.push({
|
|
454
|
-
type: "actions",
|
|
455
|
-
elements: buttons.slice(idx, idx + SLACK_ACTIONS_MAX_PER_BLOCK),
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
341
|
for (const doc of uiDocs) {
|
|
460
342
|
if (blocks.length >= SLACK_BLOCKS_MAX) {
|
|
461
343
|
break;
|
|
@@ -515,21 +397,11 @@ function slackStatusMessageTsFromMetadata(metadata) {
|
|
|
515
397
|
const TELEGRAM_CALLBACK_DATA_MAX_BYTES = 64;
|
|
516
398
|
const TELEGRAM_ACTIONS_MAX_TOTAL = 20;
|
|
517
399
|
const TELEGRAM_ACTIONS_PER_ROW = 3;
|
|
518
|
-
const TELEGRAM_DOCS_MAX = 2;
|
|
519
400
|
function utf8ByteLength(value) {
|
|
520
401
|
return new TextEncoder().encode(value).length;
|
|
521
402
|
}
|
|
522
|
-
function telegramTextForOutboxRecord(
|
|
523
|
-
const hudDocs = hudDocsForPresentation(record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
|
|
403
|
+
function telegramTextForOutboxRecord(body, uiDocs) {
|
|
524
404
|
const lines = [body.trim()];
|
|
525
|
-
if (hudDocs.length > 0) {
|
|
526
|
-
for (const doc of hudDocs) {
|
|
527
|
-
lines.push("", `HUD · ${doc.title}`);
|
|
528
|
-
for (const sectionLine of hudDocSectionLines(doc)) {
|
|
529
|
-
lines.push(stripSlackMrkdwn(sectionLine));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
405
|
const uiFallback = uiDocsTextFallback(uiDocs);
|
|
534
406
|
if (uiFallback) {
|
|
535
407
|
lines.push("", uiFallback);
|
|
@@ -552,43 +424,6 @@ function telegramOverflowText(lines) {
|
|
|
552
424
|
}
|
|
553
425
|
return `\n\nActions:\n${lines.join("\n")}`;
|
|
554
426
|
}
|
|
555
|
-
async function compileTelegramHudActions(opts) {
|
|
556
|
-
const hudDocs = hudDocsForPresentation(opts.record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
|
|
557
|
-
if (hudDocs.length === 0) {
|
|
558
|
-
return { buttonEntries: [], overflowLines: [] };
|
|
559
|
-
}
|
|
560
|
-
const buttonEntries = [];
|
|
561
|
-
const overflowLines = [];
|
|
562
|
-
const actions = hudDocs.flatMap((doc) => doc.actions).slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
|
|
563
|
-
for (const action of actions) {
|
|
564
|
-
const fallbackLine = `• ${action.label}: ${action.command_text}`;
|
|
565
|
-
let callbackData = action.command_text;
|
|
566
|
-
if (opts.encodeCallbackData) {
|
|
567
|
-
try {
|
|
568
|
-
callbackData = await opts.encodeCallbackData(action.command_text, { record: opts.record });
|
|
569
|
-
}
|
|
570
|
-
catch {
|
|
571
|
-
overflowLines.push(fallbackLine);
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (utf8ByteLength(callbackData) > TELEGRAM_CALLBACK_DATA_MAX_BYTES) {
|
|
576
|
-
overflowLines.push(fallbackLine);
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
buttonEntries.push({
|
|
580
|
-
button: {
|
|
581
|
-
text: action.label.slice(0, 64),
|
|
582
|
-
callback_data: callbackData,
|
|
583
|
-
},
|
|
584
|
-
fallbackLine,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
return {
|
|
588
|
-
buttonEntries,
|
|
589
|
-
overflowLines,
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
427
|
function commandTextForUiDocAction(action) {
|
|
593
428
|
const fromMetadata = typeof action.metadata.command_text === "string" ? action.metadata.command_text.trim() : "";
|
|
594
429
|
if (fromMetadata.length === 0) {
|
|
@@ -603,17 +438,22 @@ async function compileTelegramUiDocActions(opts) {
|
|
|
603
438
|
const buttonEntries = [];
|
|
604
439
|
const overflowLines = [];
|
|
605
440
|
for (const doc of opts.uiDocs) {
|
|
441
|
+
const statusProfile = isStatusProfileStatusVariant(doc);
|
|
606
442
|
const actions = [...doc.actions].sort((a, b) => a.id.localeCompare(b.id));
|
|
607
443
|
for (const action of actions) {
|
|
444
|
+
const commandText = commandTextForUiDocAction(action);
|
|
445
|
+
const fallbackLine = commandText
|
|
446
|
+
? `• ${action.label}: ${commandText}`
|
|
447
|
+
: `• ${action.label}: interactive unavailable (missing command_text)`;
|
|
448
|
+
if (statusProfile) {
|
|
449
|
+
overflowLines.push(fallbackLine);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
608
452
|
const uiEvent = buildSanitizedUiEventForAction({
|
|
609
453
|
doc,
|
|
610
454
|
action,
|
|
611
455
|
createdAtMs: opts.nowMs,
|
|
612
456
|
});
|
|
613
|
-
const commandText = commandTextForUiDocAction(action);
|
|
614
|
-
const fallbackLine = commandText
|
|
615
|
-
? `• ${action.label}: ${commandText}`
|
|
616
|
-
: `• ${action.label}: interactive unavailable (missing command_text)`;
|
|
617
457
|
if (!commandText || !uiEvent) {
|
|
618
458
|
overflowLines.push(fallbackLine);
|
|
619
459
|
continue;
|
|
@@ -736,28 +576,22 @@ async function sendTelegramTextChunks(opts) {
|
|
|
736
576
|
}
|
|
737
577
|
export async function deliverTelegramOutboxRecord(opts) {
|
|
738
578
|
const { botToken, record } = opts;
|
|
739
|
-
const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs:
|
|
579
|
+
const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs: UI_DOCS_MAX });
|
|
740
580
|
const nowMs = Math.trunc(Date.now());
|
|
741
|
-
const hudActions = await compileTelegramHudActions({
|
|
742
|
-
record,
|
|
743
|
-
encodeCallbackData: opts.encodeCallbackData,
|
|
744
|
-
});
|
|
745
581
|
const uiActions = await compileTelegramUiDocActions({
|
|
746
582
|
record,
|
|
747
583
|
uiDocs,
|
|
748
584
|
nowMs,
|
|
749
585
|
encodeCallbackData: opts.encodeCallbackData,
|
|
750
586
|
});
|
|
751
|
-
const
|
|
752
|
-
const visibleEntries = combinedEntries.slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
|
|
587
|
+
const visibleEntries = uiActions.buttonEntries.slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
|
|
753
588
|
const overflowLines = [
|
|
754
|
-
...hudActions.overflowLines,
|
|
755
589
|
...uiActions.overflowLines,
|
|
756
|
-
...
|
|
590
|
+
...uiActions.buttonEntries.slice(TELEGRAM_ACTIONS_MAX_TOTAL).map((entry) => entry.fallbackLine),
|
|
757
591
|
];
|
|
758
592
|
const replyMarkup = telegramReplyMarkupFromButtons(visibleEntries.map((entry) => entry.button));
|
|
759
593
|
const replyToMessageId = maybeParseTelegramMessageId(record.envelope.metadata?.telegram_reply_to_message_id);
|
|
760
|
-
const telegramText = `${telegramTextForOutboxRecord(record
|
|
594
|
+
const telegramText = `${telegramTextForOutboxRecord(record.envelope.body, uiDocs)}${telegramOverflowText(overflowLines)}`.trim();
|
|
761
595
|
const fallbackMessagePayload = {
|
|
762
596
|
...buildTelegramSendMessagePayload({
|
|
763
597
|
chatId: record.envelope.channel_conversation_id,
|
|
@@ -916,10 +750,11 @@ export async function deliverSlackOutboxRecord(opts) {
|
|
|
916
750
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
917
751
|
const attachments = record.envelope.attachments ?? [];
|
|
918
752
|
const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs: UI_DOCS_MAX });
|
|
753
|
+
const interactiveUiDocs = uiDocs.filter((doc) => !isStatusProfileStatusVariant(doc));
|
|
919
754
|
let uiDocActionPayloadsByKey = new Map();
|
|
920
|
-
if (opts.uiCallbackTokenStore &&
|
|
755
|
+
if (opts.uiCallbackTokenStore && interactiveUiDocs.some((doc) => doc.actions.length > 0)) {
|
|
921
756
|
const issued = await issueUiDocActionPayloads({
|
|
922
|
-
uiDocs,
|
|
757
|
+
uiDocs: interactiveUiDocs,
|
|
923
758
|
tokenStore: opts.uiCallbackTokenStore,
|
|
924
759
|
context: uiActionPayloadContextFromOutboxRecord(record),
|
|
925
760
|
ttlMs: UI_CALLBACK_TOKEN_TTL_MS,
|
|
@@ -928,7 +763,7 @@ export async function deliverSlackOutboxRecord(opts) {
|
|
|
928
763
|
uiDocActionPayloadsByKey = new Map(issued.map((entry) => [entry.key, entry.payload_json]));
|
|
929
764
|
}
|
|
930
765
|
const renderedBodyForBlocks = renderSlackMarkdown(record.envelope.body);
|
|
931
|
-
const blocks = slackBlocksForOutboxRecord(
|
|
766
|
+
const blocks = slackBlocksForOutboxRecord(renderedBodyForBlocks, uiDocs, {
|
|
932
767
|
uiDocActionPayloadsByKey,
|
|
933
768
|
});
|
|
934
769
|
const bodyForText = blocks
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
2
|
+
"name": "@femtomc/mu-server",
|
|
3
|
+
"version": "26.2.109",
|
|
4
|
+
"description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mu",
|
|
7
|
+
"server",
|
|
8
|
+
"api",
|
|
9
|
+
"web",
|
|
10
|
+
"automation"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"bin": {
|
|
16
|
+
"mu-server": "./dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/**"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.build.json",
|
|
29
|
+
"test": "bun test",
|
|
30
|
+
"start": "bun run dist/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@femtomc/mu-agent": "26.2.109",
|
|
34
|
+
"@femtomc/mu-control-plane": "26.2.109",
|
|
35
|
+
"@femtomc/mu-core": "26.2.109"
|
|
36
|
+
}
|
|
37
37
|
}
|