@agent-vm/mcp-portal 0.0.80 → 0.0.82
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/{agent-bearer-token-DCtpDPCZ.js → agent-bearer-token-NtEqghPk.js} +1 -1
- package/dist/{agent-bearer-token-DCtpDPCZ.js.map → agent-bearer-token-NtEqghPk.js.map} +1 -1
- package/dist/bin/mcp-portal.js +7 -7
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/core/index.d.ts +28 -10
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -5
- package/dist/hmac-token-B3QdUvuG.d.ts +40 -0
- package/dist/hmac-token-B3QdUvuG.d.ts.map +1 -0
- package/dist/{hmac-token-DBqWY3-w.js → hmac-token-D3c9OUTE.js} +1 -1
- package/dist/{hmac-token-DBqWY3-w.js.map → hmac-token-D3c9OUTE.js.map} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/mcp-proxy/index.d.ts +2 -2
- package/dist/mcp-proxy/index.js +1 -1
- package/dist/portal-auth/agent-bearer-token.js +1 -1
- package/dist/portal-auth/hmac-token.d.ts +2 -40
- package/dist/portal-auth/hmac-token.js +1 -1
- package/dist/portal-config/index.d.ts +1 -1
- package/dist/portal-config/index.js +2 -2
- package/dist/{portal-core-CatZlNq_.d.ts → portal-core-B7scBU6I.d.ts} +46 -22
- package/dist/portal-core-B7scBU6I.d.ts.map +1 -0
- package/dist/{portal-core-e-qajblz.js → portal-core-B8HZPw3z.js} +86 -10
- package/dist/portal-core-B8HZPw3z.js.map +1 -0
- package/dist/{portal-tools-Ct2GuFSc.js → portal-tools-fFyF72Nl.js} +174 -83
- package/dist/portal-tools-fFyF72Nl.js.map +1 -0
- package/dist/{resolve-agent-identity-Dnqv2hAW.js → resolve-agent-identity-BQNGUP66.js} +34 -78
- package/dist/resolve-agent-identity-BQNGUP66.js.map +1 -0
- package/dist/{resolve-agent-identity-C1xp9_2R.d.ts → resolve-agent-identity-BqYlDgBX.d.ts} +4 -13
- package/dist/{resolve-agent-identity-C1xp9_2R.d.ts.map → resolve-agent-identity-BqYlDgBX.d.ts.map} +1 -1
- package/dist/{serve-command-DHkYmO6n.js → serve-command-4BNOH14H.js} +5 -5
- package/dist/{serve-command-DHkYmO6n.js.map → serve-command-4BNOH14H.js.map} +1 -1
- package/dist/{typescript-artifact-BVLt3Ifd.js → typescript-artifact-EQH4tZ0C.js} +2 -2
- package/dist/{typescript-artifact-BVLt3Ifd.js.map → typescript-artifact-EQH4tZ0C.js.map} +1 -1
- package/dist/{upstream-mcp-client-runtime-Be_cw6pV.js → upstream-mcp-client-runtime-vu2TiTUw.js} +3 -3
- package/dist/{upstream-mcp-client-runtime-Be_cw6pV.js.map → upstream-mcp-client-runtime-vu2TiTUw.js.map} +1 -1
- package/dist/{upstream-response-middleware-1MZnAD9C.d.ts → upstream-response-middleware-CkV-rDNO.d.ts} +1 -1
- package/dist/{upstream-response-middleware-1MZnAD9C.d.ts.map → upstream-response-middleware-CkV-rDNO.d.ts.map} +1 -1
- package/dist/{upstream-response-middleware-Cd1MxA6A.js → upstream-response-middleware-_dthoE1r.js} +2 -2
- package/dist/{upstream-response-middleware-Cd1MxA6A.js.map → upstream-response-middleware-_dthoE1r.js.map} +1 -1
- package/dist/{zod-schema-loader-DLGQpYFD.d.ts → zod-schema-loader-BubVafy-.d.ts} +9 -2
- package/dist/zod-schema-loader-BubVafy-.d.ts.map +1 -0
- package/dist/{zod-schema-loader-yNekKNpm.js → zod-schema-loader-C3I-MnWq.js} +76 -5
- package/dist/zod-schema-loader-C3I-MnWq.js.map +1 -0
- package/package.json +3 -3
- package/dist/portal-auth/hmac-token.d.ts.map +0 -1
- package/dist/portal-core-CatZlNq_.d.ts.map +0 -1
- package/dist/portal-core-e-qajblz.js.map +0 -1
- package/dist/portal-tools-Ct2GuFSc.js.map +0 -1
- package/dist/resolve-agent-identity-Dnqv2hAW.js.map +0 -1
- package/dist/zod-schema-loader-DLGQpYFD.d.ts.map +0 -1
- package/dist/zod-schema-loader-yNekKNpm.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { l as jsonObjectSchema } from "./zod-schema-loader-
|
|
2
|
-
import { _ as createPortalAgentIdentity, y as resolvePortalAccessPolicy } from "./upstream-response-middleware-
|
|
3
|
-
import { n as createPortalSessionManager } from "./upstream-mcp-client-runtime-
|
|
4
|
-
import { n as portalToolInputSchemas, t as createPortalToolHandlers } from "./portal-tools-
|
|
1
|
+
import { l as jsonObjectSchema } from "./zod-schema-loader-C3I-MnWq.js";
|
|
2
|
+
import { _ as createPortalAgentIdentity, y as resolvePortalAccessPolicy } from "./upstream-response-middleware-_dthoE1r.js";
|
|
3
|
+
import { n as createPortalSessionManager } from "./upstream-mcp-client-runtime-vu2TiTUw.js";
|
|
4
|
+
import { n as portalToolInputSchemas, r as preparePortalApprovalCallDigests, t as createPortalToolHandlers } from "./portal-tools-fFyF72Nl.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { mcpConfigToResolvedProviders } from "@agent-vm/config-contracts";
|
|
7
7
|
//#region src/core/provider-runtime.ts
|
|
@@ -29,9 +29,24 @@ async function resolveUpstreamServers(props) {
|
|
|
29
29
|
return await Promise.all(mcpConfigToResolvedProviders(props.config).map(async (provider) => resolveUpstreamServer(provider, props.resolveSecret)));
|
|
30
30
|
}
|
|
31
31
|
//#endregion
|
|
32
|
+
//#region src/core/portal-core-validation.ts
|
|
33
|
+
function isUnknownRecord$1(value) {
|
|
34
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
function isPortalCoreJsonValue(value, activeObjects = /* @__PURE__ */ new Set()) {
|
|
37
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
|
|
38
|
+
if (typeof value !== "object") return false;
|
|
39
|
+
if (activeObjects.has(value)) return false;
|
|
40
|
+
activeObjects.add(value);
|
|
41
|
+
const isValid = Array.isArray(value) && value.every((entry) => isPortalCoreJsonValue(entry, activeObjects)) || isUnknownRecord$1(value) && Object.values(value).every((entry) => isPortalCoreJsonValue(entry, activeObjects));
|
|
42
|
+
activeObjects.delete(value);
|
|
43
|
+
return isValid;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
32
46
|
//#region src/core/portal-core.ts
|
|
33
47
|
const maxQueuedPortalCoreEvents = 1024;
|
|
34
48
|
const maxPortalCoreEventBytes = 256 * 1024;
|
|
49
|
+
const maxAgentFacingValidationIssues = 5;
|
|
35
50
|
const portalCallRequestSchema = z.object({
|
|
36
51
|
arguments: jsonObjectSchema,
|
|
37
52
|
id: z.string().min(1),
|
|
@@ -51,8 +66,59 @@ function isUnknownRecord(value) {
|
|
|
51
66
|
function errorRecordFromUnknown(error) {
|
|
52
67
|
return isUnknownRecord(error) ? error : {};
|
|
53
68
|
}
|
|
69
|
+
function isStringArray(value) {
|
|
70
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
71
|
+
}
|
|
72
|
+
function isJsonValueArray(value) {
|
|
73
|
+
return Array.isArray(value) && value.every((entry) => isPortalCoreJsonValue(entry));
|
|
74
|
+
}
|
|
75
|
+
function isValidationIssueReceived(value) {
|
|
76
|
+
return isUnknownRecord(value) && typeof value.type === "string" && (value.preview === void 0 || typeof value.preview === "string");
|
|
77
|
+
}
|
|
78
|
+
function isValidationIssue(value) {
|
|
79
|
+
return isUnknownRecord(value) && typeof value.code === "string" && typeof value.message === "string" && Array.isArray(value.path) && value.path.every((pathPart) => typeof pathPart === "string" || typeof pathPart === "number") && (value.expected === void 0 || typeof value.expected === "string") && (value.keys === void 0 || isStringArray(value.keys)) && (value.received === void 0 || isValidationIssueReceived(value.received)) && (value.values === void 0 || isJsonValueArray(value.values));
|
|
80
|
+
}
|
|
81
|
+
function validationIssuesFromUnknown(error) {
|
|
82
|
+
const issues = errorRecordFromUnknown(error).issues;
|
|
83
|
+
if (!Array.isArray(issues)) return;
|
|
84
|
+
const validationIssues = issues.filter((issue) => isValidationIssue(issue));
|
|
85
|
+
return validationIssues.length > 0 ? validationIssues : void 0;
|
|
86
|
+
}
|
|
87
|
+
function validationIssuePathLabel(path) {
|
|
88
|
+
return path.length === 0 ? "(root)" : path.map((pathPart) => String(pathPart)).join(".");
|
|
89
|
+
}
|
|
90
|
+
function formattedJsonValue(value) {
|
|
91
|
+
return JSON.stringify(value) ?? "[unserializable-json-value]";
|
|
92
|
+
}
|
|
93
|
+
function receivedValueLabel(received) {
|
|
94
|
+
if (received === void 0) return;
|
|
95
|
+
if (received.preview === void 0) return received.type;
|
|
96
|
+
const preview = received.type === "string" ? JSON.stringify(received.preview) : received.preview;
|
|
97
|
+
return `${received.type} ${preview}`;
|
|
98
|
+
}
|
|
99
|
+
function validationIssueSummary(issue) {
|
|
100
|
+
const details = [
|
|
101
|
+
issue.expected === void 0 ? void 0 : `expected ${issue.expected}`,
|
|
102
|
+
issue.values === void 0 ? void 0 : `allowed values ${issue.values.map((value) => formattedJsonValue(value)).join(", ")}`,
|
|
103
|
+
issue.keys === void 0 ? void 0 : `unrecognized keys ${issue.keys.join(", ")}`,
|
|
104
|
+
receivedValueLabel(issue.received) === void 0 ? void 0 : `received ${receivedValueLabel(issue.received)}`,
|
|
105
|
+
issue.message
|
|
106
|
+
].filter((detail) => detail !== void 0);
|
|
107
|
+
return `${validationIssuePathLabel(issue.path)}: ${details.join("; ")}`;
|
|
108
|
+
}
|
|
109
|
+
function agentFacingValidationIssues(issues) {
|
|
110
|
+
return issues.slice(0, maxAgentFacingValidationIssues);
|
|
111
|
+
}
|
|
112
|
+
function messageFromValidationIssues(issues) {
|
|
113
|
+
const shownIssues = agentFacingValidationIssues(issues);
|
|
114
|
+
const truncatedIssues = issues.length - shownIssues.length;
|
|
115
|
+
const suffix = truncatedIssues > 0 ? ` | ${String(truncatedIssues)} more validation issue(s) omitted; call describe for the exact schema.` : "";
|
|
116
|
+
return `Input validation failed: ${shownIssues.map((issue) => validationIssueSummary(issue)).join(" | ")}${suffix}`;
|
|
117
|
+
}
|
|
54
118
|
function messageFromUnknown(error) {
|
|
55
119
|
if (error instanceof Error) return error.message;
|
|
120
|
+
const validationIssues = validationIssuesFromUnknown(error);
|
|
121
|
+
if (validationIssues !== void 0) return messageFromValidationIssues(validationIssues);
|
|
56
122
|
const message = errorRecordFromUnknown(error).message;
|
|
57
123
|
return typeof message === "string" ? message : String(error);
|
|
58
124
|
}
|
|
@@ -101,9 +167,17 @@ function itemErrorFromPortalResult(result) {
|
|
|
101
167
|
const namespace = errorRecord.namespace;
|
|
102
168
|
const toolName = errorRecord.toolName;
|
|
103
169
|
const upstream = errorRecord.upstream;
|
|
170
|
+
const issues = validationIssuesFromUnknown(result.error);
|
|
171
|
+
const shownIssues = issues === void 0 ? void 0 : agentFacingValidationIssues(issues);
|
|
172
|
+
const issuesTruncated = issues === void 0 || shownIssues === void 0 ? void 0 : issues.length - shownIssues.length;
|
|
104
173
|
return {
|
|
105
174
|
code: typeof kind === "string" ? kind : "portal_item_failed",
|
|
106
175
|
message: messageFromUnknown(result.error),
|
|
176
|
+
...issues === void 0 || shownIssues === void 0 ? {} : {
|
|
177
|
+
issueCount: issues.length,
|
|
178
|
+
issues: shownIssues,
|
|
179
|
+
...issuesTruncated === void 0 || issuesTruncated <= 0 ? {} : { issuesTruncated }
|
|
180
|
+
},
|
|
107
181
|
...typeof namespace === "string" ? { namespace } : {},
|
|
108
182
|
...typeof toolName === "string" ? { toolName } : {},
|
|
109
183
|
...upstream === void 0 ? {} : { upstream }
|
|
@@ -349,10 +423,7 @@ function createPortalCore(props) {
|
|
|
349
423
|
upstreamNamespaces: props.upstreamNamespaces
|
|
350
424
|
});
|
|
351
425
|
const createdAgentScopeIds = /* @__PURE__ */ new Set();
|
|
352
|
-
const approval = props.approval
|
|
353
|
-
if (props.approvalTrustBoundary === "openclaw-before-tool-call-hook") return { kind: "allow" };
|
|
354
|
-
throw new Error("MCP Portal approval evaluation is not configured.");
|
|
355
|
-
});
|
|
426
|
+
const approval = props.approval;
|
|
356
427
|
const toolRuntime = {
|
|
357
428
|
approval,
|
|
358
429
|
callUpstreamTool: props.runtime.callUpstreamTool,
|
|
@@ -391,7 +462,12 @@ function createPortalCore(props) {
|
|
|
391
462
|
}
|
|
392
463
|
}
|
|
393
464
|
return {
|
|
394
|
-
approval: {
|
|
465
|
+
approval: {
|
|
466
|
+
evaluateCalls: (calls, scope, approvalToken) => approval(calls, scope, approvalToken),
|
|
467
|
+
prepareCallDigests: async ({ input, scope }) => {
|
|
468
|
+
return preparePortalApprovalCallDigests(await sessionManager.getSession(scope), input);
|
|
469
|
+
}
|
|
470
|
+
},
|
|
395
471
|
callStream,
|
|
396
472
|
close: async () => {
|
|
397
473
|
await Promise.all([...createdAgentScopeIds].map((agentScopeId) => sessionManager.invalidateAgentScope(agentScopeId)));
|
|
@@ -422,4 +498,4 @@ function createPortalCore(props) {
|
|
|
422
498
|
//#endregion
|
|
423
499
|
export { resolveUpstreamServers as i, createPortalCore as n, listPortalCoreToolDescriptors as r, collectPortalCoreResult as t };
|
|
424
500
|
|
|
425
|
-
//# sourceMappingURL=portal-core-
|
|
501
|
+
//# sourceMappingURL=portal-core-B8HZPw3z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portal-core-B8HZPw3z.js","names":["isUnknownRecord"],"sources":["../src/core/provider-runtime.ts","../src/core/portal-core-validation.ts","../src/core/portal-core.ts"],"sourcesContent":["import {\n\tmcpConfigToResolvedProviders,\n\ttype McpConfig,\n\ttype ResolvedMcpProvider,\n\ttype SecretValue,\n} from '@agent-vm/config-contracts';\n\nimport type { NormalizedUpstreamMcpServer } from '../upstream-mcp-client-runtime.js';\n\nexport interface ResolveUpstreamServersProps {\n\treadonly config: McpConfig;\n\treadonly resolveSecret: (secret: SecretValue) => Promise<string>;\n}\n\nasync function resolveProviderSecretRecord(\n\tsecrets: Readonly<Record<string, SecretValue>>,\n\tresolveSecret: (secret: SecretValue) => Promise<string>,\n): Promise<Readonly<Record<string, string>>> {\n\tconst resolvedEntries = await Promise.all(\n\t\tObject.entries(secrets).map(\n\t\t\tasync ([name, secret]) => [name, await resolveSecret(secret)] as const,\n\t\t),\n\t);\n\treturn Object.fromEntries(resolvedEntries);\n}\n\nasync function resolveUpstreamServer(\n\tprovider: ResolvedMcpProvider,\n\tresolveSecret: (secret: SecretValue) => Promise<string>,\n): Promise<NormalizedUpstreamMcpServer> {\n\tif (provider.transport === 'stdio') {\n\t\treturn {\n\t\t\targs: provider.args,\n\t\t\tcommand: provider.command,\n\t\t\t...(provider.cwd === undefined ? {} : { cwd: provider.cwd }),\n\t\t\tenv: await resolveProviderSecretRecord(provider.env, resolveSecret),\n\t\t\tnamespace: provider.namespace,\n\t\t\ttransport: 'stdio',\n\t\t};\n\t}\n\n\treturn {\n\t\theaders: await resolveProviderSecretRecord(provider.headers, resolveSecret),\n\t\tnamespace: provider.namespace,\n\t\ttransport: provider.transport,\n\t\turl: provider.url,\n\t};\n}\n\nexport async function resolveUpstreamServers(\n\tprops: ResolveUpstreamServersProps,\n): Promise<readonly NormalizedUpstreamMcpServer[]> {\n\treturn await Promise.all(\n\t\tmcpConfigToResolvedProviders(props.config).map(async (provider) =>\n\t\t\tresolveUpstreamServer(provider, props.resolveSecret),\n\t\t),\n\t);\n}\n","import type { JsonValue } from '../json-schema.js';\n\nfunction isUnknownRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function isPortalCoreJsonValue(\n\tvalue: unknown,\n\tactiveObjects = new Set<object>(),\n): value is JsonValue {\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value === 'string' ||\n\t\ttypeof value === 'number' ||\n\t\ttypeof value === 'boolean'\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof value !== 'object') {\n\t\treturn false;\n\t}\n\tif (activeObjects.has(value)) {\n\t\treturn false;\n\t}\n\tactiveObjects.add(value);\n\tconst isValid =\n\t\t(Array.isArray(value) && value.every((entry) => isPortalCoreJsonValue(entry, activeObjects))) ||\n\t\t(isUnknownRecord(value) &&\n\t\t\tObject.values(value).every((entry) => isPortalCoreJsonValue(entry, activeObjects)));\n\tactiveObjects.delete(value);\n\treturn isValid;\n}\n","import type { Tool } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\n\nimport { jsonObjectSchema, type JsonValue } from '../json-schema.js';\nimport {\n\tcreatePortalAgentIdentity,\n\tresolvePortalAccessPolicy,\n\ttype PortalAccessPolicyConfig,\n\ttype PortalAgentIdentity,\n\ttype PortalAgentScopeSource,\n} from '../portal-access-policy.js';\nimport {\n\tcreatePortalSessionManager,\n\ttype PortalSessionManager,\n\ttype PortalSessionRuntime,\n} from '../portal-session.js';\nimport type { SkillGraphInput } from '../tool-graph.js';\nimport { isPortalCoreJsonValue } from './portal-core-validation.js';\nimport {\n\tcreatePortalToolHandlers,\n\tportalToolInputSchemas,\n\tpreparePortalApprovalCallDigests,\n\ttype PortalApprovalCallDigestMap,\n\ttype PortalApprovalCall,\n\ttype PortalBatchDiagnostic,\n\ttype PortalBatchResult,\n\ttype PortalToolResult,\n\ttype PortalToolRuntime,\n} from './portal-tools.js';\n\nexport type PortalAgentScope = PortalAgentIdentity;\n\nexport type PortalCoreToolName =\n\t| 'mcp_portal_list'\n\t| 'mcp_portal_search'\n\t| 'mcp_portal_describe'\n\t| 'mcp_portal_call';\n\nexport interface PortalAuditEvent {\n\treadonly causeMessage?: string;\n\treadonly elapsedMs?: number;\n\treadonly hint?: string;\n\treadonly kind: string;\n\treadonly message: string;\n\treadonly namespace?: string;\n\treadonly operation?: string;\n\treadonly phase?: string;\n\treadonly timeoutMs?: number;\n\treadonly toolName?: string;\n\treadonly transport?: unknown;\n}\n\nexport interface PortalCoreResult {\n\treadonly auditEvents?: readonly PortalAuditEvent[];\n\treadonly content: readonly PortalCoreContentBlock[];\n\treadonly items: readonly PortalCoreItemResult[];\n\treadonly structuredContent?: unknown;\n}\n\nexport type PortalCoreItemResult =\n\t| {\n\t\t\treadonly content: readonly PortalCoreContentBlock[];\n\t\t\treadonly requestId: string;\n\t\t\treadonly status: 'success';\n\t\t\treadonly structuredContent?: unknown;\n\t }\n\t| {\n\t\t\treadonly error: PortalCoreItemError;\n\t\t\treadonly requestId: string;\n\t\t\treadonly status: 'failed';\n\t };\n\nexport interface PortalCoreItemError {\n\treadonly code: string;\n\treadonly issues?: readonly PortalCoreValidationIssue[];\n\treadonly issueCount?: number;\n\treadonly issuesTruncated?: number;\n\treadonly message: string;\n\treadonly namespace?: string;\n\treadonly toolName?: string;\n\treadonly upstream?: unknown;\n}\n\nexport interface PortalCoreValidationIssue {\n\treadonly code: string;\n\treadonly expected?: string;\n\treadonly keys?: readonly string[];\n\treadonly message: string;\n\treadonly path: readonly (number | string)[];\n\treadonly received?: {\n\t\treadonly preview?: string;\n\t\treadonly type: string;\n\t};\n\treadonly values?: readonly JsonValue[];\n}\n\nexport type PortalCoreContentBlock =\n\t| { readonly text: string; readonly type: 'text' }\n\t| { readonly type: 'json'; readonly value: unknown };\n\nexport type PortalCoreEvent =\n\t| {\n\t\t\treadonly kind: 'started';\n\t\t\treadonly toolName: PortalCoreToolName;\n\t }\n\t| {\n\t\t\treadonly kind: 'item_started';\n\t\t\treadonly namespace?: string;\n\t\t\treadonly requestId: string;\n\t\t\treadonly toolName?: string;\n\t }\n\t| {\n\t\t\treadonly kind: 'progress';\n\t\t\treadonly message?: string;\n\t\t\treadonly progress?: number;\n\t\t\treadonly requestId?: string;\n\t\t\treadonly total?: number;\n\t }\n\t| {\n\t\t\treadonly kind: 'upstream_notification';\n\t\t\treadonly method: string;\n\t\t\treadonly params: unknown;\n\t\t\treadonly requestId?: string;\n\t }\n\t| {\n\t\t\treadonly content: PortalCoreContentBlock;\n\t\t\treadonly kind: 'partial_content';\n\t\t\treadonly requestId?: string;\n\t }\n\t| {\n\t\t\treadonly kind: 'item_completed';\n\t\t\treadonly requestId: string;\n\t\t\treadonly result: Extract<PortalCoreItemResult, { readonly status: 'success' }>;\n\t }\n\t| {\n\t\t\treadonly error: PortalCoreItemError;\n\t\t\treadonly kind: 'item_failed';\n\t\t\treadonly requestId: string;\n\t }\n\t| {\n\t\t\treadonly kind: 'completed';\n\t\t\treadonly result: PortalCoreResult;\n\t }\n\t| {\n\t\t\treadonly error: unknown;\n\t\t\treadonly kind: 'failed';\n\t };\n\nexport interface PortalCoreStreamCall {\n\treadonly input: unknown;\n\treadonly scope: PortalAgentScope;\n\treadonly signal?: AbortSignal;\n\treadonly toolName: PortalCoreToolName;\n}\n\nconst maxQueuedPortalCoreEvents = 1_024;\nconst maxPortalCoreEventBytes = 256 * 1_024;\nconst maxAgentFacingValidationIssues = 5;\n\nexport interface PortalCoreCollectOptions {\n\treadonly onEvent?: (event: PortalCoreEvent) => Promise<void> | void;\n}\n\nexport interface PortalCoreRuntime extends PortalSessionRuntime {\n\treadonly callUpstreamTool: PortalToolRuntime['callUpstreamTool'];\n}\n\nexport type PortalApprovalEvaluator = NonNullable<PortalToolRuntime['approval']>;\n\ninterface CreatePortalCoreBaseProps {\n\treadonly accessPolicy: PortalAccessPolicyConfig;\n\treadonly catalogTtlMs: number;\n\treadonly runtime: PortalCoreRuntime;\n\treadonly skills?: readonly SkillGraphInput[];\n\treadonly upstreamNamespaces: readonly string[];\n}\n\nexport interface CreatePortalCoreProps extends CreatePortalCoreBaseProps {\n\treadonly approval: PortalApprovalEvaluator;\n}\n\nexport interface PortalCore {\n\treadonly approval: {\n\t\treadonly evaluateCalls: (\n\t\t\tcalls: readonly PortalApprovalCall[],\n\t\t\tscope: PortalAgentScope,\n\t\t\tapprovalToken: string | undefined,\n\t\t) => ReturnType<PortalApprovalEvaluator>;\n\t\treadonly prepareCallDigests: (props: {\n\t\t\treadonly input: unknown;\n\t\t\treadonly scope: PortalAgentScope;\n\t\t}) => Promise<PortalApprovalCallDigestMap | null>;\n\t};\n\treadonly callStream: (call: PortalCoreStreamCall) => AsyncIterable<PortalCoreEvent>;\n\treadonly close: () => Promise<void>;\n\treadonly collectPortalCoreResult: typeof collectPortalCoreResult;\n\treadonly createAgentScope: (input: {\n\t\treadonly agentId: string;\n\t\treadonly agentScopeId: string;\n\t\treadonly authSubject?: string;\n\t\treadonly sessionId?: string;\n\t\treadonly sessionKey?: string;\n\t\treadonly source: PortalAgentScopeSource;\n\t}) => PortalAgentScope;\n\treadonly describeTools: (scope: PortalAgentScope) => readonly PortalCoreToolDescriptor[];\n\treadonly invalidateAgentScope: (agentScopeId: string) => Promise<void>;\n\treadonly invalidateSession: (scope: PortalAgentScope) => Promise<void>;\n\treadonly upstreamNamespaces: readonly string[];\n}\n\nexport interface PortalCoreToolDescriptor {\n\treadonly description: string;\n\treadonly inputSchema: Tool['inputSchema'];\n\treadonly name: PortalCoreToolName;\n}\n\nconst portalCallRequestSchema = z\n\t.object({\n\t\targuments: jsonObjectSchema,\n\t\tid: z.string().min(1),\n\t\tnamespace: z.string().min(1),\n\t\ttoolName: z.string().min(1),\n\t})\n\t.strict();\nconst portalCallInputSchema = z\n\t.object({\n\t\tcalls: z.array(portalCallRequestSchema).min(1),\n\t\tportalApprovalToken: z.string().min(1).optional(),\n\t})\n\t.strict();\n\nfunction diagnosticsToAuditEvents(\n\tdiagnostics: readonly PortalBatchDiagnostic[],\n): readonly PortalAuditEvent[] {\n\treturn diagnostics.map((diagnostic) => ({ ...diagnostic }));\n}\n\nfunction isUnknownRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction errorRecordFromUnknown(error: unknown): Record<string, unknown> {\n\treturn isUnknownRecord(error) ? error : {};\n}\n\nfunction isStringArray(value: unknown): value is readonly string[] {\n\treturn Array.isArray(value) && value.every((entry) => typeof entry === 'string');\n}\n\nfunction isJsonValueArray(value: unknown): value is readonly JsonValue[] {\n\treturn Array.isArray(value) && value.every((entry) => isPortalCoreJsonValue(entry));\n}\n\nfunction isValidationIssueReceived(\n\tvalue: unknown,\n): value is { readonly preview?: string; readonly type: string } {\n\treturn (\n\t\tisUnknownRecord(value) &&\n\t\ttypeof value.type === 'string' &&\n\t\t(value.preview === undefined || typeof value.preview === 'string')\n\t);\n}\n\nfunction isValidationIssue(value: unknown): value is PortalCoreValidationIssue {\n\treturn (\n\t\tisUnknownRecord(value) &&\n\t\ttypeof value.code === 'string' &&\n\t\ttypeof value.message === 'string' &&\n\t\tArray.isArray(value.path) &&\n\t\tvalue.path.every((pathPart) => typeof pathPart === 'string' || typeof pathPart === 'number') &&\n\t\t(value.expected === undefined || typeof value.expected === 'string') &&\n\t\t(value.keys === undefined || isStringArray(value.keys)) &&\n\t\t(value.received === undefined || isValidationIssueReceived(value.received)) &&\n\t\t(value.values === undefined || isJsonValueArray(value.values))\n\t);\n}\n\nfunction validationIssuesFromUnknown(\n\terror: unknown,\n): readonly PortalCoreValidationIssue[] | undefined {\n\tconst issues = errorRecordFromUnknown(error).issues;\n\tif (!Array.isArray(issues)) {\n\t\treturn undefined;\n\t}\n\tconst validationIssues = issues.filter((issue): issue is PortalCoreValidationIssue =>\n\t\tisValidationIssue(issue),\n\t);\n\treturn validationIssues.length > 0 ? validationIssues : undefined;\n}\n\nfunction validationIssuePathLabel(path: readonly (number | string)[]): string {\n\treturn path.length === 0 ? '(root)' : path.map((pathPart) => String(pathPart)).join('.');\n}\n\nfunction formattedJsonValue(value: JsonValue): string {\n\tconst serialized = JSON.stringify(value);\n\treturn serialized ?? '[unserializable-json-value]';\n}\n\nfunction receivedValueLabel(received: PortalCoreValidationIssue['received']): string | undefined {\n\tif (received === undefined) {\n\t\treturn undefined;\n\t}\n\tif (received.preview === undefined) {\n\t\treturn received.type;\n\t}\n\tconst preview = received.type === 'string' ? JSON.stringify(received.preview) : received.preview;\n\treturn `${received.type} ${preview}`;\n}\n\nfunction validationIssueSummary(issue: PortalCoreValidationIssue): string {\n\tconst details = [\n\t\tissue.expected === undefined ? undefined : `expected ${issue.expected}`,\n\t\tissue.values === undefined\n\t\t\t? undefined\n\t\t\t: `allowed values ${issue.values.map((value) => formattedJsonValue(value)).join(', ')}`,\n\t\tissue.keys === undefined ? undefined : `unrecognized keys ${issue.keys.join(', ')}`,\n\t\treceivedValueLabel(issue.received) === undefined\n\t\t\t? undefined\n\t\t\t: `received ${receivedValueLabel(issue.received)}`,\n\t\tissue.message,\n\t].filter((detail): detail is string => detail !== undefined);\n\treturn `${validationIssuePathLabel(issue.path)}: ${details.join('; ')}`;\n}\n\nfunction agentFacingValidationIssues(\n\tissues: readonly PortalCoreValidationIssue[],\n): readonly PortalCoreValidationIssue[] {\n\treturn issues.slice(0, maxAgentFacingValidationIssues);\n}\n\nfunction messageFromValidationIssues(issues: readonly PortalCoreValidationIssue[]): string {\n\tconst shownIssues = agentFacingValidationIssues(issues);\n\tconst truncatedIssues = issues.length - shownIssues.length;\n\tconst suffix =\n\t\ttruncatedIssues > 0\n\t\t\t? ` | ${String(truncatedIssues)} more validation issue(s) omitted; call describe for the exact schema.`\n\t\t\t: '';\n\treturn `Input validation failed: ${shownIssues\n\t\t.map((issue) => validationIssueSummary(issue))\n\t\t.join(' | ')}${suffix}`;\n}\n\nfunction messageFromUnknown(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\tconst validationIssues = validationIssuesFromUnknown(error);\n\tif (validationIssues !== undefined) {\n\t\treturn messageFromValidationIssues(validationIssues);\n\t}\n\tconst record = errorRecordFromUnknown(error);\n\tconst message = record.message;\n\treturn typeof message === 'string' ? message : String(error);\n}\n\nfunction errorFromAbortSignal(signal: AbortSignal): Error {\n\tconst reason: unknown = signal.reason;\n\treturn reason instanceof Error ? reason : new Error('MCP Portal core stream aborted.');\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (signal?.aborted) {\n\t\tthrow errorFromAbortSignal(signal);\n\t}\n}\n\nfunction assertPortalCoreEventSize(event: PortalCoreEvent): void {\n\tconst serialized = JSON.stringify(event);\n\tif (serialized === undefined) {\n\t\treturn;\n\t}\n\tconst byteLength = Buffer.byteLength(serialized, 'utf8');\n\tif (byteLength > maxPortalCoreEventBytes) {\n\t\tthrow new Error(\n\t\t\t`MCP Portal core event exceeded ${String(maxPortalCoreEventBytes)} bytes (${String(byteLength)} bytes).`,\n\t\t);\n\t}\n}\n\nfunction waitForQueuedCoreEvent(props: {\n\treadonly setNotifyQueuedEvent: (notify: (() => void) | undefined) => void;\n\treadonly signal?: AbortSignal;\n}): Promise<void> {\n\tif (props.signal === undefined) {\n\t\treturn new Promise<void>((resolve) => {\n\t\t\tprops.setNotifyQueuedEvent(resolve);\n\t\t});\n\t}\n\tconst signal = props.signal;\n\treturn new Promise<void>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst settle = (complete: () => void): void => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\tprops.setNotifyQueuedEvent(undefined);\n\t\t\tcomplete();\n\t\t};\n\t\tconst onNotify = (): void => {\n\t\t\tsettle(resolve);\n\t\t};\n\t\tconst onAbort = (): void => {\n\t\t\tsettle(() => reject(errorFromAbortSignal(signal)));\n\t\t};\n\t\tprops.setNotifyQueuedEvent(onNotify);\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\tif (signal.aborted) {\n\t\t\tonAbort();\n\t\t}\n\t});\n}\n\nfunction itemErrorFromPortalResult(result: PortalToolResult): PortalCoreItemError {\n\tif (result.ok) {\n\t\tthrow new Error('Cannot convert successful portal result into an item error.');\n\t}\n\tconst errorRecord = errorRecordFromUnknown(result.error);\n\tconst kind = errorRecord.kind;\n\tconst namespace = errorRecord.namespace;\n\tconst toolName = errorRecord.toolName;\n\tconst upstream = errorRecord.upstream;\n\tconst issues = validationIssuesFromUnknown(result.error);\n\tconst shownIssues = issues === undefined ? undefined : agentFacingValidationIssues(issues);\n\tconst issuesTruncated =\n\t\tissues === undefined || shownIssues === undefined\n\t\t\t? undefined\n\t\t\t: issues.length - shownIssues.length;\n\n\treturn {\n\t\tcode: typeof kind === 'string' ? kind : 'portal_item_failed',\n\t\tmessage: messageFromUnknown(result.error),\n\t\t...(issues === undefined || shownIssues === undefined\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tissueCount: issues.length,\n\t\t\t\t\tissues: shownIssues,\n\t\t\t\t\t...(issuesTruncated === undefined || issuesTruncated <= 0 ? {} : { issuesTruncated }),\n\t\t\t\t}),\n\t\t...(typeof namespace === 'string' ? { namespace } : {}),\n\t\t...(typeof toolName === 'string' ? { toolName } : {}),\n\t\t...(upstream === undefined ? {} : { upstream }),\n\t};\n}\n\nfunction itemResultFromPortalToolResult(\n\trequestId: string,\n\tresult: PortalToolResult,\n): PortalCoreItemResult {\n\tif (!result.ok) {\n\t\treturn {\n\t\t\terror: itemErrorFromPortalResult(result),\n\t\t\trequestId,\n\t\t\tstatus: 'failed',\n\t\t};\n\t}\n\n\treturn {\n\t\tcontent: [{ type: 'json', value: result.output }],\n\t\trequestId,\n\t\tstatus: 'success',\n\t\tstructuredContent: result.output,\n\t};\n}\n\nfunction scalarBatchResultToCoreResult(batchResult: PortalBatchResult): PortalCoreResult {\n\treturn {\n\t\tauditEvents: diagnosticsToAuditEvents(batchResult.diagnostics),\n\t\tcontent: [{ type: 'json', value: batchResult }],\n\t\titems: [],\n\t\tstructuredContent: batchResult,\n\t};\n}\n\nfunction batchItemsToCoreResult(props: {\n\treadonly diagnostics: readonly PortalBatchDiagnostic[];\n\treadonly items: readonly PortalCoreItemResult[];\n}): PortalCoreResult {\n\treturn {\n\t\tauditEvents: diagnosticsToAuditEvents(props.diagnostics),\n\t\tcontent: [],\n\t\titems: props.items,\n\t};\n}\n\nfunction namespaceDescription(namespaces: readonly string[]): string {\n\treturn namespaces.length === 0\n\t\t? 'No upstream MCP namespaces are authorized for this agent scope.'\n\t\t: `Allowed namespaces for this agent: ${namespaces.join(', ')}.`;\n}\n\nfunction cloneJsonObject<TValue>(value: TValue): TValue {\n\treturn structuredClone(value);\n}\n\nfunction withListNamespaceSchemaDescription(\n\tinputSchema: Tool['inputSchema'],\n\tnamespaces: readonly string[],\n): Tool['inputSchema'] {\n\tconst clonedSchema = cloneJsonObject(inputSchema);\n\tconst requests = isUnknownRecord(clonedSchema.properties)\n\t\t? clonedSchema.properties.requests\n\t\t: undefined;\n\tconst requestItems = isUnknownRecord(requests) ? requests.items : undefined;\n\tconst requestProperties = isUnknownRecord(requestItems) ? requestItems.properties : undefined;\n\tconst namespaceProperty = isUnknownRecord(requestProperties)\n\t\t? requestProperties.namespaces\n\t\t: undefined;\n\tif (isUnknownRecord(namespaceProperty)) {\n\t\tnamespaceProperty.description =\n\t\t\tnamespaces.length === 0\n\t\t\t\t? 'Optional namespace filter. No upstream MCP namespaces are authorized for this agent. Omit to list all currently discovered authorized namespaces.'\n\t\t\t\t: `Optional namespace filter. Allowed namespaces for this agent: ${namespaces.join(', ')}. Omit to list all currently discovered authorized namespaces.`;\n\t}\n\treturn clonedSchema;\n}\n\nexport function listPortalCoreToolDescriptors(\n\tnamespaces: readonly string[] = [],\n): readonly PortalCoreToolDescriptor[] {\n\tconst scopeDescription = namespaceDescription(namespaces);\n\treturn [\n\t\t{\n\t\t\tdescription: `List authorized MCP namespaces and compact tool summaries. ${scopeDescription}`,\n\t\t\tinputSchema: withListNamespaceSchemaDescription(\n\t\t\t\tportalToolInputSchemas.mcp_portal_list,\n\t\t\t\tnamespaces,\n\t\t\t),\n\t\t\tname: 'mcp_portal_list',\n\t\t},\n\t\t{\n\t\t\tdescription: 'Search the caller scoped MCP Portal index.',\n\t\t\tinputSchema: portalToolInputSchemas.mcp_portal_search,\n\t\t\tname: 'mcp_portal_search',\n\t\t},\n\t\t{\n\t\t\tdescription: 'Describe exact MCP tool schemas and optional TypeScript/Zod helpers.',\n\t\t\tinputSchema: portalToolInputSchemas.mcp_portal_describe,\n\t\t\tname: 'mcp_portal_describe',\n\t\t},\n\t\t{\n\t\t\tdescription: 'Validate and call an authorized upstream MCP tool by namespace and toolName.',\n\t\t\tinputSchema: portalToolInputSchemas.mcp_portal_call,\n\t\t\tname: 'mcp_portal_call',\n\t\t},\n\t];\n}\n\nexport async function collectPortalCoreResult(\n\tevents: AsyncIterable<PortalCoreEvent>,\n\toptions: PortalCoreCollectOptions = {},\n): Promise<PortalCoreResult> {\n\tlet result: PortalCoreResult | undefined;\n\tfor await (const event of events) {\n\t\tawait options.onEvent?.(event);\n\t\tif (event.kind === 'completed') {\n\t\t\tresult = event.result;\n\t\t}\n\t\tif (event.kind === 'failed') {\n\t\t\tthrow event.error;\n\t\t}\n\t}\n\tif (result === undefined) {\n\t\tthrow new Error('MCP Portal core stream ended without a completed event.');\n\t}\n\treturn result;\n}\n\nasync function* scalarToolStream(props: {\n\treadonly input: unknown;\n\treadonly scope: PortalAgentScope;\n\treadonly signal?: AbortSignal;\n\treadonly sessionManager: PortalSessionManager;\n\treadonly toolName: Exclude<PortalCoreToolName, 'mcp_portal_call'>;\n\treadonly toolRuntime: PortalToolRuntime;\n}): AsyncIterable<PortalCoreEvent> {\n\tconst handlers = createPortalToolHandlers(props.toolRuntime);\n\tconst handler =\n\t\tprops.toolName === 'mcp_portal_list'\n\t\t\t? handlers.list\n\t\t\t: props.toolName === 'mcp_portal_search'\n\t\t\t\t? handlers.search\n\t\t\t\t: handlers.describe;\n\tthrowIfAborted(props.signal);\n\tconst batchResult = await handler({ identity: props.scope, input: props.input });\n\tthrowIfAborted(props.signal);\n\tyield { kind: 'completed', result: scalarBatchResultToCoreResult(batchResult) };\n}\n\nasync function* callToolStream(props: {\n\treadonly input: unknown;\n\treadonly scope: PortalAgentScope;\n\treadonly signal?: AbortSignal;\n\treadonly toolRuntime: PortalToolRuntime;\n}): AsyncIterable<PortalCoreEvent> {\n\tconst parsedInput = portalCallInputSchema.safeParse(props.input);\n\tconst queuedEvents: PortalCoreEvent[] = [];\n\tlet notifyQueuedEvent: (() => void) | undefined;\n\tlet executionDone = false;\n\tconst pushEvent = (event: PortalCoreEvent): void => {\n\t\tassertPortalCoreEventSize(event);\n\t\tif (queuedEvents.length >= maxQueuedPortalCoreEvents) {\n\t\t\tthrow new Error(`MCP Portal core event queue exceeded ${maxQueuedPortalCoreEvents} events.`);\n\t\t}\n\t\tqueuedEvents.push(event);\n\t\tnotifyQueuedEvent?.();\n\t\tnotifyQueuedEvent = undefined;\n\t};\n\tconst streamingToolRuntime: PortalToolRuntime = {\n\t\t...props.toolRuntime,\n\t\tcallUpstreamTool: async (call) => {\n\t\t\tthrowIfAborted(props.signal);\n\t\t\tpushEvent({\n\t\t\t\tkind: 'item_started',\n\t\t\t\tnamespace: call.namespace,\n\t\t\t\trequestId: call.requestId,\n\t\t\t\ttoolName: call.toolName,\n\t\t\t});\n\t\t\tpushEvent({\n\t\t\t\tkind: 'progress',\n\t\t\t\tmessage: `Calling upstream MCP tool ${call.namespace}.${call.toolName}.`,\n\t\t\t\trequestId: call.requestId,\n\t\t\t});\n\t\t\treturn await props.toolRuntime.callUpstreamTool({\n\t\t\t\t...call,\n\t\t\t\t...(props.signal !== undefined ? { signal: props.signal } : {}),\n\t\t\t\tonEvent: (event) => {\n\t\t\t\t\tif (event.kind === 'progress') {\n\t\t\t\t\t\tpushEvent({\n\t\t\t\t\t\t\tkind: 'progress',\n\t\t\t\t\t\t\t...(event.message !== undefined ? { message: event.message } : {}),\n\t\t\t\t\t\t\t...(event.progress !== undefined ? { progress: event.progress } : {}),\n\t\t\t\t\t\t\trequestId: call.requestId,\n\t\t\t\t\t\t\t...(event.total !== undefined ? { total: event.total } : {}),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.kind === 'partial_content') {\n\t\t\t\t\t\tpushEvent({\n\t\t\t\t\t\t\tcontent: event.content,\n\t\t\t\t\t\t\tkind: 'partial_content',\n\t\t\t\t\t\t\trequestId: call.requestId,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tpushEvent({\n\t\t\t\t\t\tkind: 'upstream_notification',\n\t\t\t\t\t\tmethod: event.method,\n\t\t\t\t\t\tparams: event.params,\n\t\t\t\t\t\trequestId: call.requestId,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t};\n\tconst handlers = createPortalToolHandlers(streamingToolRuntime);\n\tif (!parsedInput.success) {\n\t\tconst batchResult = await handlers.call({ identity: props.scope, input: props.input });\n\t\tyield { kind: 'completed', result: scalarBatchResultToCoreResult(batchResult) };\n\t\treturn;\n\t}\n\n\tconst itemResults: PortalCoreItemResult[] = [];\n\tconst batchResultPromise = handlers\n\t\t.call({\n\t\t\tidentity: props.scope,\n\t\t\tinput: props.input,\n\t\t})\n\t\t.finally(() => {\n\t\t\texecutionDone = true;\n\t\t\tnotifyQueuedEvent?.();\n\t\t\tnotifyQueuedEvent = undefined;\n\t\t});\n\tconst hasPendingExecutionEvents = (): boolean => !executionDone || queuedEvents.length > 0;\n\twhile (hasPendingExecutionEvents()) {\n\t\tconst event = queuedEvents.shift();\n\t\tif (event !== undefined) {\n\t\t\tyield event;\n\t\t\tcontinue;\n\t\t}\n\t\tthrowIfAborted(props.signal);\n\t\t// Streaming consumes events as they arrive; there is no parallel work to collect here.\n\t\t// eslint-disable-next-line no-await-in-loop\n\t\tawait waitForQueuedCoreEvent({\n\t\t\tsetNotifyQueuedEvent: (notify) => {\n\t\t\t\tnotifyQueuedEvent = notify;\n\t\t\t},\n\t\t\t...(props.signal !== undefined ? { signal: props.signal } : {}),\n\t\t});\n\t}\n\tconst batchResult = await batchResultPromise;\n\tthrowIfAborted(props.signal);\n\tif (batchResult.errors.length > 0) {\n\t\tyield { kind: 'completed', result: scalarBatchResultToCoreResult(batchResult) };\n\t\treturn;\n\t}\n\tfor (const request of parsedInput.data.calls) {\n\t\tconst portalResult = batchResult.results[request.id];\n\t\tconst itemResult =\n\t\t\tportalResult === undefined\n\t\t\t\t? ({\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcode: 'portal_item_missing',\n\t\t\t\t\t\t\tmessage: `MCP Portal did not return a result for request \"${request.id}\".`,\n\t\t\t\t\t\t\tnamespace: request.namespace,\n\t\t\t\t\t\t\ttoolName: request.toolName,\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequestId: request.id,\n\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t} satisfies PortalCoreItemResult)\n\t\t\t\t: itemResultFromPortalToolResult(request.id, portalResult);\n\t\titemResults.push(itemResult);\n\t\tif (itemResult.status === 'success') {\n\t\t\tyield { kind: 'item_completed', requestId: request.id, result: itemResult };\n\t\t} else {\n\t\t\tyield { error: itemResult.error, kind: 'item_failed', requestId: request.id };\n\t\t}\n\t}\n\n\tyield {\n\t\tkind: 'completed',\n\t\tresult: batchItemsToCoreResult({ diagnostics: batchResult.diagnostics, items: itemResults }),\n\t};\n}\n\nexport function createPortalCore(props: CreatePortalCoreProps): PortalCore {\n\tconst sessionManager = createPortalSessionManager({\n\t\taccessPolicy: props.accessPolicy,\n\t\tcatalogTtlMs: props.catalogTtlMs,\n\t\truntime: props.runtime,\n\t\t...(props.skills !== undefined ? { skills: props.skills } : {}),\n\t\tupstreamNamespaces: props.upstreamNamespaces,\n\t});\n\tconst createdAgentScopeIds = new Set<string>();\n\tconst approval = props.approval;\n\tconst toolRuntime: PortalToolRuntime = {\n\t\tapproval,\n\t\tcallUpstreamTool: props.runtime.callUpstreamTool,\n\t\tgetSession: sessionManager.getSession,\n\t};\n\n\tasync function* callStream(call: PortalCoreStreamCall): AsyncIterable<PortalCoreEvent> {\n\t\ttry {\n\t\t\tthrowIfAborted(call.signal);\n\t\t\tyield { kind: 'started', toolName: call.toolName };\n\t\t\tthrowIfAborted(call.signal);\n\t\t\tif (call.toolName === 'mcp_portal_call') {\n\t\t\t\tyield* callToolStream({\n\t\t\t\t\tinput: call.input,\n\t\t\t\t\tscope: call.scope,\n\t\t\t\t\t...(call.signal !== undefined ? { signal: call.signal } : {}),\n\t\t\t\t\ttoolRuntime,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tyield* scalarToolStream({\n\t\t\t\tinput: call.input,\n\t\t\t\tscope: call.scope,\n\t\t\t\t...(call.signal !== undefined ? { signal: call.signal } : {}),\n\t\t\t\tsessionManager,\n\t\t\t\ttoolName: call.toolName,\n\t\t\t\ttoolRuntime,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tyield { error, kind: 'failed' };\n\t\t}\n\t}\n\n\treturn {\n\t\tapproval: {\n\t\t\tevaluateCalls: (calls, scope, approvalToken) => approval(calls, scope, approvalToken),\n\t\t\tprepareCallDigests: async ({ input, scope }) => {\n\t\t\t\tconst session = await sessionManager.getSession(scope);\n\t\t\t\treturn preparePortalApprovalCallDigests(session, input);\n\t\t\t},\n\t\t},\n\t\tcallStream,\n\t\tclose: async () => {\n\t\t\tawait Promise.all(\n\t\t\t\t[...createdAgentScopeIds].map((agentScopeId) =>\n\t\t\t\t\tsessionManager.invalidateAgentScope(agentScopeId),\n\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\tcollectPortalCoreResult,\n\t\tcreateAgentScope: (input) => {\n\t\t\tconst scope = createPortalAgentIdentity(input);\n\t\t\tcreatedAgentScopeIds.add(scope.agentScopeId);\n\t\t\treturn scope;\n\t\t},\n\t\tdescribeTools: (scope) => {\n\t\t\tconst policy = resolvePortalAccessPolicy({\n\t\t\t\tconfig: props.accessPolicy,\n\t\t\t\tidentity: scope,\n\t\t\t\tupstreamNamespaces: props.upstreamNamespaces,\n\t\t\t});\n\t\t\treturn listPortalCoreToolDescriptors(policy.allowedNamespaces);\n\t\t},\n\t\tinvalidateAgentScope: async (agentScopeId) => {\n\t\t\tcreatedAgentScopeIds.delete(agentScopeId);\n\t\t\tawait sessionManager.invalidateAgentScope(agentScopeId);\n\t\t},\n\t\tinvalidateSession: async (scope) => {\n\t\t\tawait sessionManager.invalidateSession(scope);\n\t\t},\n\t\tupstreamNamespaces: props.upstreamNamespaces,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,79 @@
|
|
|
1
|
-
import { l as jsonObjectSchema, n as decodeToolRef, t as buildZodValidatorFromJsonSchema } from "./zod-schema-loader-
|
|
2
|
-
import { h as createToolSummary, m as upstreamMcpFailureDetailsFromUnknown, v as portalAgentScopeKey } from "./upstream-response-middleware-
|
|
3
|
-
import { t as
|
|
1
|
+
import { l as jsonObjectSchema, n as decodeToolRef, t as buildZodValidatorFromJsonSchema } from "./zod-schema-loader-C3I-MnWq.js";
|
|
2
|
+
import { h as createToolSummary, m as upstreamMcpFailureDetailsFromUnknown, v as portalAgentScopeKey } from "./upstream-response-middleware-_dthoE1r.js";
|
|
3
|
+
import { r as verifyApprovalToken, t as hashCallArguments } from "./hmac-token-D3c9OUTE.js";
|
|
4
|
+
import { t as generateTypescriptCatalogArtifact } from "./typescript-artifact-EQH4tZ0C.js";
|
|
4
5
|
import { z } from "zod";
|
|
6
|
+
import { mcpPortalCallPolicyDecision } from "@agent-vm/config-contracts";
|
|
7
|
+
//#region src/core/portal-approval-evaluator.ts
|
|
8
|
+
function approvalTokenCallDigests(calls) {
|
|
9
|
+
return calls.map((call) => ({
|
|
10
|
+
argumentsHash: hashCallArguments(call.arguments),
|
|
11
|
+
namespace: call.namespace,
|
|
12
|
+
toolName: call.toolName
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
function callDecisionFromVerifierReason(reason) {
|
|
16
|
+
return {
|
|
17
|
+
kind: "approval_token_invalid",
|
|
18
|
+
reason
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createPortalPolicyApprovalEvaluator(props) {
|
|
22
|
+
return (calls, identity, token) => {
|
|
23
|
+
const agentId = typeof identity === "string" ? identity : identity.agentId;
|
|
24
|
+
const record = props.resolveRecord(agentId);
|
|
25
|
+
if (record === void 0) {
|
|
26
|
+
const decisionsByCallId = {};
|
|
27
|
+
for (const call of calls) decisionsByCallId[call.id] = callDecisionFromVerifierReason("unknown-agent");
|
|
28
|
+
return { decisionsByCallId };
|
|
29
|
+
}
|
|
30
|
+
const decisionsByCallId = {};
|
|
31
|
+
const callsRequiringApproval = [];
|
|
32
|
+
for (const call of calls) {
|
|
33
|
+
const policyDecision = mcpPortalCallPolicyDecision(record.profile, {
|
|
34
|
+
...call.tool.annotations === void 0 ? {} : { annotations: call.tool.annotations },
|
|
35
|
+
namespace: call.namespace,
|
|
36
|
+
toolName: call.toolName
|
|
37
|
+
});
|
|
38
|
+
if (policyDecision.kind === "allow_without_approval") {
|
|
39
|
+
decisionsByCallId[call.id] = { kind: "allow" };
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (policyDecision.kind === "requires_approval") {
|
|
43
|
+
callsRequiringApproval.push(call);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
decisionsByCallId[call.id] = { kind: "call_blocked" };
|
|
47
|
+
}
|
|
48
|
+
if (callsRequiringApproval.length === 0) return { decisionsByCallId };
|
|
49
|
+
if (record.hmacKey === void 0) {
|
|
50
|
+
for (const call of callsRequiringApproval) decisionsByCallId[call.id] = callDecisionFromVerifierReason("missing-hmac-key");
|
|
51
|
+
return { decisionsByCallId };
|
|
52
|
+
}
|
|
53
|
+
if (token === void 0) {
|
|
54
|
+
const missingTokenDecision = props.missingApprovalTokenDecision ?? { kind: "approval_token_missing" };
|
|
55
|
+
for (const call of callsRequiringApproval) decisionsByCallId[call.id] = missingTokenDecision;
|
|
56
|
+
return { decisionsByCallId };
|
|
57
|
+
}
|
|
58
|
+
const consumeTokenId = props.consumeTokenId;
|
|
59
|
+
const verification = verifyApprovalToken({
|
|
60
|
+
agentId,
|
|
61
|
+
calls: approvalTokenCallDigests(callsRequiringApproval),
|
|
62
|
+
...consumeTokenId === void 0 ? {} : { consumeTokenId: (jti, expiresAtMs) => consumeTokenId(agentId, jti, expiresAtMs) },
|
|
63
|
+
key: record.hmacKey,
|
|
64
|
+
...props.maxLifetimeMs === void 0 ? {} : { maxLifetimeMs: props.maxLifetimeMs },
|
|
65
|
+
nowMs: props.nowMs?.() ?? Date.now(),
|
|
66
|
+
token
|
|
67
|
+
});
|
|
68
|
+
if (!verification.ok) {
|
|
69
|
+
for (const call of callsRequiringApproval) decisionsByCallId[call.id] = callDecisionFromVerifierReason(verification.reason);
|
|
70
|
+
return { decisionsByCallId };
|
|
71
|
+
}
|
|
72
|
+
for (const call of callsRequiringApproval) decisionsByCallId[call.id] = { kind: "allow" };
|
|
73
|
+
return { decisionsByCallId };
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
5
77
|
//#region src/core/portal-call-validation.ts
|
|
6
78
|
function validatePortalToolArguments(tool, argumentsValue) {
|
|
7
79
|
const validator = buildZodValidatorFromJsonSchema(tool.inputSchema);
|
|
@@ -114,6 +186,9 @@ const portalToolInputSchemas = {
|
|
|
114
186
|
function messageFromError(error) {
|
|
115
187
|
return error instanceof Error ? error.message : String(error);
|
|
116
188
|
}
|
|
189
|
+
function approvalEvaluationForAllCalls(calls, decision) {
|
|
190
|
+
return { decisionsByCallId: Object.fromEntries(calls.map((call) => [call.id, decision])) };
|
|
191
|
+
}
|
|
117
192
|
function invalidPortalInput(error) {
|
|
118
193
|
return {
|
|
119
194
|
diagnostics: [],
|
|
@@ -408,6 +483,21 @@ async function executePreparedPortalCall(call, identity, runtime) {
|
|
|
408
483
|
function isPreparedPortalCall(value) {
|
|
409
484
|
return "validatedArguments" in value;
|
|
410
485
|
}
|
|
486
|
+
function preparePortalApprovalCallDigests(session, input) {
|
|
487
|
+
const parsedInput = callExecutionInputSchema.safeParse(input);
|
|
488
|
+
if (!parsedInput.success || duplicateIdResult(parsedInput.data.calls)) return null;
|
|
489
|
+
const preparedResults = parsedInput.data.calls.map((request) => preparePortalCall(session, request));
|
|
490
|
+
const digests = {};
|
|
491
|
+
for (const preparedResult of preparedResults) {
|
|
492
|
+
if (!isPreparedPortalCall(preparedResult)) continue;
|
|
493
|
+
digests[preparedResult.input.id] = {
|
|
494
|
+
argumentsHash: hashCallArguments(preparedResult.validatedArguments),
|
|
495
|
+
namespace: preparedResult.tool.namespace,
|
|
496
|
+
toolName: preparedResult.tool.toolName
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return digests;
|
|
500
|
+
}
|
|
411
501
|
async function addExecutableCallResults(props) {
|
|
412
502
|
await Promise.all(props.preparedCalls.map(async (preparedCall) => {
|
|
413
503
|
props.results[preparedCall.input.id] = await executePreparedPortalCall(preparedCall, props.identity, props.runtime);
|
|
@@ -429,7 +519,7 @@ function createPortalToolHandlers(runtime) {
|
|
|
429
519
|
tool: executableCall.tool,
|
|
430
520
|
toolName: executableCall.tool.toolName
|
|
431
521
|
}));
|
|
432
|
-
const approval = approvalCalls.length === 0 ? { kind: "allow" } : runtime.approval?.(approvalCalls, call.identity, parsedInput.data.portalApprovalToken) ?? { kind: "approval_configuration_missing" };
|
|
522
|
+
const approval = approvalCalls.length === 0 ? approvalEvaluationForAllCalls(approvalCalls, { kind: "allow" }) : runtime.approval?.(approvalCalls, call.identity, parsedInput.data.portalApprovalToken) ?? approvalEvaluationForAllCalls(approvalCalls, { kind: "approval_configuration_missing" });
|
|
433
523
|
const results = {};
|
|
434
524
|
const callsToExecute = [];
|
|
435
525
|
for (const preparedResult of preparedResults) {
|
|
@@ -441,84 +531,85 @@ function createPortalToolHandlers(runtime) {
|
|
|
441
531
|
}
|
|
442
532
|
continue;
|
|
443
533
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
534
|
+
const approvalDecision = approval.decisionsByCallId[preparedResult.input.id] ?? { kind: "approval_configuration_missing" };
|
|
535
|
+
switch (approvalDecision.kind) {
|
|
536
|
+
case "allow":
|
|
537
|
+
callsToExecute.push(preparedResult);
|
|
538
|
+
continue;
|
|
539
|
+
case "approval_required":
|
|
540
|
+
results[preparedResult.input.id] = itemError({
|
|
541
|
+
error: {
|
|
542
|
+
kind: "approval_required",
|
|
543
|
+
level: approvalDecision.level,
|
|
544
|
+
message: "Operator approval is required before this MCP Portal call can run.",
|
|
545
|
+
namespace: preparedResult.tool.namespace,
|
|
546
|
+
toolName: preparedResult.tool.toolName
|
|
547
|
+
},
|
|
548
|
+
input: {
|
|
549
|
+
...preparedResult.input,
|
|
550
|
+
arguments: preparedResult.validatedArguments
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
continue;
|
|
554
|
+
case "approval_token_missing":
|
|
555
|
+
results[preparedResult.input.id] = itemError({
|
|
556
|
+
error: {
|
|
557
|
+
kind: "approval_token_missing",
|
|
558
|
+
message: "An MCP Portal approval token is required before this MCP Portal call can run.",
|
|
559
|
+
namespace: preparedResult.tool.namespace,
|
|
560
|
+
toolName: preparedResult.tool.toolName
|
|
561
|
+
},
|
|
562
|
+
input: {
|
|
563
|
+
...preparedResult.input,
|
|
564
|
+
arguments: preparedResult.validatedArguments
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
continue;
|
|
568
|
+
case "approval_token_invalid":
|
|
569
|
+
results[preparedResult.input.id] = itemError({
|
|
570
|
+
error: {
|
|
571
|
+
kind: "approval_token_invalid",
|
|
572
|
+
message: `MCP Portal approval token is invalid: ${approvalDecision.reason}.`,
|
|
573
|
+
namespace: preparedResult.tool.namespace,
|
|
574
|
+
reason: approvalDecision.reason,
|
|
575
|
+
toolName: preparedResult.tool.toolName
|
|
576
|
+
},
|
|
577
|
+
input: {
|
|
578
|
+
...preparedResult.input,
|
|
579
|
+
arguments: preparedResult.validatedArguments
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
continue;
|
|
583
|
+
case "call_blocked":
|
|
584
|
+
results[preparedResult.input.id] = itemError({
|
|
585
|
+
error: {
|
|
586
|
+
kind: "call_blocked",
|
|
587
|
+
message: "MCP Portal policy does not allow this tool call.",
|
|
588
|
+
namespace: preparedResult.tool.namespace,
|
|
589
|
+
toolName: preparedResult.tool.toolName
|
|
590
|
+
},
|
|
591
|
+
input: {
|
|
592
|
+
...preparedResult.input,
|
|
593
|
+
arguments: preparedResult.validatedArguments
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
continue;
|
|
597
|
+
case "approval_configuration_missing":
|
|
598
|
+
results[preparedResult.input.id] = itemError({
|
|
599
|
+
error: {
|
|
600
|
+
kind: "approval_configuration_missing",
|
|
601
|
+
message: "MCP Portal approval evaluation is not configured.",
|
|
602
|
+
namespace: preparedResult.tool.namespace,
|
|
603
|
+
toolName: preparedResult.tool.toolName
|
|
604
|
+
},
|
|
605
|
+
input: {
|
|
606
|
+
...preparedResult.input,
|
|
607
|
+
arguments: preparedResult.validatedArguments
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
continue;
|
|
611
|
+
default: throw new Error(`Unhandled MCP Portal approval decision: ${JSON.stringify(approvalDecision)}`);
|
|
520
612
|
}
|
|
521
|
-
callsToExecute.push(preparedResult);
|
|
522
613
|
}
|
|
523
614
|
await addExecutableCallResults({
|
|
524
615
|
identity: call.identity,
|
|
@@ -555,6 +646,6 @@ function createPortalToolHandlers(runtime) {
|
|
|
555
646
|
};
|
|
556
647
|
}
|
|
557
648
|
//#endregion
|
|
558
|
-
export { portalToolInputSchemas as n,
|
|
649
|
+
export { createPortalPolicyApprovalEvaluator as a, validatePortalToolArguments as i, portalToolInputSchemas as n, preparePortalApprovalCallDigests as r, createPortalToolHandlers as t };
|
|
559
650
|
|
|
560
|
-
//# sourceMappingURL=portal-tools-
|
|
651
|
+
//# sourceMappingURL=portal-tools-fFyF72Nl.js.map
|