@suveren/gateway 0.35.2 → 0.36.1
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/bin/suveren-gateway.js +28 -1
- package/content/integrations/calendar.json +2 -0
- package/content/integrations/erp.json +5 -3
- package/content/integrations/gmail.json +2 -0
- package/content/integrations/linkedin.json +3 -0
- package/content/integrations/mail.json +9 -0
- package/dist/control-plane/index.mjs +8 -0
- package/dist/mcp-server/http.mjs +94 -37
- package/dist/ui/assets/index-BQ5Pr6As.js +123 -0
- package/dist/ui/index.html +1 -1
- package/lib/autostart-templates.d.mts +7 -0
- package/lib/autostart-templates.mjs +30 -0
- package/node_modules/@hap/core/dist/index.d.mts +25 -1
- package/node_modules/@hap/core/dist/index.d.ts +25 -1
- package/node_modules/@hap/core/dist/index.js +18 -0
- package/node_modules/@hap/core/dist/index.mjs +16 -0
- package/node_modules/@hap/core/package.json +1 -1
- package/node_modules/@hap/core/src/content-binding.ts +26 -1
- package/node_modules/@hap/core/src/types.ts +17 -0
- package/node_modules/postcss/lib/processor.js +1 -1
- package/node_modules/postcss/lib/stringify.js +30 -8
- package/node_modules/postcss/package.json +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-BONNmuPw.js +0 -123
package/bin/suveren-gateway.js
CHANGED
|
@@ -16,7 +16,7 @@ import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, writeFileSy
|
|
|
16
16
|
import { homedir, platform, userInfo } from 'node:os';
|
|
17
17
|
import { dirname, join, resolve } from 'node:path';
|
|
18
18
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
19
|
-
import { buildLaunchAgentPlist, buildMacLauncher, buildSystemdUnit, buildWindowsTaskXml } from '../lib/autostart-templates.mjs';
|
|
19
|
+
import { buildLaunchAgentPlist, buildMacLauncher, buildSystemdUnit, buildWindowsTaskXml, serviceOwnsInstall } from '../lib/autostart-templates.mjs';
|
|
20
20
|
import { DEFAULT_AS_URL, readConfig, writeConfig, validateAsUrl, validateCaFile, validateProxyUrl, validatePinTls, resolveAsUrl, resolvePinTls, resolvePinTlsExpectedFingerprint, resolveSimulation } from '../lib/config.mjs';
|
|
21
21
|
import { createInterface } from 'node:readline/promises';
|
|
22
22
|
import { readPairing as readAsPairing, recordTlsPin, formatFingerprint, normalizeFingerprint } from '../lib/as-pairing.mjs';
|
|
@@ -1038,7 +1038,27 @@ const LAUNCH_AGENT_LABEL = 'ai.suveren.gateway';
|
|
|
1038
1038
|
* it could not see. Every command has to know about the service the same CLI
|
|
1039
1039
|
* installs.
|
|
1040
1040
|
*/
|
|
1041
|
+
function serviceDefinitionText() {
|
|
1042
|
+
const os = platform();
|
|
1043
|
+
const file = os === 'darwin' ? launchAgentPath() : os === 'linux' ? systemdUnitPath() : null;
|
|
1044
|
+
if (!file) return null;
|
|
1045
|
+
try { return existsSync(file) ? readFileSync(file, 'utf8') : null; } catch { return null; }
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/** The login service runs this CLI's installation — see serviceOwnsInstall. */
|
|
1049
|
+
function serviceIsOurs() {
|
|
1050
|
+
return serviceOwnsInstall({
|
|
1051
|
+
definitionText: serviceDefinitionText(),
|
|
1052
|
+
dataDir: DATA_DIR,
|
|
1053
|
+
envDataDir: process.env.SUVEREN_DATA_DIR,
|
|
1054
|
+
same: (a, b) => resolve(a) === resolve(b),
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1041
1058
|
function serviceRunning() {
|
|
1059
|
+
// Another installation's service (a second gateway under this OS user) is
|
|
1060
|
+
// not "running" for this CLI — never stop, restart or report it as ours.
|
|
1061
|
+
if (!serviceIsOurs()) return false;
|
|
1042
1062
|
const os = platform();
|
|
1043
1063
|
if (os === 'darwin') {
|
|
1044
1064
|
const p = runQuiet('launchctl', ['print', `gui/${process.getuid()}/${LAUNCH_AGENT_LABEL}`]);
|
|
@@ -1104,6 +1124,13 @@ async function service(args) {
|
|
|
1104
1124
|
process.exit(2);
|
|
1105
1125
|
}
|
|
1106
1126
|
|
|
1127
|
+
if ((sub === 'install' || sub === 'uninstall') && !serviceIsOurs()) {
|
|
1128
|
+
console.error('The login service on this account belongs to another Suveren gateway installation');
|
|
1129
|
+
console.error(`(a different data folder than ${DATA_DIR}). Refusing to change it.`);
|
|
1130
|
+
console.error('Run this installation in the background with: suveren-gateway start --detach');
|
|
1131
|
+
process.exit(2);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1107
1134
|
switch (sub) {
|
|
1108
1135
|
case 'install': await impl.install(); break;
|
|
1109
1136
|
case 'uninstall': await impl.uninstall(); break;
|
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
}
|
|
123
123
|
},
|
|
124
124
|
"create_event": {
|
|
125
|
+
"message": { "text": "description", "verb": "Sent" },
|
|
125
126
|
"summary": "Event: {summary|short}[ on {start.dateTime,start.date|date}]",
|
|
126
127
|
"executionMapping": {
|
|
127
128
|
"duration_minutes": "duration",
|
|
@@ -144,6 +145,7 @@
|
|
|
144
145
|
"actionLabel": "Event created"
|
|
145
146
|
},
|
|
146
147
|
"update_event": {
|
|
148
|
+
"message": { "text": "description", "verb": "Sent" },
|
|
147
149
|
"summary": "Update event[: {summary|short}]",
|
|
148
150
|
"executionMapping": {
|
|
149
151
|
"attendees": [
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"icon": "briefcase",
|
|
7
7
|
"profile": "sales",
|
|
8
8
|
"npmPackage": "@humanagencyp/erp-mcp",
|
|
9
|
-
"npmVersion": "0.9.
|
|
9
|
+
"npmVersion": "0.9.4",
|
|
10
10
|
"mcp": {
|
|
11
11
|
"command": "erp-mcp",
|
|
12
12
|
"args": [],
|
|
@@ -113,11 +113,13 @@
|
|
|
113
113
|
"requiredValue": "unlimited"
|
|
114
114
|
},
|
|
115
115
|
"find_customers": {
|
|
116
|
+
"readAlsoUnder": ["invoicing"],
|
|
116
117
|
"category": "read",
|
|
117
118
|
"boundField": "read_access",
|
|
118
119
|
"requiredValue": "unlimited"
|
|
119
120
|
},
|
|
120
121
|
"get_customer": {
|
|
122
|
+
"readAlsoUnder": ["invoicing"],
|
|
121
123
|
"category": "read",
|
|
122
124
|
"boundField": "read_access",
|
|
123
125
|
"requiredValue": "unlimited"
|
|
@@ -276,7 +278,7 @@
|
|
|
276
278
|
],
|
|
277
279
|
"display": {
|
|
278
280
|
"order": ["customer_id", "order_id", "number", "document_date", "service_period", "lines", "net_total", "currency", "payment_term_days", "memo"],
|
|
279
|
-
"money": ["net_total", "customer_id.credit_limit", "customer_id.open_balance", "customer_id.available_credit"],
|
|
281
|
+
"money": ["net_total", "lines[].unit_price", "customer_id.credit_limit", "customer_id.open_balance", "customer_id.available_credit"],
|
|
280
282
|
"currencyField": "currency",
|
|
281
283
|
"labels": {
|
|
282
284
|
"customer_id": "Customer",
|
|
@@ -311,7 +313,7 @@
|
|
|
311
313
|
},
|
|
312
314
|
"display": {
|
|
313
315
|
"order": ["invoice_id", "number", "document_date", "credit_total", "currency", "reason", "service_period", "lines"],
|
|
314
|
-
"money": ["credit_total"],
|
|
316
|
+
"money": ["credit_total", "lines[].unit_price"],
|
|
315
317
|
"currencyField": "currency",
|
|
316
318
|
"labels": {
|
|
317
319
|
"invoice_id": "Invoice",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
"overrides": {
|
|
86
86
|
"send_message": {
|
|
87
|
+
"message": { "text": "body", "to": "to", "cc": "cc", "subject": "subject", "verb": "Sent" },
|
|
87
88
|
"summary": "Email to {to|list}: {subject|short}",
|
|
88
89
|
"executionMapping": {
|
|
89
90
|
"to": [
|
|
@@ -116,6 +117,7 @@
|
|
|
116
117
|
"display": { "aiText": "body" }
|
|
117
118
|
},
|
|
118
119
|
"create_draft": {
|
|
120
|
+
"message": { "text": "body", "to": "to", "cc": "cc", "subject": "subject", "verb": "Sent" },
|
|
119
121
|
"summary": "Draft email to {to|list}[: {subject|short}]",
|
|
120
122
|
"executionMapping": {
|
|
121
123
|
"to": [
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"overrides": {
|
|
64
64
|
"create_post": {
|
|
65
|
+
"message": { "text": "text", "verb": "Published" },
|
|
65
66
|
"summary": "LinkedIn post: {text|short}",
|
|
66
67
|
"executionMapping": {
|
|
67
68
|
"visibility": "audience"
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
"actionLabel": "Post published"
|
|
75
76
|
},
|
|
76
77
|
"create_image_post": {
|
|
78
|
+
"message": { "text": "text", "verb": "Published" },
|
|
77
79
|
"summary": "LinkedIn image post: {text|short}",
|
|
78
80
|
"executionMapping": {
|
|
79
81
|
"visibility": "audience"
|
|
@@ -86,6 +88,7 @@
|
|
|
86
88
|
"actionLabel": "Image post published"
|
|
87
89
|
},
|
|
88
90
|
"create_article_post": {
|
|
91
|
+
"message": { "text": "text", "verb": "Published" },
|
|
89
92
|
"summary": "LinkedIn article: {title,articleUrl|short}",
|
|
90
93
|
"executionMapping": {
|
|
91
94
|
"visibility": "audience"
|
|
@@ -85,7 +85,16 @@
|
|
|
85
85
|
"resultDatePath": "received_at"
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
+
"get_attachment": {
|
|
89
|
+
"category": "read",
|
|
90
|
+
"boundField": "read_access",
|
|
91
|
+
"requiredValue": "unlimited",
|
|
92
|
+
"read": {
|
|
93
|
+
"ageField": "read_age_days"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
88
96
|
"send_message": {
|
|
97
|
+
"message": { "text": "body", "to": "to", "cc": "cc", "subject": "subject", "verb": "Sent" },
|
|
89
98
|
"summary": "Email to {to|list}: {subject|short}",
|
|
90
99
|
"executionMapping": {
|
|
91
100
|
"to": [
|
|
@@ -3,6 +3,13 @@ import {
|
|
|
3
3
|
setPromptOverride
|
|
4
4
|
} from "./chunk-3BNWN6VP.mjs";
|
|
5
5
|
|
|
6
|
+
// src/lib/no-foreign-framing.ts
|
|
7
|
+
function noForeignFraming(_req, res, next) {
|
|
8
|
+
res.setHeader("X-Frame-Options", "SAMEORIGIN");
|
|
9
|
+
res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");
|
|
10
|
+
next();
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
// src/index.ts
|
|
7
14
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
8
15
|
import { existsSync as existsSync13, readFileSync as readFileSync18, renameSync as renameSync6 } from "fs";
|
|
@@ -5398,6 +5405,7 @@ var shownSnapshotStore = new ShownSnapshotStore(DATA_DIR);
|
|
|
5398
5405
|
var internalSecret2 = process.env.SUVEREN_INTERNAL_SECRET ?? randomBytes3(32).toString("hex");
|
|
5399
5406
|
setInternalSecret(internalSecret2);
|
|
5400
5407
|
var app = express();
|
|
5408
|
+
app.use(noForeignFraming);
|
|
5401
5409
|
var jsonParser = express.json();
|
|
5402
5410
|
var loginAttempts = /* @__PURE__ */ new Map();
|
|
5403
5411
|
var LOGIN_WINDOW_MS = 6e4;
|
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -3601,8 +3601,6 @@ function setAsBaseUrl(url) {
|
|
|
3601
3601
|
}
|
|
3602
3602
|
var OPERATOR_NAME = process.env.SUVEREN_OPERATOR_NAME ?? "Suveren";
|
|
3603
3603
|
var HAP_URL = "https://www.humanagencyprotocol.org/";
|
|
3604
|
-
var FOOTER_PROFILES = /* @__PURE__ */ new Set(["email", "calendar", "publish"]);
|
|
3605
|
-
var CONTENT_FIELD_CANDIDATES = ["body", "text", "description", "content"];
|
|
3606
3604
|
var FOOTER_RE = /\n*(?:—|--) (?:Sent|Published) by (?:an AI agent|[^\n]+?'s AI agent)[\s\S]*$/;
|
|
3607
3605
|
function shouldAttachFooter() {
|
|
3608
3606
|
return true;
|
|
@@ -3610,9 +3608,6 @@ function shouldAttachFooter() {
|
|
|
3610
3608
|
function receiptUrl(receiptId) {
|
|
3611
3609
|
return `${AS_BASE}/t/${receiptId}`;
|
|
3612
3610
|
}
|
|
3613
|
-
function verbForProfile(profile) {
|
|
3614
|
-
return profile === "publish" ? "Published" : "Sent";
|
|
3615
|
-
}
|
|
3616
3611
|
function hapLine() {
|
|
3617
3612
|
return `-- ${OPERATOR_NAME} implements HAP, the open Human Agency Protocol: ${HAP_URL}`;
|
|
3618
3613
|
}
|
|
@@ -3620,32 +3615,22 @@ function footerText(receiptId, verb, subject) {
|
|
|
3620
3615
|
const identity = deriveIdentityLine(subject, { operatorName: OPERATOR_NAME }).replace(/^Sent\b/, verb);
|
|
3621
3616
|
return `
|
|
3622
3617
|
|
|
3623
|
-
-- ${identity}.
|
|
3618
|
+
-- ${identity}. Ticket: ${receiptUrl(receiptId)}
|
|
3624
3619
|
|
|
3625
3620
|
${hapLine()}`;
|
|
3626
3621
|
}
|
|
3627
|
-
function isStringType(t) {
|
|
3628
|
-
return t === "string" || Array.isArray(t) && t.includes("string");
|
|
3629
|
-
}
|
|
3630
3622
|
function detectContentField(tool) {
|
|
3631
|
-
|
|
3632
|
-
const props = schema?.properties ?? {};
|
|
3633
|
-
for (const candidate of CONTENT_FIELD_CANDIDATES) {
|
|
3634
|
-
const prop = props[candidate];
|
|
3635
|
-
if (prop && isStringType(prop.type)) return candidate;
|
|
3636
|
-
}
|
|
3637
|
-
return null;
|
|
3623
|
+
return tool.gating?.message?.text ?? null;
|
|
3638
3624
|
}
|
|
3639
3625
|
function stripFooter(value) {
|
|
3640
3626
|
return value.replace(FOOTER_RE, "").replace(/\n+$/, "");
|
|
3641
3627
|
}
|
|
3642
3628
|
function appendVerificationFooter(tool, args, receiptId, subject) {
|
|
3643
|
-
const
|
|
3644
|
-
if (!
|
|
3645
|
-
const field =
|
|
3646
|
-
if (!field) return args;
|
|
3629
|
+
const message = tool.gating?.message;
|
|
3630
|
+
if (!message) return args;
|
|
3631
|
+
const field = message.text;
|
|
3647
3632
|
const current = typeof args[field] === "string" ? args[field] : "";
|
|
3648
|
-
return { ...args, [field]: stripFooter(current) + footerText(receiptId,
|
|
3633
|
+
return { ...args, [field]: stripFooter(current) + footerText(receiptId, message.verb, subject) };
|
|
3649
3634
|
}
|
|
3650
3635
|
|
|
3651
3636
|
// src/lib/content-binding.ts
|
|
@@ -4059,6 +4044,51 @@ function invalidManifestInternalReason(manifest) {
|
|
|
4059
4044
|
}
|
|
4060
4045
|
return null;
|
|
4061
4046
|
}
|
|
4047
|
+
var VALID_MESSAGE_VERBS = ["Sent", "Published"];
|
|
4048
|
+
function invalidMessageReason(toolName, entry) {
|
|
4049
|
+
if (entry === null || typeof entry !== "object") return null;
|
|
4050
|
+
const message = entry.message;
|
|
4051
|
+
if (message === void 0) return null;
|
|
4052
|
+
if (message === null || typeof message !== "object") {
|
|
4053
|
+
return `override "${toolName}".message must be an object`;
|
|
4054
|
+
}
|
|
4055
|
+
const m = message;
|
|
4056
|
+
const at = `override "${toolName}".message`;
|
|
4057
|
+
if (typeof m.text !== "string" || m.text.length === 0) {
|
|
4058
|
+
return `${at}.text must be a non-empty string`;
|
|
4059
|
+
}
|
|
4060
|
+
if (typeof m.verb !== "string" || !VALID_MESSAGE_VERBS.includes(m.verb)) {
|
|
4061
|
+
return `${at}.verb must be one of ${VALID_MESSAGE_VERBS.join(", ")}`;
|
|
4062
|
+
}
|
|
4063
|
+
for (const key of ["to", "cc", "subject"]) {
|
|
4064
|
+
if (m[key] !== void 0 && (typeof m[key] !== "string" || m[key].length === 0)) {
|
|
4065
|
+
return `${at}.${key} must be a non-empty string`;
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return null;
|
|
4069
|
+
}
|
|
4070
|
+
function invalidManifestReadAlsoUnderReason(manifest) {
|
|
4071
|
+
const overrides = manifest.toolGating?.overrides ?? {};
|
|
4072
|
+
for (const [toolName, entry] of Object.entries(overrides)) {
|
|
4073
|
+
const extra = entry?.readAlsoUnder;
|
|
4074
|
+
if (extra === void 0) continue;
|
|
4075
|
+
const at = `override "${toolName}".readAlsoUnder`;
|
|
4076
|
+
if (!Array.isArray(extra) || extra.length === 0 || !extra.every((p) => typeof p === "string" && p.length > 0)) {
|
|
4077
|
+
return `${at} must be a non-empty list of area names`;
|
|
4078
|
+
}
|
|
4079
|
+
if (entry?.category !== "read") return `${at} is allowed on read tools only (category "read")`;
|
|
4080
|
+
if (entry?.read !== void 0) return `${at} cannot be combined with a read adapter (age window)`;
|
|
4081
|
+
}
|
|
4082
|
+
return null;
|
|
4083
|
+
}
|
|
4084
|
+
function invalidManifestMessageReason(manifest) {
|
|
4085
|
+
const overrides = manifest.toolGating?.overrides ?? {};
|
|
4086
|
+
for (const [toolName, entry] of Object.entries(overrides)) {
|
|
4087
|
+
const reason = invalidMessageReason(toolName, entry);
|
|
4088
|
+
if (reason) return reason;
|
|
4089
|
+
}
|
|
4090
|
+
return null;
|
|
4091
|
+
}
|
|
4062
4092
|
function invalidManifestPreviewReason(manifest) {
|
|
4063
4093
|
const overrides = manifest.toolGating?.overrides ?? {};
|
|
4064
4094
|
for (const [toolName, entry] of Object.entries(overrides)) {
|
|
@@ -4488,6 +4518,16 @@ function loadManifests(integrationsDir) {
|
|
|
4488
4518
|
console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${internalReason}`);
|
|
4489
4519
|
continue;
|
|
4490
4520
|
}
|
|
4521
|
+
const readAlsoReason = invalidManifestReadAlsoUnderReason(manifest);
|
|
4522
|
+
if (readAlsoReason) {
|
|
4523
|
+
console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${readAlsoReason}`);
|
|
4524
|
+
continue;
|
|
4525
|
+
}
|
|
4526
|
+
const messageReason = invalidManifestMessageReason(manifest);
|
|
4527
|
+
if (messageReason) {
|
|
4528
|
+
console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${messageReason}`);
|
|
4529
|
+
continue;
|
|
4530
|
+
}
|
|
4491
4531
|
const contextReason = invalidManifestContextReason(manifest);
|
|
4492
4532
|
if (contextReason) {
|
|
4493
4533
|
console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${contextReason}`);
|
|
@@ -5698,7 +5738,7 @@ function toStringArray(value) {
|
|
|
5698
5738
|
if (typeof value === "string" && value) return [value];
|
|
5699
5739
|
return [];
|
|
5700
5740
|
}
|
|
5701
|
-
async function finalizeAttachmentsForExecution(integrationManager2, integrationId, args) {
|
|
5741
|
+
async function finalizeAttachmentsForExecution(integrationManager2, integrationId, args, message) {
|
|
5702
5742
|
const sealed = args[ATTACHMENTS_SEALED_META_KEY];
|
|
5703
5743
|
if (!sealed || sealed.attachments.length === 0 && sealed.inserts.length === 0) {
|
|
5704
5744
|
return { ok: true, args };
|
|
@@ -5749,15 +5789,23 @@ async function finalizeAttachmentsForExecution(integrationManager2, integrationI
|
|
|
5749
5789
|
contentBase64: d.contentBase64
|
|
5750
5790
|
}));
|
|
5751
5791
|
} else {
|
|
5792
|
+
if (!message?.to) {
|
|
5793
|
+
return {
|
|
5794
|
+
ok: false,
|
|
5795
|
+
message: `Refused: "${integrationId}" sends attachments as one built message, but this tool declares no message fields (manifest \`message.text\` and \`message.to\`) \u2014 nothing was sent.`
|
|
5796
|
+
};
|
|
5797
|
+
}
|
|
5798
|
+
const decl = message;
|
|
5752
5799
|
const arg = transport.arg;
|
|
5753
5800
|
const attachments = resolvedDocs.map((d) => ({ filename: d.filename, contentType: d.contentType, contentBase64: d.contentBase64 }));
|
|
5801
|
+
const str = (a, name) => name && typeof a[name] === "string" ? a[name] : "";
|
|
5754
5802
|
const withRaw = (a) => ({
|
|
5755
5803
|
...a,
|
|
5756
5804
|
[arg]: toBase64Url(buildMimeMessage({
|
|
5757
|
-
to: toStringArray(a.to),
|
|
5758
|
-
cc: toStringArray(a.cc),
|
|
5759
|
-
subject:
|
|
5760
|
-
bodyText:
|
|
5805
|
+
to: toStringArray(a[decl.to]),
|
|
5806
|
+
cc: decl.cc ? toStringArray(a[decl.cc]) : [],
|
|
5807
|
+
subject: str(a, decl.subject),
|
|
5808
|
+
bodyText: str(a, decl.text),
|
|
5761
5809
|
attachments
|
|
5762
5810
|
}))
|
|
5763
5811
|
});
|
|
@@ -5790,10 +5838,11 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
|
|
|
5790
5838
|
return void 0;
|
|
5791
5839
|
}
|
|
5792
5840
|
let effectiveArgs = toolArgs;
|
|
5793
|
-
|
|
5841
|
+
const attachmentsArg = tool?.gating?.attachmentsArg;
|
|
5842
|
+
if (attachmentsArg && binding.fields?.includes(attachmentsArg)) {
|
|
5794
5843
|
const sealed = toolArgs[ATTACHMENTS_SEALED_META_KEY];
|
|
5795
5844
|
const fingerprints = (sealed?.attachments ?? []).map((a) => ({ filename: a.filename, sha256: a.sha256 }));
|
|
5796
|
-
effectiveArgs = { ...toolArgs,
|
|
5845
|
+
effectiveArgs = { ...toolArgs, [attachmentsArg]: fingerprints };
|
|
5797
5846
|
}
|
|
5798
5847
|
const bound = selectBoundFields(binding, effectiveArgs);
|
|
5799
5848
|
return {
|
|
@@ -6324,9 +6373,10 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
|
|
|
6324
6373
|
};
|
|
6325
6374
|
}
|
|
6326
6375
|
const { profile, executionMapping, staticExecution, category, boundField: toolBoundField, requiredValue: toolRequiredValue } = tool.gating;
|
|
6376
|
+
const toolAreas = category === "read" ? [profile, ...tool.gating.readAlsoUnder ?? []] : [profile];
|
|
6327
6377
|
const eligibleMandates = () => {
|
|
6328
6378
|
const refusals = [];
|
|
6329
|
-
const matching = state2.getEnrichedAuthorizations().filter((a) => a.complete && profileMatches(a.profileId,
|
|
6379
|
+
const matching = state2.getEnrichedAuthorizations().filter((a) => a.complete && toolAreas.some((p) => profileMatches(a.profileId, p))).filter((a) => {
|
|
6330
6380
|
const refusal = integrationManager2.mandateRefusal?.(tool, a) ?? null;
|
|
6331
6381
|
if (refusal) refusals.push(refusal);
|
|
6332
6382
|
return !refusal;
|
|
@@ -6798,7 +6848,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
|
|
|
6798
6848
|
let finalArgs = callArgs;
|
|
6799
6849
|
let rebuildTransport;
|
|
6800
6850
|
if (tool.gating?.attachmentsArg || tool.gating?.insertFields?.length) {
|
|
6801
|
-
const finalized = await finalizeAttachmentsForExecution(integrationManager2, tool.integrationId, callArgs);
|
|
6851
|
+
const finalized = await finalizeAttachmentsForExecution(integrationManager2, tool.integrationId, callArgs, tool.gating?.message);
|
|
6802
6852
|
if (!finalized.ok) {
|
|
6803
6853
|
return { content: [{ type: "text", text: finalized.message }], isError: true };
|
|
6804
6854
|
}
|
|
@@ -7094,7 +7144,8 @@ function buildProxiedToolDescription(tool) {
|
|
|
7094
7144
|
return `[Suveren: no gating config] ${tool.description}`;
|
|
7095
7145
|
}
|
|
7096
7146
|
const profile = tool.gating.profile;
|
|
7097
|
-
const
|
|
7147
|
+
const areas = tool.gating.category === "read" ? [shortProfile(profile), ...(tool.gating.readAlsoUnder ?? []).map(shortProfile)] : [shortProfile(profile)];
|
|
7148
|
+
const condition = `works only under a ${areas.join(" or ")} mandate, check list-authorizations`;
|
|
7098
7149
|
if (tool.gating.category === "read") {
|
|
7099
7150
|
return `[Suveren: ${profile} \u2014 read; ${condition}] ${tool.description}`;
|
|
7100
7151
|
}
|
|
@@ -7312,7 +7363,8 @@ function listedWithoutMandate(authorizations, integrationManager2) {
|
|
|
7312
7363
|
for (const tool of integrationManager2.getAllTools()) {
|
|
7313
7364
|
if (!toolAlwaysListed(tool, isBuiltin, builtinAlwaysListed)) continue;
|
|
7314
7365
|
const profile = tool.gating.profile;
|
|
7315
|
-
|
|
7366
|
+
const areas = tool.gating.category === "read" ? [profile, ...tool.gating.readAlsoUnder ?? []] : [profile];
|
|
7367
|
+
if (authorizations.some((a) => a.complete && areas.some((p) => profileMatches(a.profileId, p)))) continue;
|
|
7316
7368
|
const short = shortProfileName2(profile);
|
|
7317
7369
|
byProfile.set(short, [...byProfile.get(short) ?? [], tool.namespacedName]);
|
|
7318
7370
|
}
|
|
@@ -7887,8 +7939,9 @@ async function executeCommitted(proposal, state2, integrationManager2) {
|
|
|
7887
7939
|
}
|
|
7888
7940
|
}
|
|
7889
7941
|
let finalToolArgs = toolArgs;
|
|
7942
|
+
let rebuildTransport;
|
|
7890
7943
|
{
|
|
7891
|
-
const finalized = await finalizeAttachmentsForExecution(integrationManager2, integrationId, toolArgs);
|
|
7944
|
+
const finalized = await finalizeAttachmentsForExecution(integrationManager2, integrationId, toolArgs, discovered?.gating?.message);
|
|
7892
7945
|
if (!finalized.ok) {
|
|
7893
7946
|
state2.executionJournal.complete(receiptId, "failed", "changed", finalized.message);
|
|
7894
7947
|
recordExecutionOutcome(state2, proposal.id, receiptId, false, finalized.message);
|
|
@@ -7900,6 +7953,7 @@ async function executeCommitted(proposal, state2, integrationManager2) {
|
|
|
7900
7953
|
};
|
|
7901
7954
|
}
|
|
7902
7955
|
finalToolArgs = finalized.args;
|
|
7956
|
+
rebuildTransport = finalized.rebuildTransport;
|
|
7903
7957
|
}
|
|
7904
7958
|
try {
|
|
7905
7959
|
let outgoingArgs = finalToolArgs;
|
|
@@ -7908,6 +7962,7 @@ async function executeCommitted(proposal, state2, integrationManager2) {
|
|
|
7908
7962
|
outgoingArgs = appendVerificationFooter(discovered, outgoingArgs, receiptId, cachedAuth?.subjects?.[0]);
|
|
7909
7963
|
}
|
|
7910
7964
|
outgoingArgs = attachTicketId(discovered, outgoingArgs, receiptId);
|
|
7965
|
+
if (rebuildTransport) outgoingArgs = rebuildTransport(outgoingArgs);
|
|
7911
7966
|
outgoingArgs = encodeOutgoingArgs(discovered, outgoingArgs);
|
|
7912
7967
|
}
|
|
7913
7968
|
outgoingArgs = stripSourceLink(outgoingArgs);
|
|
@@ -9508,7 +9563,9 @@ var IntegrationManager = class {
|
|
|
9508
9563
|
hideUnlessAuthorized: ext.hideUnlessAuthorized,
|
|
9509
9564
|
simulationOnly: ext.simulationOnly,
|
|
9510
9565
|
approvalView: ext.approvalView,
|
|
9511
|
-
internal: ext.internal
|
|
9566
|
+
internal: ext.internal,
|
|
9567
|
+
// Further areas whose mandates may authorize this read (manifest).
|
|
9568
|
+
readAlsoUnder: ext.readAlsoUnder
|
|
9512
9569
|
};
|
|
9513
9570
|
}
|
|
9514
9571
|
return {
|
|
@@ -9542,7 +9599,8 @@ var IntegrationManager = class {
|
|
|
9542
9599
|
internal: ext.internal,
|
|
9543
9600
|
attachmentsArg: ext.attachmentsArg,
|
|
9544
9601
|
insertFields: ext.insertFields,
|
|
9545
|
-
aiTextArg: ext.display?.aiText
|
|
9602
|
+
aiTextArg: ext.display?.aiText,
|
|
9603
|
+
message: ext.message
|
|
9546
9604
|
};
|
|
9547
9605
|
}
|
|
9548
9606
|
return {
|
|
@@ -11733,12 +11791,11 @@ async function startIntegrationForService(serviceId) {
|
|
|
11733
11791
|
(c) => c.id === serviceId || Object.values(c.envKeys ?? {}).some((ref) => typeof ref === "string" && ref.startsWith(`${serviceId}.`))
|
|
11734
11792
|
);
|
|
11735
11793
|
for (const config of candidates) {
|
|
11794
|
+
await startOneIntegration(config);
|
|
11736
11795
|
if (integrationManager.isRunning(config.id) && integrationManager.credentialsChangedSinceStart(config)) {
|
|
11737
11796
|
console.error(`[Suveren MCP] ${config.id}: credentials changed since start \u2014 restarting`);
|
|
11738
11797
|
await startOneIntegration(config, { restart: true });
|
|
11739
|
-
continue;
|
|
11740
11798
|
}
|
|
11741
|
-
await startOneIntegration(config);
|
|
11742
11799
|
}
|
|
11743
11800
|
}
|
|
11744
11801
|
async function startOneIntegration(config, opts = {}) {
|