From 167f0f0271300df3cac769632f64900bc9d7d62b Mon Sep 17 00:00:00 2001 From: "railway-app[bot]" <68434857+railway-app[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 23:30:40 +0000 Subject: [PATCH] feat: add cronjob_create tool to mika_runtime plugin --- Dockerfile | 2 + plugins/mika_runtime/__init__.py | 9 ++++ plugins/mika_runtime/plugin.yaml | 1 + plugins/mika_runtime/tools.py | 89 ++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/Dockerfile b/Dockerfile index 36a2a77..124c5e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,6 +72,8 @@ RUN printf '%s\n' \ '# Hermes Soul' \ '' \ 'Default soul. Override via HERMES_SOUL_OVERRIDE.' \ + '' \ + 'Quando o usuário pedir para agendar, lembrar ou automatizar algo recorrente, use a tool cronjob_create passando a frase original dele em natural_language_input.' \ > /opt/data/.hermes/SOUL.md # Cria o usuário hermes (esperado pelos scripts internos do Hermes) diff --git a/plugins/mika_runtime/__init__.py b/plugins/mika_runtime/__init__.py index ca543d4..da23b5b 100644 --- a/plugins/mika_runtime/__init__.py +++ b/plugins/mika_runtime/__init__.py @@ -2,10 +2,12 @@ from plugins.mika_runtime.tools import ( CALCOM_API_SCHEMA, + CRONJOB_CREATE_SCHEMA, INTEGRATIONS_STATUS_SCHEMA, NOTION_API_SCHEMA, TODOIST_API_SCHEMA, handle_calcom_api, + handle_cronjob_create, handle_integrations_status, handle_notion_api, handle_todoist_api, @@ -42,3 +44,10 @@ def register(ctx) -> None: handler=handle_calcom_api, emoji="📅", ) + ctx.register_tool( + name="cronjob_create", + toolset="mika_integrations", + schema=CRONJOB_CREATE_SCHEMA, + handler=handle_cronjob_create, + emoji="⏰", + ) diff --git a/plugins/mika_runtime/plugin.yaml b/plugins/mika_runtime/plugin.yaml index 86282d7..972643a 100644 --- a/plugins/mika_runtime/plugin.yaml +++ b/plugins/mika_runtime/plugin.yaml @@ -8,3 +8,4 @@ provides_tools: - mika_notion_api - mika_todoist_api - mika_calcom_api + - cronjob_create diff --git a/plugins/mika_runtime/tools.py b/plugins/mika_runtime/tools.py index e11a84f..ad60cd7 100644 --- a/plugins/mika_runtime/tools.py +++ b/plugins/mika_runtime/tools.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +import os from pathlib import Path from typing import Any, Dict, Iterable, Tuple from urllib import error, parse, request @@ -504,3 +505,91 @@ def handle_calcom_api(args: dict[str, Any], **_: Any) -> str: }, args=args, ) + + +CRONJOB_CREATE_SCHEMA = { + "name": "cronjob_create", + "description": ( + "Creates a recurring automation, reminder, or cronjob via Supabase edge " + "function. Use this whenever the user asks to schedule, remind, or automate " + "something on a recurring basis (e.g. 'remind me every Monday at 9am')." + ), + "parameters": { + "type": "object", + "properties": { + "natural_language_input": { + "type": "string", + "description": ( + "The user's original request in natural language " + "(e.g., 'remind me every Monday at 9am to check emails')." + ), + }, + "name": { + "type": "string", + "description": "A short name for this automation (optional).", + }, + }, + "required": ["natural_language_input"], + "additionalProperties": False, + }, +} + + +def handle_cronjob_create(args: dict[str, Any], **_: Any) -> str: + supabase_url = os.environ.get("SUPABASE_URL", "").rstrip("/") + internal_secret = os.environ.get("INTERNAL_FUNCTION_SECRET", "") + agent_instance_id = os.environ.get("AGENT_INSTANCE_ID", "") + + if not supabase_url: + return "Erro: variável de ambiente SUPABASE_URL não configurada." + if not internal_secret: + return "Erro: variável de ambiente INTERNAL_FUNCTION_SECRET não configurada." + if not agent_instance_id: + return "Erro: variável de ambiente AGENT_INSTANCE_ID não configurada." + + natural_language_input = str(args.get("natural_language_input") or "").strip() + if not natural_language_input: + return "Erro: natural_language_input é obrigatório." + + name = args.get("name") or None + + url = f"{supabase_url}/functions/v1/create-cronjob-from-agent" + body_payload = { + "agent_instance_id": agent_instance_id, + "natural_language_input": natural_language_input, + "name": name, + } + payload = json.dumps(body_payload, ensure_ascii=False).encode("utf-8") + headers = { + "Content-Type": "application/json", + "x-internal-secret": internal_secret, + "User-Agent": USER_AGENT, + } + + req = request.Request(url=url, data=payload, method="POST", headers=headers) + + try: + with request.urlopen(req, timeout=30) as response: + raw_body = response.read() + try: + data = json.loads(raw_body.decode("utf-8", errors="replace")) + except Exception: + data = {} + human_readable = data.get("human_readable") or data.get("description") or "" + next_run_at = data.get("next_run_at") or "" + if human_readable: + msg = f"Automação criada: {human_readable}." + if next_run_at: + msg += f" Próxima execução: {next_run_at}." + return msg + return "Automação criada com sucesso." + except error.HTTPError as exc: + raw_body = exc.read() + try: + data = json.loads(raw_body.decode("utf-8", errors="replace")) + err_msg = data.get("error") or data.get("message") or str(exc) + except Exception: + err_msg = str(exc) + return f"Erro ao criar automação: {err_msg}" + except Exception as exc: + return f"Erro de rede ao criar automação: {exc}"