15 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10-agent-capabilities | 02 | execute | 1 |
|
true |
|
|
|
Purpose: Enables CAP-05 (calendar availability checking + event creation) by replacing the service account stub in calendar_lookup.py with per-tenant OAuth token lookup. Also addresses CAP-06 (natural language tool results) by ensuring calendar and all tool outputs are formatted as readable text.
Output: Google Calendar OAuth install/callback endpoints, fully functional calendar_lookup tool with list/create/check_availability actions, encrypted per-tenant token storage, token auto-refresh with write-back.
<execution_context> @/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md @/home/adelorenzo/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/10-agent-capabilities/10-CONTEXT.md @.planning/phases/10-agent-capabilities/10-RESEARCH.mdFrom packages/shared/shared/api/channels.py:
channels_router = APIRouter(prefix="/api/portal/channels", tags=["channels"])
def _generate_oauth_state(tenant_id: uuid.UUID) -> str:
"""HMAC-SHA256 signed state with embedded tenant_id + nonce."""
...
def _verify_oauth_state(state: str) -> uuid.UUID:
"""Verify HMAC signature, return tenant_id. Raises HTTPException on failure."""
...
From packages/shared/shared/crypto.py:
class KeyEncryptionService:
def encrypt(self, plaintext: str) -> str: ...
def decrypt(self, ciphertext: str) -> str: ...
From packages/shared/shared/models/tenant.py:
class ChannelConnection(Base):
__tablename__ = "channel_connections"
id: Mapped[uuid.UUID]
tenant_id: Mapped[uuid.UUID]
channel_type: Mapped[ChannelTypeEnum] # TEXT + CHECK in DB
workspace_id: Mapped[str]
config: Mapped[dict] # JSON — stores encrypted token
created_at: Mapped[datetime]
From packages/shared/shared/config.py (after Plan 01):
class Settings(BaseSettings):
google_client_id: str = ""
google_client_secret: str = ""
- GET /callback?code={code}&state={state}:
- NO auth guard (external redirect from Google — no session cookie)
- Verify HMAC state to recover tenant_id
- Exchange code for tokens using google_auth_oauthlib or httpx POST to https://oauth2.googleapis.com/token
- Encrypt token JSON with KeyEncryptionService (Fernet)
- Upsert ChannelConnection(tenant_id=tenant_id, channel_type="google_calendar", workspace_id=str(tenant_id), config={"token": encrypted_token})
- Redirect to portal /settings?calendar=connected
- GET /{tenant_id}/status:
- Guard with require_tenant_member
- Check if ChannelConnection with channel_type='google_calendar' exists for tenant
- Return {"connected": true/false}
2. **Replace calendar_lookup.py** entirely:
- Remove all service account code
- New signature: async def calendar_lookup(date: str, action: str = "list", event_summary: str | None = None, event_start: str | None = None, event_end: str | None = None, calendar_id: str = "primary", tenant_id: str | None = None, **kwargs) -> str
- If no tenant_id: return "Calendar not available: missing tenant context."
- Load ChannelConnection(channel_type='google_calendar', tenant_id=tenant_uuid) from DB
- If not found: return "Google Calendar is not connected for this tenant. Ask an admin to connect it in Settings."
- Decrypt token JSON, build google.oauth2.credentials.Credentials
- Build Calendar service: build("calendar", "v3", credentials=creds, cache_discovery=False)
- Run API call in thread executor (same pattern as original — avoid blocking event loop)
- action="list": list events for date, format as "Calendar events for {date}:\n- {time}: {summary}\n..."
- action="check_availability": list events, format as "Busy slots on {date}:\n..." or "No events — the entire day is free."
- action="create": insert event with summary, start, end, return "Event created: {summary} from {start} to {end}"
- After API call: check if credentials.token changed (refresh occurred) — if so, encrypt and UPDATE channel_connections.config with new token
- All errors return human-readable messages, never raw exceptions
3. **Update tool registry** if needed — ensure calendar_lookup parameters schema includes action, event_summary, event_start, event_end fields so LLM knows about CRUD capabilities. Check packages/orchestrator/orchestrator/tools/registry.py for the calendar_lookup entry and update its parameters JSON schema.
4. **Tests** (write BEFORE implementation):
- test_calendar_lookup.py: mock Google Calendar API (googleapiclient.discovery.build), mock DB session to return encrypted token, test list/create/check_availability actions, test "not connected" path, test token refresh write-back
- test_calendar_auth.py: mock httpx for token exchange, test HMAC state generation/verification, test callback stores encrypted token
cd /home/adelorenzo/repos/konstruct && python -m pytest tests/unit/test_calendar_lookup.py tests/unit/test_calendar_auth.py -x -q
Google Calendar OAuth install/callback endpoints work. Calendar tool loads per-tenant tokens, supports list/create/check_availability, formats results as natural language. Token refresh writes back to DB. Service account stub completely removed. All tests pass.
Task 2: Mount new API routers on gateway and update tool response formatting
packages/gateway/gateway/main.py,
packages/orchestrator/orchestrator/tools/registry.py,
packages/orchestrator/orchestrator/agents/prompt.py
1. **Mount routers on gateway** (`packages/gateway/gateway/main.py`):
- Import kb_router from shared.api.kb and include it on the FastAPI app (same pattern as channels_router, billing_router, etc.)
- Import calendar_auth_router from shared.api.calendar_auth and include it on the app
- Verify both are accessible via curl or import
2. **Update tool registry** (`packages/orchestrator/orchestrator/tools/registry.py`):
- Update calendar_lookup tool definition's parameters schema to include:
- action: enum ["list", "check_availability", "create"] (required)
- event_summary: string (optional, for create)
- event_start: string (optional, ISO 8601 with timezone, for create)
- event_end: string (optional, ISO 8601 with timezone, for create)
- date: string (required, YYYY-MM-DD format)
- Update description to mention CRUD capabilities: "Look up, check availability, or create calendar events"
3. **Tool result formatting check** (CAP-06):
- Review agent runner prompt — the LLM already receives tool results as 'tool' role messages and formulates a response. Verify the system prompt does NOT contain instructions to dump raw JSON.
- If the system prompt builder (`packages/orchestrator/orchestrator/agents/prompt.py` or similar) has tool-related instructions, ensure it says: "When using tool results, incorporate the information naturally into your response. Never show raw data or JSON to the user."
- If no such instruction exists, add it as a tool usage instruction appended to the system prompt when tools are assigned.
4. **Verify CAP-04 (HTTP request tool)**: Confirm http_request.py needs no changes — it already works. Just verify it's in the tool registry and functions correctly.
5. **Verify CAP-07 (audit logging)**: Confirm executor.py already calls audit_logger.log_tool_call() on every invocation (it does — verified in code review). No changes needed.
cd /home/adelorenzo/repos/konstruct && python -c "from shared.api.kb import kb_router; from shared.api.calendar_auth import calendar_auth_router; print('Routers import OK')" && python -c "from orchestrator.tools.registry import TOOL_REGISTRY; print(f'Registry has {len(TOOL_REGISTRY)} tools')"
KB and Calendar Auth routers mounted on gateway. Calendar tool registry updated with CRUD parameters. System prompt includes tool result formatting instruction. CAP-04 (HTTP) confirmed working. CAP-07 (audit) confirmed working. All routers importable.
- Calendar OAuth endpoints accessible: GET /api/portal/calendar/install, GET /api/portal/calendar/callback
- KB API endpoints accessible: POST/GET/DELETE /api/portal/kb/{tenant_id}/documents
- Calendar tool supports list, create, check_availability actions
- All unit tests pass: `pytest tests/unit/test_calendar_lookup.py tests/unit/test_calendar_auth.py -x -q`
- Tool registry has updated calendar_lookup schema with CRUD params
<success_criteria>
- Google Calendar OAuth flow: install -> Google consent -> callback -> encrypted token stored in channel_connections
- Calendar tool reads per-tenant tokens and calls Google Calendar API for list, create, and availability check
- Token auto-refresh works with write-back to DB
- Natural language formatting on all tool results (no raw JSON)
- All new routers mounted on gateway
- CAP-04 and CAP-07 confirmed already working
- All unit tests pass </success_criteria>