diff --git a/.agent/rules/02-do-abstraction.md b/.agent/rules/02-do-abstraction.md deleted file mode 100644 index fb142ac4..00000000 --- a/.agent/rules/02-do-abstraction.md +++ /dev/null @@ -1,23 +0,0 @@ -# Rule: Durable Object Abstraction Pattern - -## Context -Our backend architecture standardizes Durable Object interactions into two distinct paradigms: AI Agents and WebSocket Broadcasters. Raw mounting of Durable Objects using `idFromName` and `.get()` causes type ambiguity, routing inconsistencies, and runtime errors. - -## Core Directives - -### 1. Raw DO Instantiation is Forbidden -- **NEVER** use `namespace.idFromName('name')` or `namespace.get(id)` directly within routing files, utility functions, or workflow handlers. -- **NEVER** instantiate raw `Request` objects targeted at `http://agent/...` without a client utility. - -### 2. The Agent Paradigm (HoniClient) -All stateful AI Agents in this system are built using the `honidev` framework. -- **RPC Access**: When you need to interact directly with an agent's internal methods (e.g., `stub.chat()`, `stub.workflowComplete()`), you **MUST** use `HoniClient.getStub()`. -- **HTTP Access**: When you need to send an HTTP request to an agent's internal Hono router, you **MUST** use `HoniClient.fetch()`. -- **Import Path**: `import { HoniClient } from '@utils/honi-client';` - -### 3. The Broadcast Paradigm (BroadcastClient) -WebSocket broadcasters (e.g., `ROOM_DO`, `JulesWebhookBroadcaster`) are stateful but are not AI agents. -- **Usage**: When dispatching broadcasts or checking room presence, use the unified `BroadcastClient` utility to construct the request. - -## Enforcement -This architectural standard replaces legacy utilities such as `getAgentByName` and `routeAgentRequest`, which have been removed from the codebase. Any attempt to reinvent DO mounting wrappers will be blocked during code review. diff --git a/.agent/rules/agent-skills.md b/.agent/rules/agent-skills.md new file mode 100644 index 00000000..1ce017f7 --- /dev/null +++ b/.agent/rules/agent-skills.md @@ -0,0 +1,29 @@ +# Agent Skills Service Standards + +## 1. Skill Storage Model +- Skills MUST NOT be fetched directly from GitHub during active AI inference. +- They must be ingested "out-of-band" into the D1 `agent_skills` table using the `/api/skills/ingest` or `/api/skills/ingest-structured` routes. +- The `SkillManager` service (`@/ai/providers/agent-support/skills.ts`) is the sole interface for skill resolution at inference time. + +## 2. Provider-Level Injection +- Backend Agents must define skills using the `options: { skills: ['skill-name'] }` array passed to `AIProvider` generation methods. +- The `AIProvider` is solely responsible for querying `SkillManager`, which reads from D1 and caches in-memory with TTL. +- Chat agents receive dynamic skills via the `X-Agent-Skills` HTTP header, merged with static agent-defined skills in `resolveSystemPrompt()`. + +## 3. Drizzle ORM Syntax +- Always use the `drizzle-zod` package for schema validation. Do NOT use `drizzle-orm/zod` (v1.0.0+ only). +- Schema definitions for agents live under `@db/schemas/agents/`. +- New relational tables (`agent_skill_allowed_tools`, `agent_skill_references`) use cascading FKs to `agentSkills.id`. + +## 4. Additive API Changes +- When modifying Hono routes in production, favor ADDING new endpoints over destructively modifying existing ones. +- Example: `/ingest-structured` was added alongside the existing `/ingest` to prevent breaking active frontend clients. + +## 5. Graceful Degradation +- Always parse external markdown and YAML frontmatter defensively. +- Do NOT fail a D1 insert if optional fields like `allowed-tools` are missing from SKILL.md. +- Default to an empty array and proceed with the core skill insertion. + +## 6. Environment Variables +- AI generation tasks must exclusively use `GEMINI_API_KEY`. +- Skill ingestion routes use `GITHUB_TOKEN` via the existing `getOctokit()` service. diff --git a/.agent/rules/agent-specialist-delegation.md b/.agent/rules/agent-specialist-delegation.md new file mode 100644 index 00000000..4030ac6a --- /dev/null +++ b/.agent/rules/agent-specialist-delegation.md @@ -0,0 +1,122 @@ +# Agent Specialist Delegation Rule + +> **Enforcement level:** Mandatory — all code under `src/backend/src/ai/agents/` +> **Introduced by:** v7 PRD — `docs/20260417/standardize_agents/v7/PRD.md` +> **Verification:** grep-based CI guard — `scripts/check-agent-delegation.sh` + +--- + +## Rule + +**Specialist agents are the single source of truth for their domain.** No agent outside the specialist's directory may import or directly call the specialist's underlying SDK/service. Instead, agents consume specialist functionality via `@callable` RPC methods accessed through `getPeerAgent(env.FOO_AGENT)`. + +--- + +## Domain Ownership Table + +| Domain | Specialist Agent | Sanctioned Directory | Owned Imports | +|--------|-----------------|---------------------|---------------| +| **Cloudflare Docs / MCP** | `CloudflareAgent` | `chat/CloudflareAgent/` | `@/ai/mcp/*`, `queryMCP`, `rewriteQuestionForMCP` | +| **GitHub / Octokit** | `GithubAgent` | `backend/GithubAgent/` | `@octokit/*`, `@services/octokit/*`, `@/services/octokit/*`, `@/services/github/client` | + +### CoordinatorAgent — Pure Router Contract + +`chat/CoordinatorAgent/` is a **pure router**. It must never import any service SDK or domain client. Allowed imports are limited to: +- `agents` (for `callable`, `getAgentByName`, `StreamingResponse`, `getPeerAgent`) +- `@/ai/providers/agent-support/base-chat-agent` +- Local `./types` + +**Forbidden in CoordinatorAgent:** +- `@octokit/*` +- `@/ai/mcp/*` +- `@/cloudflare/*` +- `@services/*`, `@/services/*` +- Any third-party SDK + +--- + +## How to Consume Specialist APIs + +### Cloudflare Docs (CloudflareAgent) + +```typescript +// ✅ CORRECT — delegate via getPeerAgent +const cloudflareAgent = (agent as any).getPeerAgent((env as any).CLOUDFLARE_AGENT); +const result = await cloudflareAgent.agenticSearch(rawQuestion); +const docs = result?.docsContext ?? null; +``` + +```typescript +// ❌ WRONG — bypasses CloudflareAgent +import { queryMCP } from '@/ai/mcp/mcp-client'; +const result = await queryMCP(env, question, 'MyAgent'); +``` + +```typescript +// ❌ WRONG — double-rewrite (CloudflareAgent.agenticSearch rewrites internally) +const rewritten = await ai.rewriteQuestionForMCP(question); +const result = await cloudflareAgent.agenticSearch(rewritten); +``` + +### GitHub Search (GithubAgent) + +```typescript +// ✅ CORRECT — delegate via getPeerAgent +const githubAgent = (agent as any).getPeerAgent((env as any).GITHUB_AGENT); +const items = await githubAgent.searchRepositories({ query, perPage: 20 }); +const codeResults = await githubAgent.searchCode(query, repoContext); +``` + +```typescript +// ❌ WRONG — bypasses GithubAgent +import { getOctokit } from "@services/octokit/core"; +const octokit = await getOctokit(env); +``` + +--- + +## Sanctioned Exception + +**`src/backend/src/services/planning/babysitter/utils.ts`** calls `queryMCP` directly. This is the **one** sanctioned non-agent call site because the babysitter runs in scheduled-worker context with no Durable Object instance available. The exception is documented inline at the call site. Do NOT copy this pattern into any code path that has access to an agent instance. + +--- + +## Enforcement + +Since no ESLint config exists in this project, enforcement is via: + +1. **This rule document** — LLM-driven PR reviews must check compliance +2. **Verification greps** — run before merging any PR that touches `src/backend/src/ai/agents/`: + +```bash +# Only CloudflareAgent may import MCP client +rg -n "from ['\"]@/ai/mcp/mcp-client" src/backend/src/ai/agents \ + | grep -v "chat/CloudflareAgent" | wc -l # must be 0 + +# Only GithubAgent may import Octokit +rg -n "@octokit|getOctokit|new Octokit" src/backend/src/ai/agents \ + | grep -v "backend/GithubAgent" | wc -l # must be 0 + +# No agent outside CloudflareAgent calls rewriteQuestionForMCP +rg -n "rewriteQuestionForMCP" src/backend/src/ai/agents \ + | grep -v "chat/CloudflareAgent" | wc -l # must be 0 + +# CoordinatorAgent imports no service SDKs +rg -n "from ['\"]@/cloudflare|@/ai/mcp|@octokit|@services" \ + src/backend/src/ai/agents/chat/CoordinatorAgent | wc -l # must be 0 +``` + +3. **CI guard script** — `scripts/check-agent-delegation.sh` (fails the build on violations) + +--- + +## Adding New Specialist Surface Area + +If your agent needs functionality that a specialist owns: + +1. Check if the specialist already exposes a `@callable` method for it +2. If not, **add a new `@callable` method on the specialist first** +3. Consume via `getPeerAgent(env.FOO_AGENT).newMethod(args)` from your agent +4. Wrap in try/catch with graceful degradation (see `GuardrailAgent/methods/cloudflare-docs.ts` for reference) + +**Never import the specialist's internal SDK into your agent.** The specialist boundary exists to ensure single-owner maintenance, consistent caching, and centralized error handling. diff --git a/.agent/rules/ai-routing.md b/.agent/rules/ai-routing.md deleted file mode 100644 index a3cc3f2a..00000000 --- a/.agent/rules/ai-routing.md +++ /dev/null @@ -1,5 +0,0 @@ -# AI Routing & Fallback Rules - -- **Silent Failures:** Never allow a third-party AI provider failure to crash the request if a `worker-ai` equivalent model can handle the prompt. -- **Type Safety:** Do not alter the return types (`string`, `T`) of the core generation functions to include metadata. Always use the `onFallback` callback mechanism in `AIOptions` to bubble up execution state. -- **Observability:** Every fallback event must be aggressively logged to D1 to track provider reliability and API Gateway latency over time. diff --git a/.agent/rules/ai-rules.md b/.agent/rules/ai-rules.md deleted file mode 100644 index ad53c853..00000000 --- a/.agent/rules/ai-rules.md +++ /dev/null @@ -1,14 +0,0 @@ -# Rule: AI Provider & Structured Responses - -## 1. Structured Output Mandate - -- **CRYSTAL CLEAR RULE**: ANYTIME the AI model is being instructed to respond with a structured response (JSON), you **MUST** use `generateStructuredResponse` or `generateStructuredWithTools` exported from `@/ai/providers`. -- **FORBIDDEN**: Do not rely on native Agent SDK schemas (e.g. `outputType: MySchema as any` in `@openai/agents`). These frequently fail to map correctly through the Cloudflare AI Gateway or result in brittle string parsing. - -## 2. The Extraction Pattern (Agents with Tools) - -If you are running an autonomous Agent that requires tool usage (e.g., `HealthDiagnostician` or `ResearchAgent`): - -1. Configure the Agent to output standard text/markdown (`outputType` must NOT be explicitly defined). -2. Await the Agent's `finalOutput` inside the execution loop. -3. Pass that string into `generateStructuredResponse` along with your Zod schema (converted via `zodToJsonSchema`) to strictly extract and type the final JSON object. This ensures Gateway compatibility while guaranteeing Zod-verified JSON. diff --git a/.agent/rules/backend-api.md b/.agent/rules/backend-api.md new file mode 100644 index 00000000..11a66f83 --- /dev/null +++ b/.agent/rules/backend-api.md @@ -0,0 +1,31 @@ +# Backend API & Hono Architecture + +## 1. Primary Protocol & Communication +- **Hono RPC (`hc`)**: Use Hono RPC for all API interactions to maintain full-stack type safety. +- **Real-Time Context**: Use WebSockets (via Durable Objects/Agents SDK) and Server-Sent Events (SSE) for long-running workflows or streaming. Return status updates seamlessly to the frontend without persistent DB polling. +- **Type Definitions**: Never redefine backend types on the frontend. Use the exported `AppType`. + +## 2. Global Env & Bindings Enforcement +- **Requirement**: `Env` is generated by `wrangler types` and globally exposed. Use `Env` freely in function signatures without relative imports (e.g., `import type { Env } ...` is FORBIDDEN). +- **Forbidden Pattern**: `import { Bindings } from '@utils/hono'` is strictly prohibited. `const app = new Hono<{ Bindings: Env }>()`. + +## 3. Pathing & Import Aliases +- **Requirement**: All internal imports MUST use defined path aliases. + - `@/*` -> local `src` + - `@db/*` -> Dizzle data layer + - `@api/*` -> Hono RPC AppType definitions + - `@ui/*` -> Shadcn + - `@shared/*` -> Shared Zod schemas +- **Forbidden Pattern**: Relative paths for cross-library access (`../../../db`). + +## 4. Dual-Scope Routing Paradigm +- **Global Routes (`/api/global/*`)**: System-wide components independent of an installation (health, generic user management, webhooks). +- **Repo-Specific Routes (`/api/repos/:owner/:repo/*`)**: Require repository context. Scopes should be strictly enforced at the middleware layer using `githubApp.octokit.rest.apps.getRepoInstallation`. + +## 5. Agent Real-Time & Networking +- Agents SDK uses WebSocket upgrades mapping to the orchestrator bindings. Do not rely on legacy non-SDK Durable Object instantiations. +- `platformProxy.configPath` within `astro.config.mjs` must explicitly point to the single unified root `wrangler.jsonc` file. + +## 6. Secrets Management +- **Mandate**: ALL backend code MUST retrieve secrets using `getSecret(env, 'SECRET_NAME')` from `@/utils/secrets` instead of directly accessing `env.SECRET_NAME` or `env.SECRET_NAME.get()`. +- **Reasoning**: This provides a unified interface whether the secret is locally bound or coming from the Cloudflare Secrets Store. diff --git a/.agent/rules/cloudflare-standards.md b/.agent/rules/cloudflare-standards.md deleted file mode 100644 index fb95c2d2..00000000 --- a/.agent/rules/cloudflare-standards.md +++ /dev/null @@ -1,6 +0,0 @@ -# Cloudflare Worker Standards (2026) - -- **Routing & Validation**: All APIs must exclusively utilize `Hono` alongside `@hono/zod-openapi` to enforce strict request schemas and automate documentation generation. -- **OpenAPI Enforcement**: Every Worker project must host `/openapi.json` (OpenAPI v3.1.0) and include a mounted `/scalar` and `/swagger` viewer UI. -- **AI Operations**: Standardize on the official `openai` SDK. Connections must be instantiated securely by configuring the `baseURL` to point directly to Cloudflare AI Gateway. -- **Types & Configuration**: Environment typing is maintained strictly through `wrangler.jsonc` (not `wrangler.toml`), and synchronized via `wrangler types`. Manual mapping of types is deprecated. diff --git a/.agent/rules/database.md b/.agent/rules/database.md new file mode 100644 index 00000000..f9acdac2 --- /dev/null +++ b/.agent/rules/database.md @@ -0,0 +1,23 @@ +# Database & D1 Governance + +## 1. Drizzle ORM Mandate & Separation of Concerns +- **Drizzle ORM** is the strict standard for D1 interactions. Raw SQL bindings (`env.DB.prepare().run()`) are FORBIDDEN for application logic. +- **Strict D1 Instance Separation**: + - `DB` (Core App logic, e.g., Agent persistence, sessions). Must be initialized via `getDb(env.DB)`. + - `DB_WEBHOOKS` (Stateless GitHub events, sync history). Must be initialized via `getWebhooksDb(env.DB_WEBHOOKS)`. + - Never cross-contaminate logic or run migrations on the wrong bindings. + +## 2. Schema and Migrations +- Schema definitions must reside in `@db/schema.ts` and `@db/schema-webhooks.ts`. +- Migrations MUST be generated via `drizzle-kit` and run via the deployment scripts (`pnpm run migrate:remote`). DO NOT write manual `.sql` migration files. +- `drizzle.config.ts` manages two separate connections depending on the `env` argument. + +## 3. Batch API Mandate +- When performing loop-based insertions (e.g., skill tool mappings), use `db.insert().values([...])` with arrays, NOT individual inserts inside a loop. +- For cross-table batch operations, use `env.DB.batch()` to group related inserts into a single round-trip. +- Cascading deletes (`onDelete: 'cascade'`) must be used on FK references to prevent orphaned rows. + +## 4. Modular Schema Organization +- Schemas are namespaced by domain under `@db/schemas/{domain}/` (e.g., `agents/`, `logs/`, `github/`). +- Each domain folder has its own `index.ts` barrel export. +- `schema.core.ts` explicitly controls which schemas participate in `drizzle.config.core.ts` migrations (excludes DB_WEBHOOKS-owned tables). diff --git a/.agent/rules/durable-object-agents.md b/.agent/rules/durable-object-agents.md deleted file mode 100644 index 99c44d69..00000000 --- a/.agent/rules/durable-object-agents.md +++ /dev/null @@ -1,7 +0,0 @@ -# .agent/rules/durable-object-agents.md - -## Rules - -- **Dynamic Heavy SDKs:** Never statically import large orchestration libraries (like `@openai/agents` or `langchain`) at the top level of a Cloudflare Worker or Durable Object. Always use dynamic `import()` inside the execution method to preserve sub-50ms cold starts. -- **Client Injection over Globals:** When executing an agent run via the OpenAI Agents SDK, ALWAYS inject the `client` explicitly into the `run()` options (e.g., `run(agent, prompt, { client })`). Never rely on global environment variables to implicitly configure the client. -- **Prefix Namespacing:** Always format the model identifier as `${provider}/${model}` immediately prior to passing it into the `OpenAIAgent` constructor to ensure Cloudflare AI Gateway routes it correctly. diff --git a/.agent/rules/durable_objects.md b/.agent/rules/durable_objects.md deleted file mode 100644 index 02b649b3..00000000 --- a/.agent/rules/durable_objects.md +++ /dev/null @@ -1,68 +0,0 @@ -# Rule: Durable Objects with SQLite State - -NEVER use `new_classes` for SQLite-backed Durable Objects. -ALWAYS use `new_sqlite_classes` in the migrations array. - -**Wrong:** -```jsonc -"migrations": [{ "tag": "v1", "new_classes": ["MyAgent"] }] -``` - -**Correct:** -```jsonc -"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }] -``` - -**Why:** `new_classes` does not initialize the SQLite storage layer. -Any class extending `Agent` from `@cloudflare/agents` REQUIRES `new_sqlite_classes`. -Violation causes runtime errors: "SQLite storage not available." - ---- - -# Rule: Durable Object & Agent Migration Strategy - -## 1. Definition - -- **Requirement**: All new Agents and Durable Objects must be explicitly defined in `durable_objects.bindings`. -- **Constraint**: ALL new classes must be registered in the `migrations` block of `wrangler.jsonc`. - -## 2. Deployment Lifecycle Rules - -### A. Fresh Deployment (Never Deployed) - -- If the worker has **never** been deployed to production, you MAY add new classes to `migrations.v1`. - -### B. Standard Deployment (Already Live) - -- If the worker **has** been deployed, you **MUST** create a new migration version (e.g., `v2` -> `v3`). -- **Forbidden**: Do NOT add new classes to existing/previous migration tags (e.g., do not retroactively add to `v1`). - -## 3. Configuration Format - -- **Field**: Always use `new_sqlite_classes` for Agents/DOs. -- **Documentation**: Use a docstring comment to explain the purpose of the new class. - -### Example - -```jsonc -"migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["ExistingAgent"] - }, - { - "tag": "v2", - "new_sqlite_classes": [ - // Specialized agent for handling X - "NewSpecializedAgent" - ] - } -] -``` - -## 4. Checklist - -1. [ ] Defined in `durable_objects.bindings`? -2. [ ] Added to `migrations`? -3. [ ] Is the migration tag incremented (if live)? -4. [ ] Is the class name docstringed? diff --git a/.agent/rules/frontend.md b/.agent/rules/frontend.md new file mode 100644 index 00000000..06fe7120 --- /dev/null +++ b/.agent/rules/frontend.md @@ -0,0 +1,17 @@ +# Frontend & UI Architecture Status + +## 1. Moody Modern Architecture & Shadcn +- **Framework**: Astro (latest) + `@astrojs/react`. +- **Styling**: Tailwind CSS v4 (OKLCH). Default Dark Theme is mandatory (``). No light mode toggles unless requested. +- **UI Components**: Shadcn UI (Official) and Shadcn-compatible registries. Replace all raw HTML component mockups with Shadcn equivalents. + +## 2. Astro Islands & Hydration +- **Hydration Rules**: All interactive React components must be wrapped as Astro islands (`client:load` or `client:visible`). +- **Routing**: Every page must have a dedicated `.astro` file in `src/pages/` for SSR. Unified monolithic Worker `wrangler.jsonc` platform proxy configuration is mandatory. + +## 3. Responsive Design & Layouts +- **Mobile First**: Wrap content in `