feat: add executeTool endpoint for direct plugin tool invocation

Adds POST /SmartAssistant/action/executeTool that accepts {tool, params, caseId}
and routes directly to the plugin tool handler via ActionExecutor. This allows
the AI Gateway to call plugin tools (like generate_direct_access_report) without
going through the conversation approval flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 11:31:16 +00:00
parent 2a8046b737
commit f84b2f1cef
3 changed files with 32 additions and 2 deletions
@@ -9,18 +9,22 @@ use Espo\Core\Exceptions\Forbidden;
use Espo\Core\InjectableFactory; use Espo\Core\InjectableFactory;
use Espo\Core\Acl; use Espo\Core\Acl;
use Espo\Modules\SmartAssistant\Services\SmartAssistantService; use Espo\Modules\SmartAssistant\Services\SmartAssistantService;
use Espo\Modules\SmartAssistant\Services\ActionExecutor;
use Espo\Modules\SmartAssistant\Services\CaseMemoryService; use Espo\Modules\SmartAssistant\Services\CaseMemoryService;
use Espo\Modules\SmartAssistant\Services\DocumentAnalyzer; use Espo\Modules\SmartAssistant\Services\DocumentAnalyzer;
use Espo\Entities\User;
class SmartAssistant class SmartAssistant
{ {
private InjectableFactory $injectableFactory; private InjectableFactory $injectableFactory;
private Acl $acl; private Acl $acl;
private User $user;
public function __construct(InjectableFactory $injectableFactory, Acl $acl) public function __construct(InjectableFactory $injectableFactory, Acl $acl, User $user)
{ {
$this->injectableFactory = $injectableFactory; $this->injectableFactory = $injectableFactory;
$this->acl = $acl; $this->acl = $acl;
$this->user = $user;
} }
private function getService(): SmartAssistantService private function getService(): SmartAssistantService
@@ -171,6 +175,24 @@ class SmartAssistant
]; ];
} }
public function postActionExecuteTool(Request $request, Response $response): array
{
$this->checkAccess();
$data = $request->getParsedBody();
if (empty($data->tool)) {
throw new BadRequest('tool is required.');
}
$tool = $data->tool;
$params = (array) ($data->params ?? []);
$caseId = $data->caseId ?? null;
$executor = $this->injectableFactory->create(ActionExecutor::class);
return $executor->execute($tool, $params, $caseId, $this->user->getId());
}
public function postActionReadDocument(Request $request, Response $response): array public function postActionReadDocument(Request $request, Response $response): array
{ {
$this->checkAccess(); $this->checkAccess();
@@ -86,5 +86,13 @@
"controller": "SmartAssistant", "controller": "SmartAssistant",
"action": "saveMemory" "action": "saveMemory"
} }
},
{
"route": "/SmartAssistant/action/executeTool",
"method": "post",
"params": {
"controller": "SmartAssistant",
"action": "executeTool"
}
} }
] ]
+1 -1
View File
@@ -3,7 +3,7 @@
"module": "SmartAssistant", "module": "SmartAssistant",
"description": "Unified AI Assistant for Legal CRM — floating chat with case memory, office alerts, and AI Gateway integration", "description": "Unified AI Assistant for Legal CRM — floating chat with case memory, office alerts, and AI Gateway integration",
"author": "klear", "author": "klear",
"version": "2.6.0", "version": "2.6.1",
"acceptableVersions": [ "acceptableVersions": [
">=8.0.0" ">=8.0.0"
], ],