feat(engine): add private NDC nodes and ontology bridge
This commit is contained in:
@@ -15,7 +15,7 @@ const SUPPORTED_CONNECTION_MODES = new Set(["hub", "direct"]);
|
||||
const SUPPORTED_EXECUTOR_STATUSES = new Set(["unknown", "online", "offline", "checking", "error"]);
|
||||
const SUPPORTED_THREAD_STATES = new Set(["active", "archived"]);
|
||||
const SUPPORTED_MESSAGE_ROLES = new Set(["user", "assistant", "system", "tool"]);
|
||||
const SUPPORTED_TOOL_PACKS = new Set(["engine", "ops", "ndc-agent-core", "deploy", "docs"]);
|
||||
const SUPPORTED_TOOL_PACKS = new Set(["engine", "ops", "ndc-agent-core", "ontology", "deploy", "docs"]);
|
||||
const SUPPORTED_RUN_STATUSES = new Set(["running", "completed", "failed", "timeout"]);
|
||||
const AI_WORKSPACE_BRIDGE_PACKAGE_NAME = "@nodedc/ai-workspace-bridge";
|
||||
const AI_WORKSPACE_BRIDGE_PACKAGE_BIN = "ai-workspace-bridge";
|
||||
@@ -81,6 +81,26 @@ const APP_ROUTING_CATALOG = [
|
||||
requiredScopes: ["engine:workspace:read"],
|
||||
deniedText: ACCESS_DENIED_TEXT,
|
||||
},
|
||||
{
|
||||
appId: "ontology",
|
||||
appTitle: "NODE.DC Ontology Core",
|
||||
surface: "global",
|
||||
skillId: "ontology-context",
|
||||
whenToUse: [
|
||||
"canonical entities",
|
||||
"aliases",
|
||||
"relations",
|
||||
"semantic guardrails",
|
||||
"data contracts",
|
||||
"cross-contour context",
|
||||
"Gelios domain model",
|
||||
],
|
||||
actionNamespaces: [],
|
||||
actionIdPrefixes: [],
|
||||
mcpServerNames: ["nodedc_ontology"],
|
||||
requiredScopes: ["ontology:catalog:read"],
|
||||
deniedText: ACCESS_DENIED_TEXT,
|
||||
},
|
||||
{
|
||||
appId: "ops",
|
||||
appTitle: "NODE.DC Ops / Tasker",
|
||||
@@ -2113,18 +2133,23 @@ async function buildRunProfile({ owner, thread, executor, ownerSettings, bridgeP
|
||||
|| thread.originSurface
|
||||
|| "global";
|
||||
const targetContexts = isPlainObject(context.contexts) ? context.contexts : {};
|
||||
const enabledToolPacks = mergeToolPacks(
|
||||
let enabledToolPacks = mergeToolPacks(
|
||||
ownerSettings?.enabledToolPacks,
|
||||
thread.enabledToolPacks,
|
||||
bridgePayload?.enabledToolPacks
|
||||
);
|
||||
const grantResolution = await resolveRunAppGrants({ owner, context, ownerSettings });
|
||||
const appGrants = summarizeRunAppGrants({ appGrants: grantResolution.appGrants });
|
||||
const mcpServers = runProfileMcpServersFromAppGrants(ownerSettings, grantResolution.appGrants);
|
||||
const ontologyGrant = ontologyMcpGrantForExecutor(executor);
|
||||
const resolvedAppGrants = ontologyGrant
|
||||
? { ...grantResolution.appGrants, ontology: ontologyGrant }
|
||||
: grantResolution.appGrants;
|
||||
if (ontologyGrant) enabledToolPacks = mergeToolPacks(enabledToolPacks, ["ontology"]);
|
||||
const appGrants = summarizeRunAppGrants({ appGrants: resolvedAppGrants });
|
||||
const mcpServers = runProfileMcpServersFromAppGrants(ownerSettings, resolvedAppGrants);
|
||||
const mcpServerNames = mcpServers.map((server) => server.serverName).filter(Boolean);
|
||||
const assistantActions = await assistantActionToolProfileForRun();
|
||||
const appCatalog = buildRunProfileAppCatalog({
|
||||
appGrants: grantResolution.appGrants,
|
||||
appGrants: resolvedAppGrants,
|
||||
mcpServers,
|
||||
assistantActions,
|
||||
});
|
||||
@@ -2148,6 +2173,7 @@ async function buildRunProfile({ owner, thread, executor, ownerSettings, bridgeP
|
||||
deniedAppIds: appAccess.deniedAppIds,
|
||||
notGrantedAppIds: appAccess.notGrantedAppIds,
|
||||
entitlementAdapters: grantResolution.diagnostics,
|
||||
ontologyMcp: ontologyMcpDiagnostic(executor, ontologyGrant),
|
||||
mcpServerNames,
|
||||
requiredMcpServerNames,
|
||||
assistantActionIds: assistantActions.actionIds,
|
||||
@@ -2189,6 +2215,41 @@ async function buildRunProfile({ owner, thread, executor, ownerSettings, bridgeP
|
||||
return runProfile;
|
||||
}
|
||||
|
||||
function ontologyMcpGrantForExecutor(executor) {
|
||||
if (!config.ontologyMcpEnabled) return null;
|
||||
if (executor?.connectionMode !== "hub") return null;
|
||||
const pairingCode = cleanPairingCode(executor?.pairingCode);
|
||||
if (!pairingCode || !config.ontologyMcpPublicBaseUrl) return null;
|
||||
return {
|
||||
appId: "ontology",
|
||||
appTitle: "NODE.DC Ontology Core",
|
||||
surface: "global",
|
||||
source: "platform-runtime",
|
||||
status: "granted",
|
||||
scopes: ["ontology:catalog:read"],
|
||||
mcpServers: [{
|
||||
appId: "ontology",
|
||||
appTitle: "NODE.DC Ontology Core",
|
||||
serverName: "nodedc_ontology",
|
||||
url: `${config.ontologyMcpPublicBaseUrl}/api/ai-workspace/hub/v1/ontology-mcp/${encodeURIComponent(pairingCode)}/mcp`,
|
||||
required: false,
|
||||
startupTimeoutSec: 20,
|
||||
toolTimeoutSec: 60,
|
||||
httpHeaders: {
|
||||
Accept: "application/json, text/event-stream",
|
||||
"MCP-Protocol-Version": "2025-06-18",
|
||||
},
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
function ontologyMcpDiagnostic(executor, grant) {
|
||||
if (grant) return { status: "granted", serverName: "nodedc_ontology", readOnly: true };
|
||||
if (!config.ontologyMcpEnabled) return { status: "disabled", readOnly: true };
|
||||
if (executor?.connectionMode !== "hub") return { status: "unavailable_for_direct_executor", readOnly: true };
|
||||
return { status: "pairing_or_public_hub_url_required", readOnly: true };
|
||||
}
|
||||
|
||||
async function assistantActionToolProfileForRun() {
|
||||
const gatewayUrl = assistantActionGatewayUrlForRun();
|
||||
const gatewayToken = optionalString(
|
||||
@@ -2711,6 +2772,10 @@ function buildRunProfilePolicyPrompt({ context, diagnostics, assistantActions })
|
||||
"- Interpret the user's natural-language request first; call assistant actions only after selecting a structured action id.",
|
||||
"- Read assistant actions may execute after structured action selection. Privileged/write assistant actions require preview, explicit user confirmation, then execute.",
|
||||
"- Ops card actions advertised in this run are valid assistant actions: use ops.card.list_recent for reading cards, ops.card.create for creating cards, and ops.card.add_comment for comments instead of refusing because direct Ops MCP tools are absent.",
|
||||
...(diagnostics.mcpServerNames.includes("nodedc_ontology") ? [
|
||||
"- The nodedc_ontology MCP server is the live, read-only semantic source for canonical entities, aliases, relations and guardrails. Use it before inventing workflow names, data contracts or cross-contour bindings.",
|
||||
"- Ontology Core does not expose telemetry, databases, credentials, command dispatch or Studio controls. Request those only through separately granted application capabilities.",
|
||||
] : []),
|
||||
"- Destructive assistant actions are forbidden; offer safe alternatives such as block/disable instead of delete.",
|
||||
"- MCP tokens and headers are runtime secrets and must never be printed in public answers.",
|
||||
];
|
||||
@@ -4465,6 +4530,11 @@ function readConfig() {
|
||||
optionalString(process.env.NDC_AI_WORKSPACE_OPS_GATEWAY_BASE_URL) ||
|
||||
"";
|
||||
const normalizedSetupGatewayUrl = setupGatewayUrl.replace(/\/+$/, "");
|
||||
const ontologyMcpPublicBaseUrl = cleanHttpEndpoint(
|
||||
optionalString(process.env.AI_WORKSPACE_ONTOLOGY_MCP_PUBLIC_URL) ||
|
||||
optionalString(process.env.NDC_AI_WORKSPACE_ONTOLOGY_MCP_PUBLIC_URL) ||
|
||||
httpUrlFromWebSocketUrl(hubWebSocketUrl)
|
||||
);
|
||||
const bridgePackageSpec =
|
||||
optionalString(process.env.AI_WORKSPACE_BRIDGE_PACKAGE_SPEC) ||
|
||||
optionalString(process.env.NDC_AI_WORKSPACE_BRIDGE_PACKAGE_SPEC) ||
|
||||
@@ -4505,6 +4575,16 @@ function readConfig() {
|
||||
),
|
||||
hubInternalAccessToken:
|
||||
explicitHubAccessToken || (isDeployedPublicHubUrl(hubInternalHttpUrl) ? "" : sharedInternalAccessToken),
|
||||
ontologyMcpEnabled: isTruthy(
|
||||
optionalString(process.env.AI_WORKSPACE_ONTOLOGY_MCP_ENABLED) ||
|
||||
optionalString(process.env.NDC_AI_WORKSPACE_ONTOLOGY_MCP_ENABLED) ||
|
||||
"false"
|
||||
) && !isFalsy(
|
||||
optionalString(process.env.AI_WORKSPACE_ONTOLOGY_MCP_ENABLED) ||
|
||||
optionalString(process.env.NDC_AI_WORKSPACE_ONTOLOGY_MCP_ENABLED) ||
|
||||
"false"
|
||||
),
|
||||
ontologyMcpPublicBaseUrl,
|
||||
ontologyLauncherBaseUrl: ontologyLauncherBaseUrl.replace(/\/+$/, ""),
|
||||
launcherInternalAccessToken,
|
||||
opsGatewayBaseUrl: opsGatewayBaseUrl.replace(/\/+$/, ""),
|
||||
|
||||
Reference in New Issue
Block a user