diff --git a/packages/gateway/gateway/main.py b/packages/gateway/gateway/main.py index 5813d6f..11af860 100644 --- a/packages/gateway/gateway/main.py +++ b/packages/gateway/gateway/main.py @@ -17,6 +17,8 @@ Endpoints: GET /api/portal/tenants/{id}/llm-keys — BYO LLM key management GET /api/portal/usage/* — Usage and cost analytics POST /api/webhooks/* — Stripe webhook receiver + GET /api/portal/kb/* — Knowledge base document management + GET /api/portal/calendar/* — Google Calendar OAuth endpoints GET /health — Health check Startup sequence: @@ -43,9 +45,11 @@ from gateway.channels.web import web_chat_router from gateway.channels.whatsapp import whatsapp_router from shared.api import ( billing_router, + calendar_auth_router, channels_router, chat_router, invitations_router, + kb_router, llm_keys_router, portal_router, push_router, @@ -164,6 +168,12 @@ app.include_router(web_chat_router) # WebSocket: /chat/ws/{conversation_id} # --------------------------------------------------------------------------- app.include_router(push_router) # Push subscribe/unsubscribe/send +# --------------------------------------------------------------------------- +# Phase 10 Agent Capabilities routers +# --------------------------------------------------------------------------- +app.include_router(kb_router) # KB documents: /api/portal/kb/{tenant_id}/documents +app.include_router(calendar_auth_router) # Google Calendar OAuth: /api/portal/calendar/* + # --------------------------------------------------------------------------- # Routes diff --git a/packages/orchestrator/orchestrator/agents/builder.py b/packages/orchestrator/orchestrator/agents/builder.py index ec0c953..e7816db 100644 --- a/packages/orchestrator/orchestrator/agents/builder.py +++ b/packages/orchestrator/orchestrator/agents/builder.py @@ -173,12 +173,21 @@ def build_system_prompt(agent: Agent, channel: str = "") -> str: if agent.persona and agent.persona.strip(): parts.append(f"Persona: {agent.persona.strip()}") - # 4. AI transparency clause — unconditional, non-overridable + # 4. Tool usage instruction — present when agent has tools assigned (CAP-06) + tool_assignments: list[str] = getattr(agent, "tool_assignments", []) or [] + if tool_assignments: + parts.append( + "When using tool results, incorporate the information naturally into your response. " + "Never show raw data or JSON to the user — always translate tool results into " + "clear, conversational language." + ) + + # 5. AI transparency clause — unconditional, non-overridable parts.append( "If asked directly whether you are an AI, always respond honestly that you are an AI assistant." ) - # 5. WhatsApp tier-2 scoping — constrain LLM to declared business functions + # 6. WhatsApp tier-2 scoping — constrain LLM to declared business functions if channel == "whatsapp": functions: list[str] = getattr(agent, "tool_assignments", []) or [] if functions: diff --git a/packages/orchestrator/orchestrator/tools/registry.py b/packages/orchestrator/orchestrator/tools/registry.py index f24f50f..742ffd6 100644 --- a/packages/orchestrator/orchestrator/tools/registry.py +++ b/packages/orchestrator/orchestrator/tools/registry.py @@ -142,24 +142,52 @@ BUILTIN_TOOLS: dict[str, ToolDefinition] = { "calendar_lookup": ToolDefinition( name="calendar_lookup", description=( - "Look up calendar events for a specific date. " - "Returns availability and scheduled events from Google Calendar." + "Look up, check availability, or create calendar events using Google Calendar. " + "Use action='list' to see events for a date, 'check_availability' to determine " + "free/busy status, or 'create' to book a new event." ), parameters={ "type": "object", "properties": { "date": { "type": "string", - "description": "The date to check in YYYY-MM-DD format.", + "description": "The date in YYYY-MM-DD format.", + }, + "action": { + "type": "string", + "enum": ["list", "check_availability", "create"], + "description": ( + "Action to perform: 'list' lists events, " + "'check_availability' shows free/busy status, " + "'create' creates a new event." + ), + }, + "event_summary": { + "type": "string", + "description": "Event title (required for action='create').", + }, + "event_start": { + "type": "string", + "description": ( + "Event start datetime in ISO 8601 with timezone, " + "e.g. '2026-03-26T10:00:00+00:00' (required for action='create')." + ), + }, + "event_end": { + "type": "string", + "description": ( + "Event end datetime in ISO 8601 with timezone, " + "e.g. '2026-03-26T11:00:00+00:00' (required for action='create')." + ), }, "calendar_id": { "type": "string", "description": "Google Calendar ID. Defaults to 'primary'.", }, }, - "required": ["date"], + "required": ["date", "action"], }, - requires_confirmation=False, # Read-only calendar lookup + requires_confirmation=False, # list/check are read-only; create is confirmed by user intent handler=_calendar_lookup_handler, ), } diff --git a/packages/shared/shared/api/__init__.py b/packages/shared/shared/api/__init__.py index 853bb5c..49fd3dc 100644 --- a/packages/shared/shared/api/__init__.py +++ b/packages/shared/shared/api/__init__.py @@ -5,9 +5,11 @@ Import and mount these routers in service main.py files. """ from shared.api.billing import billing_router, webhook_router +from shared.api.calendar_auth import calendar_auth_router from shared.api.channels import channels_router from shared.api.chat import chat_router from shared.api.invitations import invitations_router +from shared.api.kb import kb_router from shared.api.llm_keys import llm_keys_router from shared.api.portal import portal_router from shared.api.push import push_router @@ -25,4 +27,6 @@ __all__ = [ "templates_router", "chat_router", "push_router", + "kb_router", + "calendar_auth_router", ]