@thehammer/danx-dashboard-mcp 0.1.210 → 0.1.212
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/dist/listen.js +52 -1
- package/package.json +1 -1
package/dist/listen.js
CHANGED
|
@@ -122,6 +122,10 @@ export function invalidEventReason(value) {
|
|
|
122
122
|
if (typeof value.origin !== "string" || !ACTIVITY_ORIGINS.has(value.origin)) {
|
|
123
123
|
return "origin is not one of operator, agent, machine";
|
|
124
124
|
}
|
|
125
|
+
// DX-4246 — the key snapshot is part of every event the dashboard sends, `null` when no key wrote it.
|
|
126
|
+
if (value.viaKey !== null && !(isRecord(value.viaKey) && typeof value.viaKey.id === "number" && typeof value.viaKey.name === "string")) {
|
|
127
|
+
return "viaKey is not null or {id, name}";
|
|
128
|
+
}
|
|
125
129
|
const d = value.detail;
|
|
126
130
|
if (!isRecord(d))
|
|
127
131
|
return "detail is not an object";
|
|
@@ -187,7 +191,9 @@ export function invalidEventReason(value) {
|
|
|
187
191
|
* reader would have to cross-reference.
|
|
188
192
|
*/
|
|
189
193
|
function actorLabel(event) {
|
|
190
|
-
|
|
194
|
+
// DX-4246 — a write made through an API key names the user AND the key ("dan via claude-laptop (agent)").
|
|
195
|
+
const via = event.viaKey === null ? "" : ` via ${event.viaKey.name}`;
|
|
196
|
+
return `${event.actor}${via} (${event.origin})`;
|
|
191
197
|
}
|
|
192
198
|
/** Only ever called on an event `invalidEventReason` accepted, so its casts are checked. */
|
|
193
199
|
function describe(event) {
|
|
@@ -323,6 +329,32 @@ export function formatNudgeLine(event) {
|
|
|
323
329
|
"Work these cards; any you cannot work, update to its true state: open a problem, add depends_on or " +
|
|
324
330
|
"conflict_on, block it with the real reason, or rollback_pickup.");
|
|
325
331
|
}
|
|
332
|
+
/** Why a reminder cannot be read, or `null` when it can. */
|
|
333
|
+
export function invalidReminderReason(value) {
|
|
334
|
+
if (!isRecord(value))
|
|
335
|
+
return "the reminder is not an object";
|
|
336
|
+
if (value.kind !== "policy_reminder")
|
|
337
|
+
return "kind is not policy_reminder";
|
|
338
|
+
if (typeof value.text !== "string" || value.text.trim() === "")
|
|
339
|
+
return "text is not a non-empty string";
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The label every reminder carries, so the session can never read one as the operator speaking
|
|
344
|
+
* (DX-2955: "labelled unmistakably as automated system reminders, never phrased as the operator").
|
|
345
|
+
*/
|
|
346
|
+
export const REMINDER_LABEL = "AUTOMATED SYSTEM REMINDER from the danxbot dashboard monitor (not from the operator):";
|
|
347
|
+
/**
|
|
348
|
+
* The text of a reminder: the label, then the reminder as the dashboard wrote it. Unlike an
|
|
349
|
+
* activity line the body keeps its line breaks, because a reminder with something off is the
|
|
350
|
+
* numbered checklist plus what is off. Throws, naming the fault, on a malformed reminder.
|
|
351
|
+
*/
|
|
352
|
+
export function formatReminderLine(event) {
|
|
353
|
+
const invalid = invalidReminderReason(event);
|
|
354
|
+
if (invalid !== null)
|
|
355
|
+
throw new Error(`malformed reminder event: ${invalid}`);
|
|
356
|
+
return `${LINE_PREFIX} ${REMINDER_LABEL}\n${event.text.trim()}`;
|
|
357
|
+
}
|
|
326
358
|
/**
|
|
327
359
|
* Incremental SSE parser. Comment lines (keep-alives, the connect marker) and
|
|
328
360
|
* field-less blocks produce no message, which is what keeps them out of the output.
|
|
@@ -463,6 +495,25 @@ export async function runListener(options, deps) {
|
|
|
463
495
|
deps.write({ type: "event", id: null, text, origin: null });
|
|
464
496
|
return null;
|
|
465
497
|
}
|
|
498
|
+
if (message.event === "reminder") {
|
|
499
|
+
// DX-2955 - like a nudge: no usable id, never replayed, never deduplicated, and
|
|
500
|
+
// `origin: null` so `bridge.ts` delivers it immediately instead of folding it
|
|
501
|
+
// into a digest (the generic null-origin exemption in `routeEvent`).
|
|
502
|
+
let parsed;
|
|
503
|
+
let invalid;
|
|
504
|
+
try {
|
|
505
|
+
parsed = JSON.parse(message.data);
|
|
506
|
+
invalid = invalidReminderReason(parsed);
|
|
507
|
+
}
|
|
508
|
+
catch (err) {
|
|
509
|
+
invalid = `not JSON: ${err.message}`;
|
|
510
|
+
}
|
|
511
|
+
const text = invalid === null
|
|
512
|
+
? formatReminderLine(parsed)
|
|
513
|
+
: `${LINE_PREFIX} could not read reminder (${invalid}): ${oneLine(message.data, 300)}`;
|
|
514
|
+
deps.write({ type: "event", id: null, text, origin: null });
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
466
517
|
if (message.event !== "activity")
|
|
467
518
|
return null;
|
|
468
519
|
// `Number(null)` is 0, so a frame with no id must never be read as event 0: that id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.212",
|
|
4
4
|
"description": "Stdio MCP server fronting danxbot's dashboard API for agents (operator sessions and dispatched workers alike): the generic danxbot_api/danxbot_api_list/danxbot_api_spec tools reach any route the caller's credential may call, plus dedicated agent-specific tools (plan_connect, danxbot_complete, and other per-dispatch lifecycle callbacks) (DX-3465, PLN-11 R-25).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|