@50arms/platform-mcp 0.1.437 → 0.1.438
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/heartbeat-reporter.js +4 -1
- package/dist/index.js +9 -0
- package/dist/machine-toolkit.js +1 -1
- package/dist/session-access.js +20 -6
- package/package.json +1 -1
|
@@ -33,7 +33,8 @@ export class HeartbeatReporter {
|
|
|
33
33
|
// DX-5088 - when the heartbeat first failed to land in the current outage (across every reconnect); null while the last one landed.
|
|
34
34
|
outageSince = null;
|
|
35
35
|
outageAnnounced = false;
|
|
36
|
-
// DX-4418 - the stop notice for a revoked key goes out once; every later beat is refused the same way.
|
|
36
|
+
// DX-4418 - the stop notice for a revoked key goes out once per revocation; every later beat is refused the same way. DX-6894: a beat the
|
|
37
|
+
// dashboard accepts again (a person signed the session back in) re-arms it, so a second revocation is announced too.
|
|
37
38
|
revocationAnnounced = false;
|
|
38
39
|
constructor(deps, transcriptPath) {
|
|
39
40
|
this.deps = deps;
|
|
@@ -71,6 +72,8 @@ export class HeartbeatReporter {
|
|
|
71
72
|
const answer = await sendHeartbeat(this.deps.auth(), this.deps.http, this.deps.connection(), stats, title, statsHeld);
|
|
72
73
|
if (answer.landed)
|
|
73
74
|
this.dashboardTitle = answer.title ?? undefined;
|
|
75
|
+
if (answer.landed && answer.revocation === null)
|
|
76
|
+
this.revocationAnnounced = false;
|
|
74
77
|
// DX-4418 - a person revoked this session's key: tell the session to stop, at once and only once. Its next
|
|
75
78
|
// dashboard call says the same.
|
|
76
79
|
if (answer.revocation !== null && !this.revocationAnnounced) {
|
package/dist/index.js
CHANGED
|
@@ -908,11 +908,20 @@ strictTool("plan_connect",
|
|
|
908
908
|
'The `naming` block\'s exact `{Name}: PLAN-NNN {short title}`, after renaming this thread. Omit on the first connect.'),
|
|
909
909
|
assignment_id: z.number().int().positive().optional().describe("The assignment this connect answers, when a person's assignment message names one. Refused `assignment_ended` once it is over: then do nothing more about it."),
|
|
910
910
|
disconnect: z.boolean().optional().describe("true = leave `plan_id` (your current plan); no `title`."),
|
|
911
|
+
person_sign_in: z
|
|
912
|
+
.boolean()
|
|
913
|
+
.optional()
|
|
914
|
+
.describe(
|
|
915
|
+
// DX-6894 (DEC-589): set only by the plugin band when a PERSON presses its Sign in; the one call that leaves a revoked key's stop.
|
|
916
|
+
"Set only by the plugin's Sign in button when a person presses it; an agent never sets it. On a session whose key a person revoked it lifts the stop and starts a new access request a person must approve; any call without it still answers the stop."),
|
|
911
917
|
},
|
|
912
918
|
// DX-2862 — the connect is also the moment the session's connection record is written for `restart_notice` and the listener reminder: of the
|
|
913
919
|
// dashboard MCP servers a session may have configured, the one that connected the plan is the one whose credential they
|
|
914
920
|
// must resolve (the relay needs none of this: it lives in each server, with that server's own credential).
|
|
915
921
|
async (args) => {
|
|
922
|
+
// DX-6894 — a person's Sign in press leaves the revoked stop; every call without it still answers that stop.
|
|
923
|
+
if (args.person_sign_in === true)
|
|
924
|
+
access.personSignIn();
|
|
916
925
|
// DX-4390 — a leave names the plan it leaves, and needs a key like any other call.
|
|
917
926
|
if (args.disconnect === true && args.plan_id === undefined) {
|
|
918
927
|
return haltResult("plan_connect: `disconnect: true` needs `plan_id`, the plan this session is currently on");
|
package/dist/machine-toolkit.js
CHANGED
|
@@ -250,7 +250,7 @@ function resolveToolkitVersion(stamped, readManifest) {
|
|
|
250
250
|
return stamped ?? readManifest();
|
|
251
251
|
}
|
|
252
252
|
var readOwnManifest = () => JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8")).version;
|
|
253
|
-
var TOOLKIT_VERSION = resolveToolkitVersion(false ? void 0 : "0.1.
|
|
253
|
+
var TOOLKIT_VERSION = resolveToolkitVersion(false ? void 0 : "0.1.438", readOwnManifest);
|
|
254
254
|
function envelope(now) {
|
|
255
255
|
return { contract_version: CONTRACT_VERSION, toolkit_version: TOOLKIT_VERSION, profile: PROFILE, measured_at: now.toISOString() };
|
|
256
256
|
}
|
package/dist/session-access.js
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
* (`session-key.ts`) and a 401 on it deletes the file (`dropKey`).
|
|
9
9
|
* - signed out no credential. Every tool but `plan_connect` halts; `plan_connect` asks.
|
|
10
10
|
*
|
|
11
|
-
* DX-4418: a fourth state, REVOKED, ends all three
|
|
12
|
-
* (as opposed to one that lapsed); this object then remembers it
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* DX-4418: a fourth state, REVOKED, ends all three. The dashboard answers `401 key_revoked` to a key a person revoked
|
|
12
|
+
* (as opposed to one that lapsed); this object then remembers it, every dashboard tool (and `plan_connect`) returns the one stop
|
|
13
|
+
* halt (`key-revoked-halt.ts`), and no new access request is made. A session's key file is deleted; a dispatch's credential is
|
|
14
|
+
* left to its dispatch, but the halt is the same. DX-6894: the one way out is `personSignIn`, the call a PERSON's Sign in press
|
|
15
|
+
* makes (`plan_connect` with `person_sign_in: true`); nothing an agent calls lifts the stop.
|
|
15
16
|
*
|
|
16
17
|
* ASKING NEVER BLOCKS FOR MINUTES. The first signed-out `plan_connect` posts the request and
|
|
17
18
|
* answers at once with the approval URL and confirm code. From then on THIS object polls claim in
|
|
@@ -69,7 +70,7 @@ const notReady = (answer, isError, halt) => ({
|
|
|
69
70
|
export class SessionAccess {
|
|
70
71
|
deps;
|
|
71
72
|
credential;
|
|
72
|
-
// DX-4418: set once the dashboard says a person revoked the key
|
|
73
|
+
// DX-4418: set once the dashboard says a person revoked the key. Only `personSignIn` clears it (DX-6894); a restarted server is a new process.
|
|
73
74
|
revoked = null;
|
|
74
75
|
pending = null;
|
|
75
76
|
// DX-4390: two concurrent signed-out plan_connect calls must not each post a request.
|
|
@@ -105,6 +106,19 @@ export class SessionAccess {
|
|
|
105
106
|
revokedHalt() {
|
|
106
107
|
return this.revoked === null ? null : keyRevokedHalt(this.revoked.revocation, this.revoked.dispatched);
|
|
107
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* DX-6894 (DEC-589): a person pressed Sign in on a session whose key a person revoked: leave the revoked state so the next ask starts a
|
|
111
|
+
* fresh access request, which a person must approve. Only a session's own key can be signed in again: a dispatch's credential is the
|
|
112
|
+
* dispatch's, so its stop stays. True when the revoked state was left; false when the session was not revoked or holds a dispatch
|
|
113
|
+
* credential (then nothing changes).
|
|
114
|
+
*/
|
|
115
|
+
personSignIn() {
|
|
116
|
+
if (this.revoked === null || this.credential !== null)
|
|
117
|
+
return false;
|
|
118
|
+
this.deps.log?.(`[platform-mcp] a person pressed Sign in on a revoked session (revoked by ${this.revoked.revocation.revokedBy} at ${this.revoked.revocation.revokedAt}): asking for access again`);
|
|
119
|
+
this.revoked = null;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
108
122
|
/**
|
|
109
123
|
* DX-4418: a dashboard answer that says a person revoked the key behind it: remembered for good (a session's key file is
|
|
110
124
|
* deleted, a dispatch's credential is left to its dispatch) and answered as the stop halt. Null for every other answer.
|
|
@@ -196,7 +210,7 @@ export class SessionAccess {
|
|
|
196
210
|
return run;
|
|
197
211
|
}
|
|
198
212
|
async ensureAccessNow(title) {
|
|
199
|
-
// DX-4418: a person revoked this session's access; it
|
|
213
|
+
// DX-4418: a person revoked this session's access; it asks again only after `personSignIn` (DX-6894).
|
|
200
214
|
const revokedHalt = this.revokedHalt();
|
|
201
215
|
if (revokedHalt !== null)
|
|
202
216
|
return notReady({ state: "access_revoked", instruction: revokedHalt }, true, revokedHalt);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@50arms/platform-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.438",
|
|
4
4
|
"description": "Stdio MCP server fronting the 50 Arms dashboard API for agents (operator sessions and dispatched workers alike): the generic 50arms_api/50arms_api_list/50arms_api_spec tools reach any route the caller's credential may call, plus dedicated agent-specific tools (plan_connect, request_permission, attach_file, and 50arms_slack_reply) (DX-3465, PLN-11 R-25).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|