@suveren/gateway 0.2.12 → 0.3.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/README.md +21 -7
- package/dist/control-plane/index.mjs +146 -2
- package/dist/mcp-server/http.mjs +85 -38
- package/dist/ui/assets/index-RkZBhIxU.js +105 -0
- package/dist/ui/index.html +1 -1
- package/node_modules/@hap/core/dist/index.d.mts +147 -3
- package/node_modules/@hap/core/dist/index.d.ts +147 -3
- package/node_modules/@hap/core/dist/index.js +74 -10
- package/node_modules/@hap/core/dist/index.mjs +69 -10
- package/node_modules/@hap/core/package.json +1 -1
- package/node_modules/@hap/core/src/gatekeeper.ts +4 -4
- package/node_modules/@hap/core/src/identity.ts +77 -0
- package/node_modules/@hap/core/src/index.ts +2 -0
- package/node_modules/@hap/core/src/intent-disclosure.ts +69 -0
- package/node_modules/@hap/core/src/types.ts +62 -2
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/README.md +16 -1
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/index.js +4 -20
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/read.js +17 -17
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/types/json.js +60 -32
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/types/raw.js +3 -3
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/types/text.js +3 -3
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/types/urlencoded.js +16 -20
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/lib/utils.js +18 -16
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/LICENSE +22 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/README.md +69 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +26 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js +170 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/package.json +52 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/package.json +23 -10
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/assert/strict.d.ts +10 -1
- package/node_modules/@types/node/assert.d.ts +144 -17
- package/node_modules/@types/node/async_hooks.d.ts +9 -9
- package/node_modules/@types/node/buffer.d.ts +0 -5
- package/node_modules/@types/node/child_process.d.ts +17 -21
- package/node_modules/@types/node/cluster.d.ts +13 -13
- package/node_modules/@types/node/console.d.ts +1 -1
- package/node_modules/@types/node/crypto.d.ts +454 -571
- package/node_modules/@types/node/diagnostics_channel.d.ts +37 -29
- package/node_modules/@types/node/dns.d.ts +14 -14
- package/node_modules/@types/node/events.d.ts +3 -3
- package/node_modules/@types/node/fs/promises.d.ts +4 -4
- package/node_modules/@types/node/fs.d.ts +3 -12
- package/node_modules/@types/node/globals.d.ts +4 -4
- package/node_modules/@types/node/http.d.ts +5 -5
- package/node_modules/@types/node/http2.d.ts +5 -1
- package/node_modules/@types/node/index.d.ts +0 -3
- package/node_modules/@types/node/inspector.d.ts +1 -1
- package/node_modules/@types/node/module.d.ts +16 -45
- package/node_modules/@types/node/net.d.ts +1 -1
- package/node_modules/@types/node/os.d.ts +2 -2
- package/node_modules/@types/node/package.json +4 -4
- package/node_modules/@types/node/perf_hooks.d.ts +1 -9
- package/node_modules/@types/node/process.d.ts +11 -11
- package/node_modules/@types/node/readline.d.ts +12 -11
- package/node_modules/@types/node/repl.d.ts +10 -10
- package/node_modules/@types/node/sqlite.d.ts +1 -1
- package/node_modules/@types/node/stream/promises.d.ts +2 -2
- package/node_modules/@types/node/stream/web.d.ts +1 -1
- package/node_modules/@types/node/stream.d.ts +5 -5
- package/node_modules/@types/node/test/reporters.d.ts +1 -1
- package/node_modules/@types/node/test.d.ts +8 -8
- package/node_modules/@types/node/tls.d.ts +1 -1
- package/node_modules/@types/node/ts5.6/index.d.ts +1 -4
- package/node_modules/@types/node/ts5.7/index.d.ts +0 -3
- package/node_modules/@types/node/url.d.ts +3 -3
- package/node_modules/@types/node/util/types.d.ts +1 -1
- package/node_modules/@types/node/util.d.ts +3 -20
- package/node_modules/@types/node/v8.d.ts +1 -1
- package/node_modules/@types/node/vm.d.ts +9 -9
- package/node_modules/@types/node/web-globals/domexception.d.ts +15 -0
- package/node_modules/@types/node/worker_threads.d.ts +2 -5
- package/node_modules/@types/node/zlib.d.ts +3 -3
- package/node_modules/fast-uri/index.js +1 -1
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security.test.js +28 -0
- package/node_modules/hono/dist/adapter/aws-lambda/handler.js +14 -20
- package/node_modules/hono/dist/adapter/lambda-edge/handler.js +8 -2
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +14 -20
- package/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +8 -2
- package/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
- package/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
- package/node_modules/hono/dist/cjs/jsx/base.js +8 -14
- package/node_modules/hono/dist/cjs/jsx/components.js +41 -21
- package/node_modules/hono/dist/cjs/jsx/context.js +131 -5
- package/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
- package/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
- package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -32
- package/node_modules/hono/dist/cjs/middleware/serve-static/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/timing/timing.js +3 -1
- package/node_modules/hono/dist/cjs/utils/ipaddr.js +6 -1
- package/node_modules/hono/dist/helper/css/common.js +3 -1
- package/node_modules/hono/dist/helper/css/index.js +9 -1
- package/node_modules/hono/dist/jsx/base.js +15 -15
- package/node_modules/hono/dist/jsx/components.js +42 -22
- package/node_modules/hono/dist/jsx/context.js +129 -5
- package/node_modules/hono/dist/jsx/streaming.js +10 -8
- package/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
- package/node_modules/hono/dist/middleware/cors/index.js +2 -5
- package/node_modules/hono/dist/middleware/language/language.js +10 -32
- package/node_modules/hono/dist/middleware/serve-static/index.js +1 -1
- package/node_modules/hono/dist/middleware/timing/timing.js +3 -1
- package/node_modules/hono/dist/tsconfig.build.tsbuildinfo +1 -1
- package/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
- package/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
- package/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
- package/node_modules/hono/dist/utils/ipaddr.js +6 -1
- package/node_modules/hono/package.json +19 -13
- package/node_modules/http-proxy-middleware/README.md +3 -10
- package/node_modules/http-proxy-middleware/dist/errors.d.ts +4 -0
- package/node_modules/http-proxy-middleware/dist/errors.js +16 -1
- package/node_modules/http-proxy-middleware/dist/factory.d.ts +2 -2
- package/node_modules/http-proxy-middleware/dist/handlers/fix-request-body-utils/stringify-form-data.d.ts +7 -0
- package/node_modules/http-proxy-middleware/dist/handlers/fix-request-body-utils/stringify-form-data.js +45 -0
- package/node_modules/http-proxy-middleware/dist/handlers/fix-request-body.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/handlers/fix-request-body.js +23 -23
- package/node_modules/http-proxy-middleware/dist/handlers/response-interceptor.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/handlers/response-interceptor.js +1 -1
- package/node_modules/http-proxy-middleware/dist/http-proxy-middleware.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/http-proxy-middleware.js +2 -2
- package/node_modules/http-proxy-middleware/dist/legacy/create-proxy-middleware.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/legacy/create-proxy-middleware.js +1 -1
- package/node_modules/http-proxy-middleware/dist/legacy/options-adapter.js +1 -1
- package/node_modules/http-proxy-middleware/dist/legacy/types.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/path-filter.d.ts +1 -1
- package/node_modules/http-proxy-middleware/dist/path-filter.js +1 -1
- package/node_modules/http-proxy-middleware/dist/path-rewriter.js +1 -1
- package/node_modules/http-proxy-middleware/dist/plugins/default/error-response-plugin.js +2 -1
- package/node_modules/http-proxy-middleware/dist/plugins/default/logger-plugin.js +3 -3
- package/node_modules/http-proxy-middleware/dist/router.js +26 -8
- package/node_modules/http-proxy-middleware/dist/status-code.js +15 -12
- package/node_modules/http-proxy-middleware/dist/types.d.ts +3 -3
- package/node_modules/http-proxy-middleware/dist/types.js +0 -4
- package/node_modules/http-proxy-middleware/dist/utils/sanitize.d.ts +1 -0
- package/node_modules/http-proxy-middleware/dist/utils/sanitize.js +6 -0
- package/node_modules/http-proxy-middleware/package.json +30 -28
- package/node_modules/qs/CHANGELOG.md +11 -0
- package/node_modules/qs/dist/qs.js +17 -17
- package/node_modules/qs/lib/parse.js +19 -8
- package/node_modules/qs/lib/utils.js +47 -8
- package/node_modules/qs/package.json +6 -5
- package/node_modules/qs/test/parse.js +230 -0
- package/node_modules/qs/test/utils.js +194 -0
- package/node_modules/side-channel/CHANGELOG.md +10 -0
- package/node_modules/side-channel/README.md +1 -1
- package/node_modules/side-channel/index.js +5 -2
- package/node_modules/side-channel/package.json +10 -10
- package/node_modules/side-channel/test/index.js +16 -0
- package/node_modules/undici-types/agent.d.ts +0 -2
- package/node_modules/undici-types/client.d.ts +25 -19
- package/node_modules/undici-types/dispatcher.d.ts +7 -33
- package/node_modules/undici-types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/undici-types/fetch.d.ts +24 -4
- package/node_modules/undici-types/h2c-client.d.ts +6 -6
- package/node_modules/undici-types/handlers.d.ts +1 -2
- package/node_modules/undici-types/header.d.ts +5 -0
- package/node_modules/undici-types/index.d.ts +3 -1
- package/node_modules/undici-types/interceptors.d.ts +1 -1
- package/node_modules/undici-types/package.json +1 -1
- package/node_modules/undici-types/pool.d.ts +0 -2
- package/node_modules/undici-types/proxy-agent.d.ts +2 -2
- package/node_modules/undici-types/round-robin-pool.d.ts +0 -2
- package/node_modules/undici-types/snapshot-agent.d.ts +4 -0
- package/node_modules/undici-types/socks5-proxy-agent.d.ts +2 -2
- package/node_modules/undici-types/webidl.d.ts +0 -1
- package/package.json +3 -3
- package/dist/ui/assets/index-CS16uhqb.js +0 -102
- package/node_modules/@types/node/compatibility/iterators.d.ts +0 -21
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# Suveren Gateway
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
> **HAP is the protocol. Suveren is an implementation of it.**
|
|
4
|
+
>
|
|
5
|
+
> The Human Agency Protocol (HAP) is the open standard for bounded AI-agent
|
|
6
|
+
> authority — it defines the roles (**Authority Server**, **Gatekeeper**,
|
|
7
|
+
> **Executor**) and the concepts (profiles, gates, attestations, bounds,
|
|
8
|
+
> context, receipts). **Suveren** implements them: this repo is the **Gateway**,
|
|
9
|
+
> and [`suveren-as`](https://www.suveren.ai) is the **Authority Server**. The
|
|
10
|
+
> protocol is open — anyone can build their own compliant gateway.
|
|
11
|
+
|
|
12
|
+
This repository is the **Suveren Gateway** — Suveren's implementation of the HAP
|
|
13
|
+
**Gatekeeper + Executor** roles. It runs locally and verifies every tool call
|
|
14
|
+
against its authorization before the call reaches an external service. (The
|
|
15
|
+
`@hap/core` library inside this repo keeps its HAP name because it re-exports the
|
|
16
|
+
open protocol library — "HAP-compliant" and spec references describe the open
|
|
17
|
+
standard, not the Suveren brand.)
|
|
4
18
|
|
|
5
19
|
Part of [suveren.ai](https://www.suveren.ai).
|
|
6
20
|
|
|
@@ -16,11 +30,11 @@ Works with any MCP-compatible agent. Define and authorize what they're allowed t
|
|
|
16
30
|
|
|
17
31
|
Set the threshold. Routine actions execute automatically within the bounds you defined. High-stakes actions pause for your review before the agent acts.
|
|
18
32
|
|
|
19
|
-
**Automatic** — You commit to specific bounds upfront: max amounts, allowed actions, time windows. The agent executes autonomously within those bounds.
|
|
33
|
+
**Automatic** — You commit to specific bounds upfront: max amounts, allowed actions, time windows. The agent executes autonomously within those bounds. For each tool call the gateway verifies your authorization and requests a receipt from the Authority Server, which issues the signed receipt before the call runs — no receipt, no execution.
|
|
20
34
|
|
|
21
35
|
**Review each action** — You define bounds but defer full commitment. When the agent proposes an action, you review it in the gateway UI — seeing exactly which tool, which arguments, which context. You approve or reject. Execution only proceeds after your decision.
|
|
22
36
|
|
|
23
|
-
Both modes are bounded.
|
|
37
|
+
Both modes are bounded. In both, the Authority Server issues a signed receipt before the action runs — no receipt, no execution — and that signed history is a full audit trail. The difference is whether you trust the bounds enough for autonomous execution, or want to review each action individually.
|
|
24
38
|
|
|
25
39
|
---
|
|
26
40
|
|
|
@@ -33,7 +47,7 @@ Human AI Agent
|
|
|
33
47
|
| articulate direction, |
|
|
34
48
|
| commit (or defer) |
|
|
35
49
|
v |
|
|
36
|
-
|
|
50
|
+
Authority Server |
|
|
37
51
|
| 2. Sign attestation (Ed25519) |
|
|
38
52
|
v |
|
|
39
53
|
Gateway |
|
|
@@ -41,14 +55,14 @@ Gateway |
|
|
|
41
55
|
| 4. Tool call <-----------|
|
|
42
56
|
| |
|
|
43
57
|
| 5. Fully committed: |
|
|
44
|
-
| Gatekeeper
|
|
45
|
-
| -> receipt
|
|
58
|
+
| Gatekeeper checks bounds, asks AS |
|
|
59
|
+
| -> AS issues receipt, execute |
|
|
46
60
|
| |
|
|
47
61
|
| Deferred commitment: |
|
|
48
62
|
| -> proposal created |
|
|
49
63
|
| -> human reviews in UI |
|
|
50
64
|
| -> commit or reject |
|
|
51
|
-
| ->
|
|
65
|
+
| -> on commit: AS receipt, execute |
|
|
52
66
|
```
|
|
53
67
|
|
|
54
68
|
The agent never holds credentials or signing authority. It acts within the bounds you set — high autonomy without losing accountability.
|
|
@@ -362,6 +362,13 @@ async function getGateContent(path) {
|
|
|
362
362
|
if (!res.ok) throw new Error("Failed to fetch gate content");
|
|
363
363
|
return res.json();
|
|
364
364
|
}
|
|
365
|
+
async function getEnrichedAuthorizations() {
|
|
366
|
+
const res = await fetch(`${MCP_BASE}/internal/authorizations`, {
|
|
367
|
+
headers: internalHeaders()
|
|
368
|
+
});
|
|
369
|
+
if (!res.ok) throw new Error("Failed to fetch authorizations");
|
|
370
|
+
return res.json();
|
|
371
|
+
}
|
|
365
372
|
async function getMcpHealth() {
|
|
366
373
|
const res = await fetch(`${MCP_BASE}/health`);
|
|
367
374
|
if (!res.ok) throw new Error("MCP server unreachable");
|
|
@@ -375,6 +382,7 @@ import {
|
|
|
375
382
|
HkdfSha256,
|
|
376
383
|
Aes256Gcm
|
|
377
384
|
} from "@hpke/core";
|
|
385
|
+
import { canonicalizeText } from "@hap/core";
|
|
378
386
|
function getSuite() {
|
|
379
387
|
return new CipherSuite({
|
|
380
388
|
kem: new DhkemX25519HkdfSha256(),
|
|
@@ -975,6 +983,84 @@ ${request.currentText}
|
|
|
975
983
|
};
|
|
976
984
|
}
|
|
977
985
|
}
|
|
986
|
+
async function callModel(config, messages, opts) {
|
|
987
|
+
if (config.provider === "ollama") {
|
|
988
|
+
const response2 = await fetch(`${config.endpoint}/api/chat`, {
|
|
989
|
+
method: "POST",
|
|
990
|
+
headers: { "Content-Type": "application/json" },
|
|
991
|
+
body: JSON.stringify({
|
|
992
|
+
model: config.model,
|
|
993
|
+
messages,
|
|
994
|
+
stream: false,
|
|
995
|
+
options: { temperature: opts.temperature, num_predict: opts.maxTokens }
|
|
996
|
+
}),
|
|
997
|
+
signal: AbortSignal.timeout(opts.timeoutMs)
|
|
998
|
+
});
|
|
999
|
+
if (!response2.ok) throw new Error(`Ollama: ${response2.status}`);
|
|
1000
|
+
const data2 = await response2.json();
|
|
1001
|
+
return data2.message?.content?.trim() || "No response generated.";
|
|
1002
|
+
}
|
|
1003
|
+
const headers = { "Content-Type": "application/json" };
|
|
1004
|
+
if (config.apiKey) headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
1005
|
+
const response = await fetch(`${config.endpoint}/chat/completions`, {
|
|
1006
|
+
method: "POST",
|
|
1007
|
+
headers,
|
|
1008
|
+
body: JSON.stringify({ model: config.model, messages, temperature: opts.temperature, max_tokens: opts.maxTokens }),
|
|
1009
|
+
signal: AbortSignal.timeout(opts.timeoutMs)
|
|
1010
|
+
});
|
|
1011
|
+
if (!response.ok) {
|
|
1012
|
+
const errorText = await response.text();
|
|
1013
|
+
throw new Error(`AI provider: ${response.status} - ${errorText}`);
|
|
1014
|
+
}
|
|
1015
|
+
const data = await response.json();
|
|
1016
|
+
return data.choices?.[0]?.message?.content?.trim() || "No response generated.";
|
|
1017
|
+
}
|
|
1018
|
+
var INTENT_REVIEW_PROMPT = `You are a reviewing assistant for Suveren. A human is creating a NEW agent authorization. Compare the MEANING of its Intent against the Intent of their EXISTING authorizations on the same profile.
|
|
1019
|
+
|
|
1020
|
+
Focus ONLY on the intent prose \u2014 the stated situation, goal, and soft rules / watch-outs. Surface:
|
|
1021
|
+
- Contradictions: a soft rule or goal in one intent that conflicts with another (e.g. one says "never email on weekends", the new one implies weekend sends; one says "formal tone only", another "keep it casual").
|
|
1022
|
+
- Tension: intent that works against, or ignores a caution stated in, another authorization.
|
|
1023
|
+
- Redundant purpose: the new intent describes essentially the same purpose as an existing one.
|
|
1024
|
+
|
|
1025
|
+
Do NOT report:
|
|
1026
|
+
- That scopes/recipients overlap, or that numeric limits differ \u2014 those are detected separately and already shown to the user. Ignore them entirely.
|
|
1027
|
+
- Anything not grounded in the actual intent text.
|
|
1028
|
+
|
|
1029
|
+
Use each authorization's scope ONLY to judge whether two intents' rules would actually collide in practice (overlapping targets) versus being independent \u2014 never report the scope overlap itself.
|
|
1030
|
+
|
|
1031
|
+
Rules:
|
|
1032
|
+
- Be concise \u2014 a few short bullets at most.
|
|
1033
|
+
- If the new intent has no real content yet (just placeholder prompts like "Why \u2014 \u2026", "Goal \u2014 \u2026", "Watch out \u2014 \u2026"), reply EXACTLY: "Not enough intent written yet to review."
|
|
1034
|
+
- If the intents are compatible, reply EXACTLY: "No intent conflicts found with your existing authorizations."
|
|
1035
|
+
- Refer to each existing authorization by its number in square brackets \u2014 e.g. [1], [2] \u2014 matching the numbered list provided, and you may also state its scope. Never invent names. Do NOT rewrite intent or make the decision \u2014 you surface; the human decides.`;
|
|
1036
|
+
async function getIntentReview(config, request) {
|
|
1037
|
+
const disclaimer = "AI surfaces reality. You decide.";
|
|
1038
|
+
const lines = [];
|
|
1039
|
+
lines.push(`NEW authorization on profile "${request.profileName}":`);
|
|
1040
|
+
if (request.scope) lines.push(` Scope (for context only): ${request.scope}`);
|
|
1041
|
+
lines.push(` Intent: ${request.newIntent.trim() || "(empty)"}`);
|
|
1042
|
+
lines.push("");
|
|
1043
|
+
lines.push("EXISTING authorizations on the same profile:");
|
|
1044
|
+
request.existingGrants.forEach((g, i) => {
|
|
1045
|
+
lines.push(` [${i + 1}]`);
|
|
1046
|
+
if (g.scope) lines.push(` Scope (for context only): ${g.scope}`);
|
|
1047
|
+
lines.push(` Intent: ${g.intent.trim() || "(empty)"}`);
|
|
1048
|
+
});
|
|
1049
|
+
const messages = [
|
|
1050
|
+
{ role: "system", content: INTENT_REVIEW_PROMPT },
|
|
1051
|
+
{ role: "user", content: lines.join("\n") }
|
|
1052
|
+
];
|
|
1053
|
+
try {
|
|
1054
|
+
const review = await callModel(config, messages, { temperature: 0.2, maxTokens: 400, timeoutMs: 45e3 });
|
|
1055
|
+
return { success: true, review, disclaimer };
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
return {
|
|
1058
|
+
success: false,
|
|
1059
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
1060
|
+
disclaimer
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
978
1064
|
async function testAIConnectivity(config) {
|
|
979
1065
|
try {
|
|
980
1066
|
if (config.provider === "ollama") {
|
|
@@ -999,6 +1085,10 @@ async function testAIConnectivity(config) {
|
|
|
999
1085
|
}
|
|
1000
1086
|
|
|
1001
1087
|
// src/routes/ai.ts
|
|
1088
|
+
var describeScope = (c = {}) => {
|
|
1089
|
+
const vals = Object.values(c).map((v) => String(v).trim()).filter(Boolean);
|
|
1090
|
+
return vals.length > 0 ? vals.join(", ") : "all (unscoped)";
|
|
1091
|
+
};
|
|
1002
1092
|
function createAIRouter(vault2) {
|
|
1003
1093
|
const router = Router3();
|
|
1004
1094
|
function loadAIConfig() {
|
|
@@ -1043,6 +1133,46 @@ function createAIRouter(vault2) {
|
|
|
1043
1133
|
const result = await getAIChatResponse(config, request);
|
|
1044
1134
|
res.json(result);
|
|
1045
1135
|
});
|
|
1136
|
+
router.post("/intent-review", async (req, res) => {
|
|
1137
|
+
const config = loadAIConfig();
|
|
1138
|
+
if (!config) {
|
|
1139
|
+
res.status(400).json({ error: "AI not configured. Save AI settings in Settings > General." });
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
const { profileId, newIntent, context } = req.body;
|
|
1143
|
+
if (!profileId) {
|
|
1144
|
+
res.status(400).json({ error: "Missing profileId" });
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
let existing = [];
|
|
1148
|
+
try {
|
|
1149
|
+
const data = await getEnrichedAuthorizations();
|
|
1150
|
+
existing = (data.authorizations ?? []).filter((a) => a.profileId === profileId && !!a.intent?.trim());
|
|
1151
|
+
} catch {
|
|
1152
|
+
existing = [];
|
|
1153
|
+
}
|
|
1154
|
+
if (existing.length === 0) {
|
|
1155
|
+
res.json({ success: true, review: null, note: "No other authorizations on this profile to compare against." });
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
const result = await getIntentReview(config, {
|
|
1159
|
+
profileName: profileId.split("/").pop() ?? profileId,
|
|
1160
|
+
newIntent: newIntent ?? "",
|
|
1161
|
+
scope: describeScope(context),
|
|
1162
|
+
existingGrants: existing.map((a) => ({
|
|
1163
|
+
intent: a.intent ?? "",
|
|
1164
|
+
scope: describeScope(a.context)
|
|
1165
|
+
}))
|
|
1166
|
+
});
|
|
1167
|
+
res.json({
|
|
1168
|
+
...result,
|
|
1169
|
+
grants: existing.map((a) => ({
|
|
1170
|
+
scope: describeScope(a.context),
|
|
1171
|
+
intent: a.intent,
|
|
1172
|
+
bounds: a.bounds
|
|
1173
|
+
}))
|
|
1174
|
+
});
|
|
1175
|
+
});
|
|
1046
1176
|
router.post("/test", async (req, res) => {
|
|
1047
1177
|
const body = req.body;
|
|
1048
1178
|
let config;
|
|
@@ -1234,6 +1364,7 @@ function createMCPRouter() {
|
|
|
1234
1364
|
|
|
1235
1365
|
// src/routes/encrypt-intent.ts
|
|
1236
1366
|
import { Router as Router6 } from "express";
|
|
1367
|
+
import { computeIntentDisclosureHash } from "@hap/core";
|
|
1237
1368
|
function createEncryptIntentRouter() {
|
|
1238
1369
|
const router = Router6();
|
|
1239
1370
|
router.post("/", async (req, res) => {
|
|
@@ -1279,10 +1410,14 @@ function createEncryptIntentRouter() {
|
|
|
1279
1410
|
enc: Buffer.from(wrap.enc).toString("base64")
|
|
1280
1411
|
};
|
|
1281
1412
|
}
|
|
1413
|
+
const intentCiphertextB64 = Buffer.from(encrypted.intentCiphertext).toString("base64");
|
|
1414
|
+
const approversFrozen = parsed.map((r) => r.userId);
|
|
1415
|
+
const intentDisclosureHash = computeIntentDisclosureHash(intentCiphertextB64, approversFrozen);
|
|
1282
1416
|
res.json({
|
|
1283
|
-
intentCiphertext:
|
|
1417
|
+
intentCiphertext: intentCiphertextB64,
|
|
1284
1418
|
encryptedKeys,
|
|
1285
|
-
approversFrozen
|
|
1419
|
+
approversFrozen,
|
|
1420
|
+
intentDisclosureHash
|
|
1286
1421
|
});
|
|
1287
1422
|
} catch (err) {
|
|
1288
1423
|
console.error("[Control Plane] encrypt-intent error:", err);
|
|
@@ -1863,6 +1998,15 @@ app.get("/gate-content", authGuard, async (req, res) => {
|
|
|
1863
1998
|
res.status(500).json({ error: "Failed to fetch gate content from MCP server" });
|
|
1864
1999
|
}
|
|
1865
2000
|
});
|
|
2001
|
+
app.get("/active-authorizations", authGuard, async (_req, res) => {
|
|
2002
|
+
try {
|
|
2003
|
+
const data = await getEnrichedAuthorizations();
|
|
2004
|
+
res.json(data);
|
|
2005
|
+
} catch (err) {
|
|
2006
|
+
console.error("[Control Plane] Authorizations retrieval error:", err);
|
|
2007
|
+
res.status(500).json({ error: "Failed to fetch authorizations from MCP server" });
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
1866
2010
|
app.post("/gate-content", jsonParser, authGuard, async (req, res) => {
|
|
1867
2011
|
try {
|
|
1868
2012
|
const { frameHash, boundsHash, contextHash, context, path, gateContent } = req.body;
|
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -182,6 +182,11 @@ var SPClient = class {
|
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
// src/lib/attestation-cache.ts
|
|
185
|
+
import { decodeAttestationBlob } from "@hap/core";
|
|
186
|
+
function isCommitmentDowngrade(auth) {
|
|
187
|
+
const requiresReview = auth.signedCommitmentMode === "review" || auth.signedCommitmentMode === "review_above_cap";
|
|
188
|
+
return requiresReview && (auth.deferredCommitmentDomains ?? []).length === 0;
|
|
189
|
+
}
|
|
185
190
|
var AttestationCache = class {
|
|
186
191
|
constructor(spClient) {
|
|
187
192
|
this.spClient = spClient;
|
|
@@ -226,8 +231,25 @@ var AttestationCache = class {
|
|
|
226
231
|
async syncAuthorization(frameHash) {
|
|
227
232
|
const result = await this.spClient.getAttestations(frameHash);
|
|
228
233
|
if (!result.profile_id || !result.frame) return null;
|
|
234
|
+
if (result.revoked) {
|
|
235
|
+
this.authorizations.delete(frameHash);
|
|
236
|
+
if (result.frame_hash) this.authorizations.delete(result.frame_hash);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
229
239
|
const storageHash = result.frame_hash ?? result.bounds_hash;
|
|
230
240
|
const bounds = result.bounds ?? result.frame;
|
|
241
|
+
let signedCommitmentMode;
|
|
242
|
+
let subjects;
|
|
243
|
+
const firstBlob = result.attestations[0]?.blob;
|
|
244
|
+
if (firstBlob) {
|
|
245
|
+
try {
|
|
246
|
+
const payload = decodeAttestationBlob(firstBlob).payload;
|
|
247
|
+
signedCommitmentMode = payload.commitment_mode;
|
|
248
|
+
subjects = payload.subjects;
|
|
249
|
+
} catch {
|
|
250
|
+
signedCommitmentMode = void 0;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
231
253
|
const auth = {
|
|
232
254
|
frameHash: storageHash,
|
|
233
255
|
boundsHash: result.bounds_hash,
|
|
@@ -247,9 +269,11 @@ var AttestationCache = class {
|
|
|
247
269
|
requiredDomains: result.required_domains ?? [],
|
|
248
270
|
attestedDomains: result.attested_domains ?? [],
|
|
249
271
|
deferredCommitmentDomains: result.deferred_commitment_domains ?? [],
|
|
272
|
+
signedCommitmentMode,
|
|
273
|
+
subjects,
|
|
250
274
|
complete: result.complete
|
|
251
275
|
};
|
|
252
|
-
this.authorizations.set(auth.path, auth);
|
|
276
|
+
this.authorizations.set(auth.frameHash ?? auth.path, auth);
|
|
253
277
|
return auth;
|
|
254
278
|
}
|
|
255
279
|
/**
|
|
@@ -278,7 +302,7 @@ var AttestationCache = class {
|
|
|
278
302
|
* Cache an authorization directly (e.g., from SP response after creation).
|
|
279
303
|
*/
|
|
280
304
|
cacheAuthorization(auth) {
|
|
281
|
-
this.authorizations.set(auth.path, auth);
|
|
305
|
+
this.authorizations.set(auth.frameHash ?? auth.path, auth);
|
|
282
306
|
}
|
|
283
307
|
/**
|
|
284
308
|
* Remove a cached authorization by path. Called when the SP reports the
|
|
@@ -715,7 +739,7 @@ var SharedState = class {
|
|
|
715
739
|
getEnrichedAuthorizations() {
|
|
716
740
|
const authorizations = this.cache.getAllAuthorizations();
|
|
717
741
|
return authorizations.map((auth) => {
|
|
718
|
-
const gateEntry =
|
|
742
|
+
const gateEntry = (auth.frameHash ? this.gateStore.get(auth.frameHash) : null) ?? (auth.boundsHash ? this.gateStore.get(auth.boundsHash) : null) ?? this.gateStore.get(auth.path) ?? null;
|
|
719
743
|
return {
|
|
720
744
|
...auth,
|
|
721
745
|
gateContent: gateEntry?.gateContent ?? null,
|
|
@@ -905,20 +929,23 @@ function buildMandateBrief(opts) {
|
|
|
905
929
|
import { getProfile as getProfile3 } from "@hap/core";
|
|
906
930
|
|
|
907
931
|
// src/lib/receipt-footer.ts
|
|
932
|
+
import { deriveIdentityLine } from "@hap/core";
|
|
908
933
|
var AS_BASE = (process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai").replace(/\/$/, "");
|
|
934
|
+
var OPERATOR_NAME = process.env.SUVEREN_OPERATOR_NAME ?? "Suveren";
|
|
909
935
|
var FOOTER_PROFILES = /* @__PURE__ */ new Set(["email", "calendar", "publish"]);
|
|
910
936
|
var CONTENT_FIELD_CANDIDATES = ["body", "text", "description", "content"];
|
|
911
|
-
var FOOTER_MARKER = "\u2014 Sent by an AI agent
|
|
937
|
+
var FOOTER_MARKER = "\u2014 Sent by an AI agent";
|
|
912
938
|
function shouldAttachFooter() {
|
|
913
939
|
return true;
|
|
914
940
|
}
|
|
915
941
|
function verifyUrl(receiptId) {
|
|
916
942
|
return `${AS_BASE}/r/${receiptId}`;
|
|
917
943
|
}
|
|
918
|
-
function footerText(receiptId) {
|
|
944
|
+
function footerText(receiptId, subject) {
|
|
945
|
+
const line = deriveIdentityLine(subject, { operatorName: OPERATOR_NAME });
|
|
919
946
|
return `
|
|
920
947
|
|
|
921
|
-
${
|
|
948
|
+
\u2014 ${line}. Verify: ${verifyUrl(receiptId)}`;
|
|
922
949
|
}
|
|
923
950
|
function isStringType(t) {
|
|
924
951
|
return t === "string" || Array.isArray(t) && t.includes("string");
|
|
@@ -937,45 +964,34 @@ function stripFooter(value) {
|
|
|
937
964
|
if (i === -1) return value;
|
|
938
965
|
return value.slice(0, i).replace(/\n+$/, "");
|
|
939
966
|
}
|
|
940
|
-
function appendVerificationFooter(tool, args, receiptId) {
|
|
967
|
+
function appendVerificationFooter(tool, args, receiptId, subject) {
|
|
941
968
|
const profile = tool.gating?.profile;
|
|
942
969
|
if (!profile || !FOOTER_PROFILES.has(profile)) return args;
|
|
943
970
|
const field = detectContentField(tool);
|
|
944
971
|
if (!field) return args;
|
|
945
972
|
const current = typeof args[field] === "string" ? args[field] : "";
|
|
946
|
-
return { ...args, [field]: stripFooter(current) + footerText(receiptId) };
|
|
973
|
+
return { ...args, [field]: stripFooter(current) + footerText(receiptId, subject) };
|
|
947
974
|
}
|
|
948
975
|
|
|
949
976
|
// src/lib/content-binding.ts
|
|
950
|
-
import {
|
|
951
|
-
import { canonicalize, getProfile as getProfile2 } from "@hap/core";
|
|
952
|
-
function canonicalizeText(input) {
|
|
953
|
-
const nfc = input.normalize("NFC");
|
|
954
|
-
const lf = nfc.replace(/\r\n?/g, "\n");
|
|
955
|
-
const lines = lf.split("\n").map((line) => line.replace(/[ \t]+$/, ""));
|
|
956
|
-
return lines.join("\n").replace(/\n+$/, "");
|
|
957
|
-
}
|
|
958
|
-
function sha256Hex(bytes) {
|
|
959
|
-
return createHash("sha256").update(bytes, "utf8").digest("hex");
|
|
960
|
-
}
|
|
977
|
+
import { computeContentHash, getProfile as getProfile2 } from "@hap/core";
|
|
961
978
|
function getContentBinding(profileId) {
|
|
962
|
-
|
|
963
|
-
return profile?.content_binding;
|
|
979
|
+
return getProfile2(profileId)?.content_binding;
|
|
964
980
|
}
|
|
965
981
|
function computeContentBinding(profileId, tool, toolArgs) {
|
|
966
982
|
const binding = getContentBinding(profileId);
|
|
967
983
|
if (!binding) return void 0;
|
|
968
|
-
let
|
|
984
|
+
let contentHash;
|
|
969
985
|
if (binding.kind === "jcs") {
|
|
970
|
-
|
|
986
|
+
contentHash = computeContentHash(binding, toolArgs);
|
|
971
987
|
} else {
|
|
972
988
|
const field = tool ? detectContentField(tool) : null;
|
|
973
989
|
if (!field) return void 0;
|
|
974
990
|
const raw = typeof toolArgs[field] === "string" ? toolArgs[field] : "";
|
|
975
|
-
|
|
991
|
+
contentHash = computeContentHash(binding, raw);
|
|
976
992
|
}
|
|
977
993
|
return {
|
|
978
|
-
contentHash
|
|
994
|
+
contentHash,
|
|
979
995
|
contentBinding: { version: binding.version, kind: binding.kind }
|
|
980
996
|
};
|
|
981
997
|
}
|
|
@@ -1111,7 +1127,7 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
|
|
|
1111
1127
|
}
|
|
1112
1128
|
const errors = [];
|
|
1113
1129
|
for (const auth of matchingAuths) {
|
|
1114
|
-
const { result } = await state2.gatekeeper.verifyExecution(auth.path, execution, {
|
|
1130
|
+
const { result } = await state2.gatekeeper.verifyExecution(auth.frameHash ?? auth.path, execution, {
|
|
1115
1131
|
bounds: auth.bounds,
|
|
1116
1132
|
context: auth.context
|
|
1117
1133
|
});
|
|
@@ -1119,6 +1135,15 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
|
|
|
1119
1135
|
}
|
|
1120
1136
|
if (result.approved) {
|
|
1121
1137
|
const authHash = auth.frameHash ?? auth.boundsHash;
|
|
1138
|
+
if (isCommitmentDowngrade(auth)) {
|
|
1139
|
+
return {
|
|
1140
|
+
content: [{
|
|
1141
|
+
type: "text",
|
|
1142
|
+
text: `Refusing to execute ${tool.originalName}: the signed authorization requires review (commitment_mode="${auth.signedCommitmentMode}") but the Authority Server returned no pending approvers. This inconsistency (a possible commitment-mode downgrade) is rejected fail-closed. Re-fetch the authorization or contact the Authority Server operator.`
|
|
1143
|
+
}],
|
|
1144
|
+
isError: true
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1122
1147
|
if ((auth.deferredCommitmentDomains ?? []).length > 0) {
|
|
1123
1148
|
try {
|
|
1124
1149
|
const enrichedArgs = await attachImagePreview(args);
|
|
@@ -1227,7 +1252,7 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
|
|
|
1227
1252
|
}
|
|
1228
1253
|
if (err instanceof SPReceiptError && err.statusCode === 403) {
|
|
1229
1254
|
if (/revoked/i.test(err.message)) {
|
|
1230
|
-
state2.cache.invalidate(auth.path);
|
|
1255
|
+
state2.cache.invalidate(auth.frameHash ?? auth.path);
|
|
1231
1256
|
}
|
|
1232
1257
|
return {
|
|
1233
1258
|
content: [{ type: "text", text: `Blocked by SP: ${err.message}` }],
|
|
@@ -1245,7 +1270,7 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
|
|
|
1245
1270
|
execution: { ...execution },
|
|
1246
1271
|
timestamp: Math.floor(Date.now() / 1e3)
|
|
1247
1272
|
});
|
|
1248
|
-
let outgoingArgs = shouldAttachFooter() && receiptId ? appendVerificationFooter(tool, args, receiptId) : args;
|
|
1273
|
+
let outgoingArgs = shouldAttachFooter() && receiptId ? appendVerificationFooter(tool, args, receiptId, auth.subjects?.[0]) : args;
|
|
1249
1274
|
if (receiptId) outgoingArgs = attachReceiptId(tool, outgoingArgs, receiptId);
|
|
1250
1275
|
return integrationManager2.callTool(tool.integrationId, tool.originalName, outgoingArgs);
|
|
1251
1276
|
}
|
|
@@ -1390,6 +1415,15 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
|
|
|
1390
1415
|
output2.push(`[${auth.path}] ${auth.profileId} (${remainingMin} min remaining)${statusLabel}`);
|
|
1391
1416
|
output2.push("");
|
|
1392
1417
|
output2.push(` Bounds: ${boundsDesc}`);
|
|
1418
|
+
const reviewDomains = auth.deferredCommitmentDomains ?? [];
|
|
1419
|
+
output2.push(reviewDomains.length > 0 ? " Mode: review \u2014 each action requires your approval before it runs (a proposal is created, not executed)" : " Mode: automatic \u2014 actions run immediately within bounds");
|
|
1420
|
+
try {
|
|
1421
|
+
const meta = await state2.spClient.getFrameMetadata(auth.frameHash ?? auth.boundsHash ?? auth.path);
|
|
1422
|
+
if (meta?.aboveCap) {
|
|
1423
|
+
output2.push(" \u26A0 Above team cap \u2014 actions here require approval even within these bounds.");
|
|
1424
|
+
}
|
|
1425
|
+
} catch {
|
|
1426
|
+
}
|
|
1393
1427
|
const shortName = shortProfileName2(auth.profileId);
|
|
1394
1428
|
const profile = getProfile3(auth.profileId) ?? getProfile3(shortName);
|
|
1395
1429
|
if (profile) {
|
|
@@ -1443,6 +1477,9 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
|
|
|
1443
1477
|
if (auth.complete) {
|
|
1444
1478
|
const lines = [` [${auth.path}] ${auth.profileId} \u2014 ${remainingMin} min remaining`];
|
|
1445
1479
|
lines.push(` Bounds: ${boundsDesc}`);
|
|
1480
|
+
if ((auth.deferredCommitmentDomains ?? []).length > 0) {
|
|
1481
|
+
lines.push(" Mode: review (each action requires your approval)");
|
|
1482
|
+
}
|
|
1446
1483
|
const shortName = shortProfileName2(auth.profileId);
|
|
1447
1484
|
const profile = getProfile3(auth.profileId) ?? getProfile3(shortName);
|
|
1448
1485
|
if (profile) {
|
|
@@ -1866,18 +1903,17 @@ function createMcpServer(state2, integrationManager2) {
|
|
|
1866
1903
|
}
|
|
1867
1904
|
|
|
1868
1905
|
// src/lib/gate-content.ts
|
|
1869
|
-
import {
|
|
1870
|
-
import { decodeAttestationBlob } from "@hap/core";
|
|
1906
|
+
import { computeIntentHash } from "@hap/core";
|
|
1907
|
+
import { decodeAttestationBlob as decodeAttestationBlob2 } from "@hap/core";
|
|
1871
1908
|
function hashGateContent(text) {
|
|
1872
|
-
|
|
1873
|
-
return `sha256:${hex}`;
|
|
1909
|
+
return computeIntentHash(text);
|
|
1874
1910
|
}
|
|
1875
1911
|
function verifyGateContentHashes(content, auth) {
|
|
1876
1912
|
const errors = [];
|
|
1877
1913
|
if (auth.attestations.length === 0) {
|
|
1878
1914
|
return { valid: false, errors: ["No attestations available to verify against"] };
|
|
1879
1915
|
}
|
|
1880
|
-
const attestation =
|
|
1916
|
+
const attestation = decodeAttestationBlob2(auth.attestations[0].blob);
|
|
1881
1917
|
const expectedHashes = attestation.payload.gate_content_hashes;
|
|
1882
1918
|
if (!expectedHashes?.intent) {
|
|
1883
1919
|
return { valid: false, errors: ["Attestation does not contain intent hash"] };
|
|
@@ -2477,7 +2513,7 @@ app.post("/internal/gate-content", internalOnly, async (req, res) => {
|
|
|
2477
2513
|
res.status(400).json({ error: "Gate content hash mismatch", details: verification.errors });
|
|
2478
2514
|
return;
|
|
2479
2515
|
}
|
|
2480
|
-
const path = rawPath ||
|
|
2516
|
+
const path = rawPath || storageHash;
|
|
2481
2517
|
state.setGateContent(path, storageHash, auth.profileId, gateContent, {
|
|
2482
2518
|
boundsHash,
|
|
2483
2519
|
contextHash,
|
|
@@ -2537,7 +2573,7 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
|
|
|
2537
2573
|
const syncHash = gate.frameHash ?? gate.boundsHash;
|
|
2538
2574
|
const auth = await state.cache.syncAuthorization(syncHash);
|
|
2539
2575
|
if (auth) {
|
|
2540
|
-
state.setGateContent(
|
|
2576
|
+
state.setGateContent(syncHash, syncHash, auth.profileId, gate.gateContent, {
|
|
2541
2577
|
boundsHash: gate.boundsHash,
|
|
2542
2578
|
contextHash: gate.contextHash,
|
|
2543
2579
|
context: gate.context
|
|
@@ -2545,8 +2581,8 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
|
|
|
2545
2581
|
synced++;
|
|
2546
2582
|
console.error(`[Suveren MCP] Re-synced gate: ${gate.path}`);
|
|
2547
2583
|
} else {
|
|
2548
|
-
state.cache.invalidate(
|
|
2549
|
-
state.gateStore.delete(
|
|
2584
|
+
state.cache.invalidate(syncHash);
|
|
2585
|
+
state.gateStore.delete(syncHash);
|
|
2550
2586
|
orphaned++;
|
|
2551
2587
|
console.error(`[Suveren MCP] Orphan gate purged (SP attestation deleted): ${gate.path}`);
|
|
2552
2588
|
}
|
|
@@ -2709,6 +2745,17 @@ app.get("/internal/gate-content", internalOnly, (req, res) => {
|
|
|
2709
2745
|
res.json({ entries: gates });
|
|
2710
2746
|
}
|
|
2711
2747
|
});
|
|
2748
|
+
app.get("/internal/authorizations", internalOnly, (_req, res) => {
|
|
2749
|
+
const authorizations = state.getEnrichedAuthorizations().map((a) => ({
|
|
2750
|
+
profileId: a.profileId,
|
|
2751
|
+
frameHash: a.frameHash,
|
|
2752
|
+
bounds: a.frame,
|
|
2753
|
+
context: a.context ?? {},
|
|
2754
|
+
intent: a.gateContent?.intent ?? null,
|
|
2755
|
+
deferredCommitmentDomains: a.deferredCommitmentDomains ?? []
|
|
2756
|
+
}));
|
|
2757
|
+
res.json({ authorizations });
|
|
2758
|
+
});
|
|
2712
2759
|
app.get("/internal/manifests", internalOnly, (_req, res) => {
|
|
2713
2760
|
res.json({ manifests: getAllManifests() });
|
|
2714
2761
|
});
|
|
@@ -2782,7 +2829,7 @@ app.listen(port, "0.0.0.0", () => {
|
|
|
2782
2829
|
console.error(`[Suveren MCP] SP server: ${spUrl}`);
|
|
2783
2830
|
profilesLoaded = loadProfiles();
|
|
2784
2831
|
loadManifests();
|
|
2785
|
-
if (integrationRegistry.getEnabled().length === 0) {
|
|
2832
|
+
if (process.env.SUVEREN_DISABLE_AUTO_INTEGRATIONS !== "1" && integrationRegistry.getEnabled().length === 0) {
|
|
2786
2833
|
const personalManifests = getAllManifests().filter((m) => m.personalDefault);
|
|
2787
2834
|
for (const manifest of personalManifests) {
|
|
2788
2835
|
const optionalKeys = new Set(
|