@thehammer/danx-dashboard-mcp 0.1.197 → 0.1.199
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/bridge.js +12 -1
- package/dist/session-stats.js +9 -2
- package/package.json +2 -2
package/dist/bridge.js
CHANGED
|
@@ -73,6 +73,7 @@ import { oneLine } from "./one-line.js";
|
|
|
73
73
|
import { credentialFingerprint, describeCredentialSource, readCredential, } from "./credential.js";
|
|
74
74
|
import { readSessionConnection, sessionConnectionPath } from "./session-connection.js";
|
|
75
75
|
import { capResumeIds, DELIVERED_ID_MEMORY, HEALTHY_CONNECTION_MS, READ_IDLE_TIMEOUT_MS, runListener, } from "./listen.js";
|
|
76
|
+
import { isClaimState } from "@thehammer/danx-session-telemetry";
|
|
76
77
|
import { OperatorSessionStats, transcriptPathFromEnv, } from "./session-stats.js";
|
|
77
78
|
/**
|
|
78
79
|
* DX-2730 D4 — WHICH EVENTS WAKE THE SESSION, NOW THAT EVERY ONE OF THEM CAN.
|
|
@@ -600,7 +601,13 @@ async function sendHeartbeat(options, deps, connection, stats) {
|
|
|
600
601
|
// rather than treat them as the answer to its write.
|
|
601
602
|
const applied = ok && isRecord(outcome.body) && outcome.body.statsError === undefined;
|
|
602
603
|
const statsBody = applied && isRecord(outcome.body) && isRecord(outcome.body.stats) ? outcome.body.stats : null;
|
|
603
|
-
|
|
604
|
+
// DX-3962: an answer from a server that predates attribution stores cursors with no `claims`. Treated exactly like
|
|
605
|
+
// an answer with no stats (the older-server path): the collector never starts, the heartbeat still lands, and
|
|
606
|
+
// nothing is recorded until the server is deployed. Handing it on would crash the collector at attach.
|
|
607
|
+
const answered = statsBody !== null &&
|
|
608
|
+
isRecord(statsBody.sourceCursors) &&
|
|
609
|
+
isRecord(statsBody.totals) &&
|
|
610
|
+
Object.values(statsBody.sourceCursors).every((cursor) => isRecord(cursor) && isClaimState(cursor.claims))
|
|
604
611
|
? statsBody
|
|
605
612
|
: null;
|
|
606
613
|
return { landed: true, status: outcome.status, stats: answered };
|
|
@@ -691,6 +698,10 @@ export async function runBridge(options, deps) {
|
|
|
691
698
|
// PROCESS reconnecting, never an external watchdog and never a second
|
|
692
699
|
// process racing the first.
|
|
693
700
|
forceReconnect?.();
|
|
701
|
+
}).catch((err) => {
|
|
702
|
+
// DX-3962: a stats failure must never take the bridge down. `heartbeatTick` is fired without being awaited, so
|
|
703
|
+
// a throw here would be an unhandled rejection that kills the process and with it the session's plan events.
|
|
704
|
+
process.stderr.write(`operator session stats: heartbeat tick failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
694
705
|
});
|
|
695
706
|
};
|
|
696
707
|
const heartbeat = deps.scheduleInterval(heartbeatTick, HEARTBEAT_INTERVAL_MS);
|
package/dist/session-stats.js
CHANGED
|
@@ -59,10 +59,17 @@ export function transcriptPathFromEnv(env) {
|
|
|
59
59
|
const realWatcher = (options) => new SessionLogWatcher(options);
|
|
60
60
|
/** The collector's dispatch-id argument; the heartbeat is addressed by session, not dispatch. */
|
|
61
61
|
const OPERATOR_DISPATCH_REF = "operator-session";
|
|
62
|
+
/**
|
|
63
|
+
* DX-3962 - the bridge's collector. `"operator-bridge"` is the ONE mode that credits a sub-agent's replies to the card
|
|
64
|
+
* it claimed (from its own transcript's pickup); a worker builds its collector with attribution off.
|
|
65
|
+
*/
|
|
66
|
+
function newCollector() {
|
|
67
|
+
return new DispatchUsage("operator-bridge");
|
|
68
|
+
}
|
|
62
69
|
export class OperatorSessionStats {
|
|
63
70
|
transcriptPath;
|
|
64
71
|
makeWatcher;
|
|
65
|
-
usage =
|
|
72
|
+
usage = newCollector();
|
|
66
73
|
watcher = null;
|
|
67
74
|
stopped = false;
|
|
68
75
|
constructor(transcriptPath, makeWatcher = realWatcher) {
|
|
@@ -153,7 +160,7 @@ export class OperatorSessionStats {
|
|
|
153
160
|
return;
|
|
154
161
|
watcher.stop();
|
|
155
162
|
this.watcher = null;
|
|
156
|
-
this.usage =
|
|
163
|
+
this.usage = newCollector();
|
|
157
164
|
})
|
|
158
165
|
.catch((err) => {
|
|
159
166
|
process.stderr.write(`operator session stats: transcript watcher failed: ${err.message}\n`);
|
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.199",
|
|
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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
34
|
-
"@thehammer/danx-session-telemetry": "0.1.
|
|
34
|
+
"@thehammer/danx-session-telemetry": "0.1.22",
|
|
35
35
|
"zod": "^3.25.76"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|