@agent-deck/backend 1.8.0 → 1.8.2
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/.tsbuildinfo +1 -1
- package/dist/execution-authority/types.d.ts +1 -1
- package/dist/execution-authority/types.d.ts.map +1 -1
- package/dist/lib/agent-deck-context.d.ts +1 -1
- package/dist/lib/agent-deck-context.d.ts.map +1 -1
- package/dist/lib/agent-deck-context.js +4 -17
- package/dist/lib/agent-deck-context.js.map +1 -1
- package/dist/lib/backend-api-error.d.ts.map +1 -1
- package/dist/lib/backend-api-error.js +0 -1
- package/dist/lib/backend-api-error.js.map +1 -1
- package/dist/lib/bound-deck-scope.d.ts.map +1 -1
- package/dist/lib/bound-deck-scope.js +0 -4
- package/dist/lib/bound-deck-scope.js.map +1 -1
- package/dist/lib/http-auth.d.ts +0 -14
- package/dist/lib/http-auth.d.ts.map +1 -1
- package/dist/lib/http-auth.js +0 -25
- package/dist/lib/http-auth.js.map +1 -1
- package/dist/lib/invalid-deck-id-path-segment.d.ts +6 -0
- package/dist/lib/invalid-deck-id-path-segment.d.ts.map +1 -0
- package/dist/lib/invalid-deck-id-path-segment.js +11 -0
- package/dist/lib/invalid-deck-id-path-segment.js.map +1 -0
- package/dist/lib/route-policy.js +2 -2
- package/dist/lib/route-policy.js.map +1 -1
- package/dist/mcp-server.d.ts +6 -13
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +64 -140
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-session-binding.d.ts +7 -15
- package/dist/mcp-session-binding.d.ts.map +1 -1
- package/dist/mcp-session-binding.js +14 -30
- package/dist/mcp-session-binding.js.map +1 -1
- package/dist/mcp-tools/policy.d.ts +0 -3
- package/dist/mcp-tools/policy.d.ts.map +1 -1
- package/dist/mcp-tools/policy.js +6 -43
- package/dist/mcp-tools/policy.js.map +1 -1
- package/dist/mcp-tools/register.d.ts +1 -5
- package/dist/mcp-tools/register.d.ts.map +1 -1
- package/dist/mcp-tools/register.js +100 -104
- package/dist/mcp-tools/register.js.map +1 -1
- package/dist/mcp-tools/test-harness.d.ts +5 -5
- package/dist/mcp-tools/test-harness.d.ts.map +1 -1
- package/dist/mcp-tools/test-harness.js +15 -21
- package/dist/mcp-tools/test-harness.js.map +1 -1
- package/dist/models/database.d.ts +2 -0
- package/dist/models/database.d.ts.map +1 -1
- package/dist/models/database.js +5 -0
- package/dist/models/database.js.map +1 -1
- package/dist/playbooks/stub-sync.d.ts +9 -0
- package/dist/playbooks/stub-sync.d.ts.map +1 -1
- package/dist/playbooks/stub-sync.js +51 -12
- package/dist/playbooks/stub-sync.js.map +1 -1
- package/dist/routes/decks.d.ts.map +1 -1
- package/dist/routes/decks.js +0 -1
- package/dist/routes/decks.js.map +1 -1
- package/dist/routes/execution-authority.d.ts.map +1 -1
- package/dist/routes/execution-authority.js +115 -28
- package/dist/routes/execution-authority.js.map +1 -1
- package/dist/routes/launch.d.ts +8 -0
- package/dist/routes/launch.d.ts.map +1 -0
- package/dist/routes/launch.js +35 -0
- package/dist/routes/launch.js.map +1 -0
- package/dist/routes/scope.d.ts.map +1 -1
- package/dist/routes/scope.js +1 -39
- package/dist/routes/scope.js.map +1 -1
- package/dist/routes/services.d.ts.map +1 -1
- package/dist/routes/services.js +1 -7
- package/dist/routes/services.js.map +1 -1
- package/dist/routes/trusted-session.d.ts.map +1 -1
- package/dist/routes/trusted-session.js +50 -152
- package/dist/routes/trusted-session.js.map +1 -1
- package/dist/scope/live-display-registry.d.ts +0 -2
- package/dist/scope/live-display-registry.d.ts.map +1 -1
- package/dist/scope/live-display-registry.js +0 -1
- package/dist/scope/live-display-registry.js.map +1 -1
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -5
- package/dist/server/index.js.map +1 -1
- package/dist/test/auth-fixtures.d.ts +1 -1
- package/dist/test/auth-fixtures.d.ts.map +1 -1
- package/dist/test/auth-fixtures.js +2 -11
- package/dist/test/auth-fixtures.js.map +1 -1
- package/dist/trusted-session/auth.d.ts +1 -7
- package/dist/trusted-session/auth.d.ts.map +1 -1
- package/dist/trusted-session/auth.js +7 -54
- package/dist/trusted-session/auth.js.map +1 -1
- package/dist/trusted-session/policy-hook.d.ts.map +1 -1
- package/dist/trusted-session/policy-hook.js +1 -51
- package/dist/trusted-session/policy-hook.js.map +1 -1
- package/dist/trusted-session/route-policy-registry.d.ts.map +1 -1
- package/dist/trusted-session/route-policy-registry.js +5 -50
- package/dist/trusted-session/route-policy-registry.js.map +1 -1
- package/dist/trusted-session/store.d.ts +12 -45
- package/dist/trusted-session/store.d.ts.map +1 -1
- package/dist/trusted-session/store.js +66 -208
- package/dist/trusted-session/store.js.map +1 -1
- package/package.json +2 -2
- package/static-ui/assets/{index-DhP_UPyM.js → index-Bb9ckehB.js} +1 -1
- package/static-ui/index.html +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-session-binding.js","sourceRoot":"","sources":["../src/mcp-session-binding.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"mcp-session-binding.js","sourceRoot":"","sources":["../src/mcp-session-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,9 +21,6 @@ export declare function mcpContractError(error_code: string, message: string, co
|
|
|
21
21
|
}[];
|
|
22
22
|
isError: boolean;
|
|
23
23
|
};
|
|
24
|
-
/** Control-plane ops are never available under short-lived execution authority. */
|
|
25
|
-
export declare function denyControlPlaneUnderAuthority(host: McpToolHost): ReturnType<typeof mcpContractError> | null;
|
|
26
|
-
export declare function authorizeExecutionAuthorityTool(host: McpToolHost, serviceId: string, toolName: string): Promise<ReturnType<typeof mcpContractError> | null>;
|
|
27
24
|
/** Reads live runtime session mode from backend (source of truth after dashboard approval). */
|
|
28
25
|
export declare function requireMcpAdmin(host: McpToolHost): Promise<ReturnType<typeof mcpPolicyError> | null>;
|
|
29
26
|
export declare function requireMcpDashboard(): ReturnType<typeof mcpPolicyError> | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/mcp-tools/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/mcp-tools/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO;;;;;;EAWhD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,uBAAuB;;;;;;EAM3D;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;EAU1G;AAmBD,+FAA+F;AAC/F,wBAAsB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,GAAG,IAAI,CAAC,CAa1G;AAED,wBAAgB,mBAAmB,IAAI,UAAU,CAAC,OAAO,cAAc,CAAC,GAAG,IAAI,CAK9E"}
|
package/dist/mcp-tools/policy.js
CHANGED
|
@@ -3,26 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.formatMcpToolError = formatMcpToolError;
|
|
4
4
|
exports.mcpPolicyError = mcpPolicyError;
|
|
5
5
|
exports.mcpContractError = mcpContractError;
|
|
6
|
-
exports.denyControlPlaneUnderAuthority = denyControlPlaneUnderAuthority;
|
|
7
|
-
exports.authorizeExecutionAuthorityTool = authorizeExecutionAuthorityTool;
|
|
8
6
|
exports.requireMcpAdmin = requireMcpAdmin;
|
|
9
7
|
exports.requireMcpDashboard = requireMcpDashboard;
|
|
10
8
|
const shared_1 = require("@agent-deck/shared");
|
|
11
9
|
const backend_api_error_1 = require("../lib/backend-api-error");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'AUTHORITY_REVOKED',
|
|
15
|
-
'AUTHORITY_UNKNOWN',
|
|
16
|
-
'AUTHORITY_SECRET_INVALID',
|
|
17
|
-
'AUDIENCE_MISMATCH',
|
|
18
|
-
'ENROLLMENT_REVOKED',
|
|
19
|
-
'COORDINATOR_NOT_ENROLLED',
|
|
20
|
-
'INTERACTION_REQUIRED',
|
|
21
|
-
'RESOURCE_OUT_OF_SCOPE',
|
|
22
|
-
]);
|
|
10
|
+
/** Codes that MCP surfaces as `{ok:false,error_code}` rather than trusted-session shape. */
|
|
11
|
+
const CONTRACT_SHAPED_CODES = new Set(['RESOURCE_OUT_OF_SCOPE']);
|
|
23
12
|
function formatMcpToolError(error) {
|
|
24
13
|
if (error instanceof backend_api_error_1.BackendApiError && error.errorCode) {
|
|
25
|
-
if (
|
|
14
|
+
if (CONTRACT_SHAPED_CODES.has(error.errorCode)) {
|
|
26
15
|
return mcpContractError(error.errorCode, error.message);
|
|
27
16
|
}
|
|
28
17
|
return mcpPolicyError(error.errorCode);
|
|
@@ -50,34 +39,6 @@ function mcpContractError(error_code, message, correlation) {
|
|
|
50
39
|
isError: true,
|
|
51
40
|
};
|
|
52
41
|
}
|
|
53
|
-
/** Control-plane ops are never available under short-lived execution authority. */
|
|
54
|
-
function denyControlPlaneUnderAuthority(host) {
|
|
55
|
-
const auth = host.sessionBinding.getExecutionAuthority?.(host.getSessionId());
|
|
56
|
-
if (!auth)
|
|
57
|
-
return null;
|
|
58
|
-
return mcpContractError('INTERACTION_REQUIRED', 'Control-plane decision required; do not hold the worker', { authorityId: auth.authorityId, requestId: `req_authority_control_${Date.now()}` });
|
|
59
|
-
}
|
|
60
|
-
async function authorizeExecutionAuthorityTool(host, serviceId, toolName) {
|
|
61
|
-
const auth = host.sessionBinding.getExecutionAuthority?.(host.getSessionId());
|
|
62
|
-
if (!auth)
|
|
63
|
-
return null;
|
|
64
|
-
const res = await fetch(`${host.backendUrl}/api/execution-authority/authorize-call`, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
67
|
-
body: JSON.stringify({
|
|
68
|
-
authorityId: auth.authorityId,
|
|
69
|
-
authoritySecret: auth.authoritySecret,
|
|
70
|
-
audience: auth.audience,
|
|
71
|
-
serviceId,
|
|
72
|
-
toolName,
|
|
73
|
-
}),
|
|
74
|
-
});
|
|
75
|
-
const body = (await res.json());
|
|
76
|
-
if (!res.ok || !body.ok) {
|
|
77
|
-
return mcpContractError(body.error_code ?? 'RESOURCE_OUT_OF_SCOPE', body.message ?? 'Authority denied tool call', body.correlation);
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
42
|
function bodyMessage(code) {
|
|
82
43
|
switch (code) {
|
|
83
44
|
case 'ADMIN_REQUIRED':
|
|
@@ -85,9 +46,11 @@ function bodyMessage(code) {
|
|
|
85
46
|
case 'DASHBOARD_REQUIRED':
|
|
86
47
|
return 'Operation is never available to an agent';
|
|
87
48
|
case 'GRANT_REQUIRED':
|
|
88
|
-
return 'No
|
|
49
|
+
return 'No deck selected for this connection';
|
|
89
50
|
case 'RESOURCE_OUT_OF_SCOPE':
|
|
90
51
|
return 'Resource is outside the bound deck';
|
|
52
|
+
case 'DECK_FIXED':
|
|
53
|
+
return "This connection's deck was set when it was launched and cannot be changed by the agent. Change it where the connection is configured (for example the Agent Dealer profile).";
|
|
91
54
|
default:
|
|
92
55
|
return code;
|
|
93
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/mcp-tools/policy.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/mcp-tools/policy.ts"],"names":[],"mappings":";;AAQA,gDAWC;AAED,wCAMC;AAED,4CAUC;AAoBD,0CAaC;AAED,kDAKC;AA/ED,+CAAuF;AAEvF,gEAA2D;AAG3D,4FAA4F;AAC5F,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAEjE,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,YAAY,mCAAe,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACxD,IAAI,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/C,OAAO,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACpF,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,IAA6B;IAC1D,MAAM,IAAI,GAAG,IAAA,4BAAmB,EAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,UAAkB,EAAE,OAAe,EAAE,WAAqC;IACzG,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;aACtE;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAA6B;IAChD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,gBAAgB;YACnB,OAAO,kCAAkC,CAAC;QAC5C,KAAK,oBAAoB;YACvB,OAAO,0CAA0C,CAAC;QACpD,KAAK,gBAAgB;YACnB,OAAO,sCAAsC,CAAC;QAChD,KAAK,uBAAuB;YAC1B,OAAO,oCAAoC,CAAC;QAC9C,KAAK,YAAY;YACf,OAAO,8KAA8K,CAAC;QACxL;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,+FAA+F;AACxF,KAAK,UAAU,eAAe,CAAC,IAAiB;IACrD,IAAI,OAAO,CAAC,GAAG,CAAC,+BAA+B,KAAK,GAAG,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACpD,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YAC3B,OAAO,cAAc,CAAC,gBAAgB,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC,gBAAgB,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,SAAgB,mBAAmB;IACjC,IAAI,OAAO,CAAC,GAAG,CAAC,+BAA+B,KAAK,GAAG,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,cAAc,CAAC,oBAAoB,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -44,11 +44,7 @@ export type McpToolHost = {
|
|
|
44
44
|
workspaceRoot?: string;
|
|
45
45
|
mode?: 'normal' | 'agent-admin';
|
|
46
46
|
}): void;
|
|
47
|
-
|
|
48
|
-
authorityId: string;
|
|
49
|
-
authoritySecret: string;
|
|
50
|
-
audience: string;
|
|
51
|
-
} | undefined;
|
|
47
|
+
isLaunchSession(sessionId: string): boolean;
|
|
52
48
|
hasSessionDeckOverride(sessionId: string): boolean;
|
|
53
49
|
};
|
|
54
50
|
badgeBySession: Map<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/mcp-tools/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/mcp-tools/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.registerMcpTools = registerMcpTools;
|
|
4
7
|
exports.listToolNamesForProfile = listToolNamesForProfile;
|
|
5
8
|
const zod_1 = require("zod");
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
6
11
|
const shared_1 = require("@agent-deck/shared");
|
|
12
|
+
const stub_sync_1 = require("../playbooks/stub-sync");
|
|
7
13
|
const mcp_session_binding_1 = require("../mcp-session-binding");
|
|
8
14
|
const deck_card_ops_1 = require("./deck-card-ops");
|
|
9
15
|
const profile_1 = require("./profile");
|
|
10
16
|
const policy_1 = require("./policy");
|
|
11
17
|
const backend_api_error_1 = require("../lib/backend-api-error");
|
|
12
18
|
const cardTypeSchema = zod_1.z.enum(['service', 'credential', 'playbook']);
|
|
19
|
+
function assignmentFileExists(workspaceRoot) {
|
|
20
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(workspaceRoot, '.agent-deck', 'use.json'));
|
|
21
|
+
}
|
|
22
|
+
function shouldSyncStubsOnBind(host, sessionId, workspaceRoot) {
|
|
23
|
+
// Grant sessions always sync. Launch sessions sync only when the folder has an assignment
|
|
24
|
+
// (IDE). Dealer worktrees without use.json keep skipping stub sync (NOT-105 / NOT-108).
|
|
25
|
+
if (!host.sessionBinding.isLaunchSession(sessionId)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return assignmentFileExists(workspaceRoot);
|
|
29
|
+
}
|
|
13
30
|
function isOutOfScopeError(error) {
|
|
14
31
|
return error instanceof backend_api_error_1.BackendApiError && error.errorCode === 'RESOURCE_OUT_OF_SCOPE';
|
|
15
32
|
}
|
|
@@ -17,8 +34,10 @@ function isOutOfScopeError(error) {
|
|
|
17
34
|
* Resolve deck id/name for bind/switch.
|
|
18
35
|
* Normal agents may only GET the bound deck; out-of-scope requires elevation,
|
|
19
36
|
* then a retry of GET /api/decks/:id (agent-admin is allowed to read any deck).
|
|
37
|
+
*
|
|
38
|
+
* Launch sessions without an assignment file stay DECK_FIXED (NOT-108).
|
|
20
39
|
*/
|
|
21
|
-
async function resolveDeckForBind(host, deckRef) {
|
|
40
|
+
async function resolveDeckForBind(host, deckRef, workspaceRoot) {
|
|
22
41
|
try {
|
|
23
42
|
return await host.fetchDeck(deckRef);
|
|
24
43
|
}
|
|
@@ -26,6 +45,11 @@ async function resolveDeckForBind(host, deckRef) {
|
|
|
26
45
|
if (!isOutOfScopeError(error)) {
|
|
27
46
|
throw error;
|
|
28
47
|
}
|
|
48
|
+
if (host.sessionBinding.isLaunchSession(host.getSessionId())) {
|
|
49
|
+
if (!workspaceRoot || !assignmentFileExists(workspaceRoot)) {
|
|
50
|
+
return (0, policy_1.mcpPolicyError)('DECK_FIXED');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
29
53
|
const denied = await (0, policy_1.requireMcpAdmin)(host);
|
|
30
54
|
if (denied) {
|
|
31
55
|
return denied;
|
|
@@ -42,26 +66,7 @@ async function resolveDeckForBind(host, deckRef) {
|
|
|
42
66
|
}
|
|
43
67
|
}
|
|
44
68
|
}
|
|
45
|
-
/** Tools allowed under short-lived execution authority (everything else → INTERACTION_REQUIRED). */
|
|
46
|
-
const EXECUTION_AUTHORITY_ALLOWED_TOOLS = new Set([
|
|
47
|
-
'get_session_binding',
|
|
48
|
-
'get_bound_deck',
|
|
49
|
-
'list_service_tools',
|
|
50
|
-
'call_service_tool',
|
|
51
|
-
'get_playbook',
|
|
52
|
-
]);
|
|
53
69
|
function registerMcpTools(host) {
|
|
54
|
-
const rawRegister = host.registerTool.bind(host);
|
|
55
|
-
host.registerTool = (name, config, handler) => {
|
|
56
|
-
rawRegister(name, config, async (...args) => {
|
|
57
|
-
if (!EXECUTION_AUTHORITY_ALLOWED_TOOLS.has(name)) {
|
|
58
|
-
const blocked = (0, policy_1.denyControlPlaneUnderAuthority)(host);
|
|
59
|
-
if (blocked)
|
|
60
|
-
return blocked;
|
|
61
|
-
}
|
|
62
|
-
return handler(...args);
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
70
|
registerRuntimeTools(host);
|
|
66
71
|
if ((0, profile_1.profileIncludes)(host.profile, 'editing')) {
|
|
67
72
|
registerEditingTools(host);
|
|
@@ -81,35 +86,56 @@ function registerRuntimeTools(host) {
|
|
|
81
86
|
},
|
|
82
87
|
}, async ({ workspaceRoot, deckId }) => {
|
|
83
88
|
try {
|
|
84
|
-
const blocked = (0, policy_1.denyControlPlaneUnderAuthority)(host);
|
|
85
|
-
if (blocked)
|
|
86
|
-
return blocked;
|
|
87
89
|
const sessionId = host.getSessionId();
|
|
88
90
|
const current = host.sessionBinding.getBinding(sessionId);
|
|
89
|
-
const
|
|
91
|
+
const isLaunch = host.sessionBinding.isLaunchSession(sessionId);
|
|
92
|
+
const resolved = await resolveDeckForBind(host, deckId, workspaceRoot);
|
|
90
93
|
if ('isError' in resolved) {
|
|
91
94
|
return resolved;
|
|
92
95
|
}
|
|
93
96
|
const deck = resolved;
|
|
94
97
|
let bindResult;
|
|
95
98
|
if (current.runtimeSessionId) {
|
|
96
|
-
if (current.deckId && current.deckId !== deck.id) {
|
|
99
|
+
if (isLaunch && current.deckId && current.deckId !== deck.id) {
|
|
100
|
+
if (!assignmentFileExists(workspaceRoot)) {
|
|
101
|
+
return (0, policy_1.mcpPolicyError)('DECK_FIXED');
|
|
102
|
+
}
|
|
97
103
|
const denied = await (0, policy_1.requireMcpAdmin)(host);
|
|
98
104
|
if (denied) {
|
|
99
105
|
return denied;
|
|
100
106
|
}
|
|
107
|
+
bindResult = (await host.callBackendAPI('/api/trusted-session/bind-workspace', {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
110
|
+
body: JSON.stringify({ workspaceRoot, deckId: deck.id, updateAssignment: true }),
|
|
111
|
+
}));
|
|
112
|
+
host.sessionBinding.setTrustedSession(sessionId, {
|
|
113
|
+
runtimeSessionId: current.runtimeSessionId,
|
|
114
|
+
deckId: String(bindResult?.deckId ?? deck.id),
|
|
115
|
+
workspaceRoot,
|
|
116
|
+
mode: bindResult?.mode ?? current.mode ?? 'normal',
|
|
117
|
+
});
|
|
118
|
+
(0, stub_sync_1.healUseManifest)(workspaceRoot, deck);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
if (current.deckId && current.deckId !== deck.id) {
|
|
122
|
+
const denied = await (0, policy_1.requireMcpAdmin)(host);
|
|
123
|
+
if (denied) {
|
|
124
|
+
return denied;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
bindResult = (await host.callBackendAPI('/api/trusted-session/bind-workspace', {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify({ workspaceRoot, deckId: deck.id }),
|
|
131
|
+
}));
|
|
132
|
+
host.sessionBinding.setTrustedSession(sessionId, {
|
|
133
|
+
runtimeSessionId: current.runtimeSessionId,
|
|
134
|
+
deckId: String(bindResult?.deckId ?? deck.id),
|
|
135
|
+
workspaceRoot,
|
|
136
|
+
mode: bindResult?.mode ?? current.mode ?? 'normal',
|
|
137
|
+
});
|
|
101
138
|
}
|
|
102
|
-
bindResult = (await host.callBackendAPI('/api/trusted-session/bind-workspace', {
|
|
103
|
-
method: 'POST',
|
|
104
|
-
headers: { 'Content-Type': 'application/json' },
|
|
105
|
-
body: JSON.stringify({ workspaceRoot, deckId: deck.id }),
|
|
106
|
-
}));
|
|
107
|
-
host.sessionBinding.setTrustedSession(sessionId, {
|
|
108
|
-
runtimeSessionId: current.runtimeSessionId,
|
|
109
|
-
deckId: String(bindResult?.deckId ?? deck.id),
|
|
110
|
-
workspaceRoot,
|
|
111
|
-
mode: bindResult?.mode ?? current.mode ?? 'normal',
|
|
112
|
-
});
|
|
113
139
|
}
|
|
114
140
|
else {
|
|
115
141
|
if (current.deckId && current.deckId !== deck.id) {
|
|
@@ -122,26 +148,25 @@ function registerRuntimeTools(host) {
|
|
|
122
148
|
host.sessionBinding.setDeckId(sessionId, deck.id);
|
|
123
149
|
}
|
|
124
150
|
let stubSync = null;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
151
|
+
if (shouldSyncStubsOnBind(host, sessionId, workspaceRoot)) {
|
|
152
|
+
try {
|
|
153
|
+
stubSync = await host.syncWorkspaceOnBind(workspaceRoot, deck);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
stubSync = null;
|
|
157
|
+
}
|
|
130
158
|
}
|
|
131
159
|
await host.registerLiveDisplay(sessionId);
|
|
132
160
|
const payload = await host.buildBindingPayload(sessionId);
|
|
133
161
|
const response = {
|
|
134
162
|
...payload,
|
|
135
163
|
deck_name: deck.name,
|
|
136
|
-
message: bindResult?.
|
|
137
|
-
? '
|
|
164
|
+
message: bindResult?.assignmentUpdated
|
|
165
|
+
? 'Folder assignment updated to new deck.'
|
|
138
166
|
: 'Session bound to deck.',
|
|
139
167
|
};
|
|
140
|
-
if (bindResult?.
|
|
141
|
-
response.
|
|
142
|
-
response.peers_revoked = bindResult.peersRevoked;
|
|
143
|
-
response.previous_deck_workspace_count = bindResult.previousDeckWorkspaceCount;
|
|
144
|
-
response.grant_refresh_note = bindResult.grantRefreshNote;
|
|
168
|
+
if (bindResult?.assignmentUpdated) {
|
|
169
|
+
response.assignment_updated = true;
|
|
145
170
|
}
|
|
146
171
|
if (stubSync) {
|
|
147
172
|
response.stubs = {
|
|
@@ -173,18 +198,12 @@ function registerRuntimeTools(host) {
|
|
|
173
198
|
});
|
|
174
199
|
r('switch_bound_deck', {
|
|
175
200
|
title: 'Switch Bound Deck',
|
|
176
|
-
description: 'Persistently bind this workspace to another deck (requires agent-admin).
|
|
201
|
+
description: 'Persistently bind this workspace to another deck (requires agent-admin). Updates the folder assignment when present.',
|
|
177
202
|
inputSchema: { deckId: zod_1.z.string().min(1) },
|
|
178
203
|
}, async ({ deckId }) => {
|
|
179
|
-
const denied = await (0, policy_1.requireMcpAdmin)(host);
|
|
180
|
-
if (denied) {
|
|
181
|
-
return denied;
|
|
182
|
-
}
|
|
183
204
|
try {
|
|
184
205
|
const sessionId = host.getSessionId();
|
|
185
206
|
const snapshot = host.sessionBinding.getBinding(sessionId);
|
|
186
|
-
// Already elevated above — GET /api/decks/:id allows agent-admin any deck.
|
|
187
|
-
const deck = await host.fetchDeck(deckId);
|
|
188
207
|
const workspaceRoot = snapshot.workspaceRoot;
|
|
189
208
|
if (!workspaceRoot) {
|
|
190
209
|
return host.toolError(new Error('workspaceRoot missing — call bind_workspace first'));
|
|
@@ -192,19 +211,36 @@ function registerRuntimeTools(host) {
|
|
|
192
211
|
if (!snapshot.runtimeSessionId) {
|
|
193
212
|
return host.toolError(new Error('GRANT_REQUIRED'));
|
|
194
213
|
}
|
|
195
|
-
const
|
|
214
|
+
const isLaunch = host.sessionBinding.isLaunchSession(sessionId);
|
|
215
|
+
if (isLaunch && !assignmentFileExists(workspaceRoot)) {
|
|
216
|
+
return (0, policy_1.mcpPolicyError)('DECK_FIXED');
|
|
217
|
+
}
|
|
218
|
+
const denied = await (0, policy_1.requireMcpAdmin)(host);
|
|
219
|
+
if (denied) {
|
|
220
|
+
return denied;
|
|
221
|
+
}
|
|
222
|
+
// Already elevated above — GET /api/decks/:id allows agent-admin any deck.
|
|
223
|
+
const deck = await host.fetchDeck(deckId);
|
|
224
|
+
const bound = (await host.callBackendAPI('/api/trusted-session/bind-workspace', {
|
|
196
225
|
method: 'POST',
|
|
197
226
|
headers: { 'Content-Type': 'application/json' },
|
|
198
|
-
body: JSON.stringify({
|
|
199
|
-
|
|
227
|
+
body: JSON.stringify({
|
|
228
|
+
workspaceRoot,
|
|
229
|
+
deckId: deck.id,
|
|
230
|
+
...(isLaunch ? { updateAssignment: true } : {}),
|
|
231
|
+
}),
|
|
232
|
+
}));
|
|
200
233
|
host.sessionBinding.setTrustedSession(sessionId, {
|
|
201
234
|
runtimeSessionId: snapshot.runtimeSessionId,
|
|
202
235
|
deckId: String(bound.deckId ?? deck.id),
|
|
203
236
|
workspaceRoot,
|
|
204
237
|
mode: bound.mode ?? 'agent-admin',
|
|
205
238
|
});
|
|
239
|
+
if (isLaunch || assignmentFileExists(workspaceRoot)) {
|
|
240
|
+
(0, stub_sync_1.healUseManifest)(workspaceRoot, deck);
|
|
241
|
+
}
|
|
206
242
|
let stubSync = null;
|
|
207
|
-
if (workspaceRoot) {
|
|
243
|
+
if (shouldSyncStubsOnBind(host, sessionId, workspaceRoot)) {
|
|
208
244
|
try {
|
|
209
245
|
stubSync = await host.syncWorkspaceOnBind(workspaceRoot, deck);
|
|
210
246
|
}
|
|
@@ -217,15 +253,12 @@ function registerRuntimeTools(host) {
|
|
|
217
253
|
const response = {
|
|
218
254
|
...payload,
|
|
219
255
|
deck_name: deck.name,
|
|
220
|
-
message: bound.
|
|
221
|
-
? '
|
|
256
|
+
message: bound.assignmentUpdated
|
|
257
|
+
? 'Folder assignment updated to new deck.'
|
|
222
258
|
: 'Already bound to this deck.',
|
|
223
259
|
};
|
|
224
|
-
if (bound.
|
|
225
|
-
response.
|
|
226
|
-
response.peers_revoked = bound.peersRevoked;
|
|
227
|
-
response.previous_deck_workspace_count = bound.previousDeckWorkspaceCount;
|
|
228
|
-
response.grant_refresh_note = bound.grantRefreshNote;
|
|
260
|
+
if (bound.assignmentUpdated) {
|
|
261
|
+
response.assignment_updated = true;
|
|
229
262
|
}
|
|
230
263
|
if (stubSync) {
|
|
231
264
|
response.stubs = {
|
|
@@ -359,9 +392,6 @@ function registerRuntimeTools(host) {
|
|
|
359
392
|
inputSchema: {},
|
|
360
393
|
}, async () => {
|
|
361
394
|
try {
|
|
362
|
-
const blocked = (0, policy_1.denyControlPlaneUnderAuthority)(host);
|
|
363
|
-
if (blocked)
|
|
364
|
-
return blocked;
|
|
365
395
|
const binding = host.sessionBinding.getBinding(host.getSessionId());
|
|
366
396
|
const runtimeSessionId = binding.runtimeSessionId;
|
|
367
397
|
if (!runtimeSessionId) {
|
|
@@ -384,9 +414,6 @@ function registerRuntimeTools(host) {
|
|
|
384
414
|
inputSchema: {},
|
|
385
415
|
}, async () => {
|
|
386
416
|
try {
|
|
387
|
-
const blocked = (0, policy_1.denyControlPlaneUnderAuthority)(host);
|
|
388
|
-
if (blocked)
|
|
389
|
-
return blocked;
|
|
390
417
|
const binding = host.sessionBinding.getBinding(host.getSessionId());
|
|
391
418
|
const runtimeSessionId = binding.runtimeSessionId;
|
|
392
419
|
if (!runtimeSessionId) {
|
|
@@ -492,36 +519,6 @@ function registerRuntimeTools(host) {
|
|
|
492
519
|
inputSchema: { serviceId: zod_1.z.string() },
|
|
493
520
|
}, async ({ serviceId }) => {
|
|
494
521
|
try {
|
|
495
|
-
const auth = host.sessionBinding.getExecutionAuthority?.(host.getSessionId());
|
|
496
|
-
if (auth) {
|
|
497
|
-
const connect = await fetch(`${host.backendUrl}/api/execution-authority/mcp/connect`, {
|
|
498
|
-
method: 'POST',
|
|
499
|
-
headers: {
|
|
500
|
-
'Content-Type': 'application/json',
|
|
501
|
-
Authorization: `Bearer ${auth.authorityId}:${auth.authoritySecret}`,
|
|
502
|
-
},
|
|
503
|
-
body: JSON.stringify({
|
|
504
|
-
authorityId: auth.authorityId,
|
|
505
|
-
authoritySecret: auth.authoritySecret,
|
|
506
|
-
audience: auth.audience,
|
|
507
|
-
}),
|
|
508
|
-
});
|
|
509
|
-
const body = (await connect.json());
|
|
510
|
-
if (!connect.ok || !body.ok) {
|
|
511
|
-
return (0, policy_1.mcpContractError)(body.error_code ?? 'AUTHORITY_SECRET_INVALID', body.message ?? 'Authority invalid', { authorityId: auth.authorityId });
|
|
512
|
-
}
|
|
513
|
-
if (!body.data?.allowedServices?.includes(serviceId)) {
|
|
514
|
-
return host.toolError(new backend_api_error_1.BackendApiError('Service is not on the bound deck', 403, 'RESOURCE_OUT_OF_SCOPE'));
|
|
515
|
-
}
|
|
516
|
-
const tools = await host.callBackendAPI(`/api/services/${serviceId}/tools`);
|
|
517
|
-
const allowedNames = new Set((body.data.allowedTools ?? [])
|
|
518
|
-
.filter((t) => t.serviceId === serviceId)
|
|
519
|
-
.map((t) => t.toolName));
|
|
520
|
-
const filtered = Array.isArray(tools)
|
|
521
|
-
? tools.filter((t) => typeof t.name === 'string' && allowedNames.has(t.name))
|
|
522
|
-
: tools;
|
|
523
|
-
return host.toolResult(filtered);
|
|
524
|
-
}
|
|
525
522
|
const tools = await host.callBackendAPI(`/api/services/${serviceId}/tools`);
|
|
526
523
|
return host.toolResult(tools);
|
|
527
524
|
}
|
|
@@ -539,8 +536,6 @@ function registerRuntimeTools(host) {
|
|
|
539
536
|
},
|
|
540
537
|
}, async ({ serviceId, toolName, arguments: args = {} }) => {
|
|
541
538
|
try {
|
|
542
|
-
// Authorization + audit happen once in POST /api/services/:id/call
|
|
543
|
-
// (authorizeAuthorityServiceCall → ledger). Do not preflight authorize-call here.
|
|
544
539
|
let normalizedArgs = args;
|
|
545
540
|
if (typeof normalizedArgs === 'string' && normalizedArgs.length > 0) {
|
|
546
541
|
try {
|
|
@@ -915,6 +910,7 @@ function listToolNamesForProfile(profile) {
|
|
|
915
910
|
setWorkspace: () => { },
|
|
916
911
|
setDeckId: () => { },
|
|
917
912
|
setTrustedSession: () => { },
|
|
913
|
+
isLaunchSession: () => false,
|
|
918
914
|
hasSessionDeckOverride: () => false,
|
|
919
915
|
},
|
|
920
916
|
badgeBySession: new Map(),
|