@thehammer/danx-dashboard-mcp 0.1.338 → 0.1.340

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 CHANGED
@@ -135,6 +135,9 @@ export function invalidEventReason(value) {
135
135
  case "comment_added": {
136
136
  if (!isCappedText(d.excerpt))
137
137
  return "detail.excerpt is not capped text";
138
+ // DX-5435: the line tells the agent which comment to signal on, so a person's comment must name it.
139
+ if (isPersonsComment(value) && typeof d.comment_id !== "number")
140
+ return "detail.comment_id is not a number";
138
141
  // DX-2906 — `problem` is OPTIONAL (only present when the comment named
139
142
  // one); absent is valid, but a present-and-malformed one is not.
140
143
  if (d.problem === undefined)
@@ -227,6 +230,20 @@ function actorLabel(event) {
227
230
  const via = event.viaKey === null ? "" : ` via ${event.viaKey.name}`;
228
231
  return `${event.actor}${via} (${event.origin})`;
229
232
  }
233
+ /** DX-5435: a comment a person wrote (not the session's own pause notice, not an agent's or the machine's): the one an agent should signal it is answering. */
234
+ function isPersonsComment(event) {
235
+ return event.origin === "operator" && event.release !== true;
236
+ }
237
+ /**
238
+ * DX-5435 / DX-5566: what the agent does with a person's comment. The responding signal puts the agent's pending acknowledgment message under the person's
239
+ * comment in the Comments drawer, and the agent's next comment on the card replaces it. There is no withdraw: the agent ALWAYS follows up, with at least a
240
+ * short confirmation when it has nothing more to say, so the person knows the comment was received and actioned. The text is written here, with the line the
241
+ * agent reads, so every session that updates this package follows it without a plugin change.
242
+ */
243
+ export function respondingInstruction(cardId, commentId) {
244
+ const path = `/api/issues/${cardId}/comments/${commentId}/responding`;
245
+ return `First call danxbot_api POST ${path} (no body) so the person sees you acknowledge it, then ALWAYS follow up with a comment on the card, even when there is nothing to add (a short "Understood." or "Updating the card."), because your comment replaces that acknowledgment and the person must know it was received and actioned`;
246
+ }
230
247
  /** Only ever called on an event `invalidEventReason` accepted, so its casts are checked. */
231
248
  function describe(event) {
232
249
  const d = event.detail;
@@ -240,9 +257,10 @@ function describe(event) {
240
257
  const problem = d.problem;
241
258
  if (event.release === true)
242
259
  return `${who} notice: ${quoted(d.excerpt)}`;
260
+ const respond = isPersonsComment(event) ? `. ${respondingInstruction(event.cardId, d.comment_id)}` : "";
243
261
  if (problem === undefined)
244
- return `${who} commented: ${quoted(d.excerpt)}`;
245
- return `${who} commented on problem ${quoted(problem.statement)}: ${quoted(d.excerpt)}`;
262
+ return `${who} commented: ${quoted(d.excerpt)}${respond}`;
263
+ return `${who} commented on problem ${quoted(problem.statement)}: ${quoted(d.excerpt)}${respond}`;
246
264
  }
247
265
  case "solution_answered": {
248
266
  // DX-2735: every answer answers ONE problem on a card that may carry several,
@@ -176,7 +176,7 @@ export class PlanEventRelay {
176
176
  this.start(cursor);
177
177
  if (this.buffer.length > 0)
178
178
  return this.answer();
179
- // DX-5415 / DX-4233: supersede where the waiter is installed, not before the pauses: calls resume in call order, so the newest always holds the slot and an older one that resumed late cannot orphan the waiter beneath it.
179
+ // DX-5415 / DX-4233: supersede where the waiter is installed, not only before the pauses, so an older call that resumed late cannot orphan the waiter beneath it. Calls resume in call order because a settle (capped at FINAL_SETTLE_BOUND_MS) ends before the idle window (HEARTBEAT_ACTIVITY_WINDOW_MS) can chain a second one; a wall-clock jump can break that order, costing the newer caller one early empty answer, never a second waiter.
180
180
  this.supersedeWaiter();
181
181
  return new Promise((resolve) => {
182
182
  const onAbort = () => this.supersedeWaiter();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.338",
3
+ "version": "0.1.340",
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, request_permission, attach_file, and danxbot_slack_reply) (DX-3465, PLN-11 R-25).",
5
5
  "license": "MIT",
6
6
  "type": "module",