From fe89952252b03fa4d1d18a83649c9cec5c7e7f9d Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 14 Apr 2026 13:26:12 +0200
Subject: [PATCH] fixes ai toolbox connections
---
modules/interfaces/interfaceDbApp.py | 18 +++++++++++-
.../serviceAgent/actionToolAdapter.py | 5 ++++
.../coreTools/_connectionTools.py | 8 +++---
.../services/serviceAgent/mainServiceAgent.py | 28 +++++++++++++++----
.../services/serviceAgent/toolboxRegistry.py | 4 +--
5 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/modules/interfaces/interfaceDbApp.py b/modules/interfaces/interfaceDbApp.py
index bb75e972..e4384882 100644
--- a/modules/interfaces/interfaceDbApp.py
+++ b/modules/interfaces/interfaceDbApp.py
@@ -1266,11 +1266,27 @@ class AppObjects:
return []
def getUserConnectionById(self, connectionId: str) -> Optional[UserConnection]:
- """Get a single UserConnection by ID."""
+ """Get a single UserConnection by ID or by reference string (connection:authority:username)."""
try:
+ # Try direct UUID lookup first
connections = self.db.getRecordset(
UserConnection, recordFilter={"id": connectionId}
)
+
+ # Fallback: parse "connection:authority:username" format from AI agent
+ if not connections and connectionId.startswith("connection:"):
+ parts = connectionId.split(":", 2)
+ if len(parts) >= 3:
+ authority = parts[1]
+ username = parts[2]
+ allConns = self.db.getRecordset(UserConnection, recordFilter={"externalUsername": username})
+ for c in (allConns or []):
+ a = c.get("authority", "")
+ aVal = a.value if hasattr(a, "value") else str(a)
+ if aVal == authority:
+ connections = [c]
+ break
+
if connections:
conn_dict = connections[0]
return UserConnection(
diff --git a/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py b/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py
index 815be871..0026fa23 100644
--- a/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py
+++ b/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py
@@ -135,6 +135,11 @@ def _createDispatchHandler(actionExecutor, methodName: str, actionName: str):
"""Create an async handler that dispatches to the ActionExecutor."""
async def _handler(args: Dict[str, Any], context: Dict[str, Any]) -> ToolResult:
try:
+ if context:
+ if "featureInstanceId" not in args and context.get("featureInstanceId"):
+ args["featureInstanceId"] = context["featureInstanceId"]
+ if "mandateId" not in args and context.get("mandateId"):
+ args["mandateId"] = context["mandateId"]
result = await actionExecutor.executeAction(methodName, actionName, args)
data = _formatActionResult(result)
return ToolResult(
diff --git a/modules/serviceCenter/services/serviceAgent/coreTools/_connectionTools.py b/modules/serviceCenter/services/serviceAgent/coreTools/_connectionTools.py
index e4018014..7073429f 100644
--- a/modules/serviceCenter/services/serviceAgent/coreTools/_connectionTools.py
+++ b/modules/serviceCenter/services/serviceAgent/coreTools/_connectionTools.py
@@ -44,12 +44,12 @@ def _registerConnectionTools(registry: ToolRegistry, services):
return ToolResult(toolCallId="", toolName="listConnections", success=True, data="No connections available.")
lines = []
for conn in connections:
+ connId = conn.get("id", "") if isinstance(conn, dict) else getattr(conn, "id", "")
authority = conn.get("authority", "?") if isinstance(conn, dict) else getattr(conn, "authority", "?")
authorityVal = authority.value if hasattr(authority, "value") else str(authority)
username = conn.get("externalUsername", "") if isinstance(conn, dict) else getattr(conn, "externalUsername", "")
email = conn.get("externalEmail", "") if isinstance(conn, dict) else getattr(conn, "externalEmail", "")
- ref = f"connection:{authorityVal}:{username}"
- lines.append(f"- {ref} ({email})")
+ lines.append(f"- connectionId: {connId} | {authorityVal} | {username} ({email})")
return ToolResult(toolCallId="", toolName="listConnections", success=True, data="\n".join(lines))
except Exception as e:
return ToolResult(toolCallId="", toolName="listConnections", success=False, error=str(e))
@@ -137,7 +137,7 @@ def _registerConnectionTools(registry: ToolRegistry, services):
return ToolResult(toolCallId="", toolName="sendMail", success=False, error=str(e))
_connToolParams = {
- "connectionId": {"type": "string", "description": "UserConnection ID"},
+ "connectionId": {"type": "string", "description": "UserConnection UUID (from listConnections output, e.g. '3fa85f64-...')"},
"service": {"type": "string", "description": "Service name (sharepoint, outlook, drive, etc.)"},
}
@@ -177,7 +177,7 @@ def _registerConnectionTools(registry: ToolRegistry, services):
parameters={
"type": "object",
"properties": {
- "connectionId": {"type": "string", "description": "UserConnection ID"},
+ "connectionId": {"type": "string", "description": "UserConnection UUID (from listConnections output)"},
"to": {"type": "array", "items": {"type": "string"}, "description": "Recipient email addresses"},
"subject": {"type": "string", "description": "Email subject"},
"body": {"type": "string", "description": "Email body — plain text or HTML markup"},
diff --git a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
index b24d9fcb..3638ccf6 100644
--- a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
+++ b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
@@ -267,6 +267,8 @@ class AgentService:
registerCoreTools(registry, self.services)
try:
+ from modules.workflows.processing.shared.methodDiscovery import discoverMethods
+ discoverMethods(self.services)
from modules.workflows.processing.core.actionExecutor import ActionExecutor
actionExecutor = ActionExecutor(self.services)
adapter = ActionToolAdapter(actionExecutor)
@@ -293,10 +295,14 @@ class AgentService:
userConnections: List[str] = []
try:
- connectionService = self._getService("connection")
- if connectionService and hasattr(connectionService, "getConnections"):
- connections = connectionService.getConnections() or []
- userConnections = [c.get("authority", "") for c in connections if c.get("authority")]
+ chatService = self.services.chat if hasattr(self.services, "chat") else None
+ if chatService and hasattr(chatService, "getUserConnections"):
+ connections = chatService.getUserConnections() or []
+ for c in connections:
+ authority = c.get("authority", "") if isinstance(c, dict) else getattr(c, "authority", "")
+ authorityVal = authority.value if hasattr(authority, "value") else str(authority)
+ if authorityVal:
+ userConnections.append(authorityVal)
except Exception as e:
logger.debug("Could not resolve user connections for toolbox activation: %s", e)
@@ -370,13 +376,23 @@ class AgentService:
if registry.isValidTool(toolName):
activatedCount += 1
continue
+ try:
+ from modules.serviceCenter.services.serviceAgent.coreTools import registerCoreTools
+ registerCoreTools(registry, self.services)
+ if registry.isValidTool(toolName):
+ activatedCount += 1
+ logger.info("requestToolbox: re-registered tool '%s' (core) from toolbox '%s'", toolName, toolboxId)
+ continue
+ except Exception:
+ pass
try:
from modules.serviceCenter.services.serviceAgent.actionToolAdapter import ActionToolAdapter
- adapter = ActionToolAdapter(self._getService("actionExecutor"))
+ from modules.workflows.processing.core.actionExecutor import ActionExecutor
+ adapter = ActionToolAdapter(ActionExecutor(self.services))
adapter.registerAll(registry)
if registry.isValidTool(toolName):
activatedCount += 1
- logger.info("requestToolbox: re-registered tool '%s' from toolbox '%s'", toolName, toolboxId)
+ logger.info("requestToolbox: re-registered tool '%s' (action) from toolbox '%s'", toolName, toolboxId)
else:
logger.warning("requestToolbox: tool '%s' from toolbox '%s' could not be registered", toolName, toolboxId)
except Exception as regErr:
diff --git a/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py b/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py
index 32440896..d05cfded 100644
--- a/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py
+++ b/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py
@@ -149,7 +149,7 @@ def _registerDefaultToolboxes() -> None:
id="email",
label="Email",
description="Send emails or save as draft via Outlook (supports HTML body and file attachments). Use sendMail with draft=true for drafts.",
- requiresConnection="microsoft",
+ requiresConnection="msft",
isDefault=False,
tools=[
"sendMail",
@@ -159,7 +159,7 @@ def _registerDefaultToolboxes() -> None:
id="sharepoint",
label="SharePoint",
description="Access SharePoint sites, lists, and files",
- requiresConnection="microsoft",
+ requiresConnection="msft",
isDefault=False,
tools=[
"sharepoint_findDocuments", "sharepoint_readDocuments",