From 31b1469e39502a9e1814f7bc9419dbcad9083061 Mon Sep 17 00:00:00 2001 From: mzaxd Date: Sun, 12 Apr 2026 12:09:55 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8F=B0=E6=A8=A1=E5=9D=97=E5=8F=8A=20Agent=20OS=20?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 - docs/agent-os-runtime.md | 51 -- e2e/agent-os-live.spec.ts | 137 ---- src/app/[locale]/admin/chat/page.tsx | 5 - src/app/[locale]/admin/layout.tsx | 44 -- src/app/[locale]/admin/page.tsx | 45 -- .../admin/policies/proposals/page.tsx | 23 - .../[locale]/admin/resources/agents/page.tsx | 14 - src/app/[locale]/admin/resources/mcp/page.tsx | 14 - src/app/[locale]/admin/resources/page.tsx | 37 - .../[locale]/admin/resources/skills/page.tsx | 14 - .../[locale]/admin/resources/tools/page.tsx | 14 - src/app/[locale]/admin/runs/[id]/page.tsx | 19 - src/app/[locale]/admin/runs/page.tsx | 30 - src/app/[locale]/layout.tsx | 10 - src/app/api/admin/_lib/auth.ts | 48 -- .../admin/approvals/[id]/decision/route.ts | 64 -- src/app/api/admin/approvals/route.ts | 40 -- src/app/api/admin/auth/token/route.ts | 52 -- src/app/api/admin/policies/proposals/route.ts | 60 -- src/app/api/admin/resources/agents/route.ts | 16 - src/app/api/admin/resources/mcp/route.ts | 18 - src/app/api/admin/resources/route.ts | 33 - src/app/api/admin/resources/skills/route.ts | 22 - src/app/api/admin/resources/tools/route.ts | 20 - src/app/api/admin/runs/route.ts | 16 - src/app/api/agent/goals/route.ts | 56 -- src/app/api/agent/runs/[id]/control/route.ts | 71 -- src/app/api/agent/runs/[id]/route.ts | 34 - src/components/admin/AdminGoalConsole.tsx | 91 --- src/components/admin/AdminJsonPanel.tsx | 104 --- .../admin/AdminPolicyProposalForm.tsx | 148 ---- src/lib/agent-os/admin-auth.ts | 90 --- src/lib/agent-os/model-client.ts | 212 ------ src/lib/agent-os/n8n-production-workflows.ts | 111 --- src/lib/agent-os/orchestrator.test.ts | 111 --- src/lib/agent-os/orchestrator.ts | 671 ------------------ src/lib/agent-os/policy-engine.test.ts | 51 -- src/lib/agent-os/policy-engine.ts | 167 ----- src/lib/agent-os/policy-runtime.test.ts | 36 - src/lib/agent-os/policy-runtime.ts | 110 --- src/lib/agent-os/resource-pool.ts | 646 ----------------- src/lib/agent-os/schemas.ts | 35 - src/lib/agent-os/store.ts | 331 --------- src/lib/agent-os/types.ts | 191 ----- src/messages/en.json | 72 +- src/messages/zh.json | 72 +- 47 files changed, 2 insertions(+), 4265 deletions(-) delete mode 100644 docs/agent-os-runtime.md delete mode 100644 e2e/agent-os-live.spec.ts delete mode 100644 src/app/[locale]/admin/chat/page.tsx delete mode 100644 src/app/[locale]/admin/layout.tsx delete mode 100644 src/app/[locale]/admin/page.tsx delete mode 100644 src/app/[locale]/admin/policies/proposals/page.tsx delete mode 100644 src/app/[locale]/admin/resources/agents/page.tsx delete mode 100644 src/app/[locale]/admin/resources/mcp/page.tsx delete mode 100644 src/app/[locale]/admin/resources/page.tsx delete mode 100644 src/app/[locale]/admin/resources/skills/page.tsx delete mode 100644 src/app/[locale]/admin/resources/tools/page.tsx delete mode 100644 src/app/[locale]/admin/runs/[id]/page.tsx delete mode 100644 src/app/[locale]/admin/runs/page.tsx delete mode 100644 src/app/api/admin/_lib/auth.ts delete mode 100644 src/app/api/admin/approvals/[id]/decision/route.ts delete mode 100644 src/app/api/admin/approvals/route.ts delete mode 100644 src/app/api/admin/auth/token/route.ts delete mode 100644 src/app/api/admin/policies/proposals/route.ts delete mode 100644 src/app/api/admin/resources/agents/route.ts delete mode 100644 src/app/api/admin/resources/mcp/route.ts delete mode 100644 src/app/api/admin/resources/route.ts delete mode 100644 src/app/api/admin/resources/skills/route.ts delete mode 100644 src/app/api/admin/resources/tools/route.ts delete mode 100644 src/app/api/admin/runs/route.ts delete mode 100644 src/app/api/agent/goals/route.ts delete mode 100644 src/app/api/agent/runs/[id]/control/route.ts delete mode 100644 src/app/api/agent/runs/[id]/route.ts delete mode 100644 src/components/admin/AdminGoalConsole.tsx delete mode 100644 src/components/admin/AdminJsonPanel.tsx delete mode 100644 src/components/admin/AdminPolicyProposalForm.tsx delete mode 100644 src/lib/agent-os/admin-auth.ts delete mode 100644 src/lib/agent-os/model-client.ts delete mode 100644 src/lib/agent-os/n8n-production-workflows.ts delete mode 100644 src/lib/agent-os/orchestrator.test.ts delete mode 100644 src/lib/agent-os/orchestrator.ts delete mode 100644 src/lib/agent-os/policy-engine.test.ts delete mode 100644 src/lib/agent-os/policy-engine.ts delete mode 100644 src/lib/agent-os/policy-runtime.test.ts delete mode 100644 src/lib/agent-os/policy-runtime.ts delete mode 100644 src/lib/agent-os/resource-pool.ts delete mode 100644 src/lib/agent-os/schemas.ts delete mode 100644 src/lib/agent-os/store.ts delete mode 100644 src/lib/agent-os/types.ts diff --git a/.env.example b/.env.example index 8d41947..058e52b 100644 --- a/.env.example +++ b/.env.example @@ -4,17 +4,6 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/agent_park" # Webhook API - Generate a secure key for production WEBHOOK_API_KEY="sk_live_your_secure_api_key_min_32_chars" -# Admin Console key for Agent OS endpoints and dashboard -ADMIN_CONSOLE_KEY="sk_admin_your_secure_admin_key_min_32_chars" - -# Agent OS model runtime (OpenAI-compatible) -# BigModel GLM example endpoint: -# https://open.bigmodel.cn/api/coding/paas/v4/chat/completions -AGENT_OS_MODEL_ENDPOINT="https://open.bigmodel.cn/api/coding/paas/v4/chat/completions" -AGENT_OS_MODEL_NAME="glm-4.7" -AGENT_OS_MODEL_API_KEY="sk_your_model_api_key" -AGENT_OS_MODEL_TIMEOUT_MS="30000" - # n8n AI Search Webhook N8N_AI_SEARCH_WEBHOOK="https://n8n.mzaxd.fun/webhook/ai-search" diff --git a/docs/agent-os-runtime.md b/docs/agent-os-runtime.md deleted file mode 100644 index 7e094e4..0000000 --- a/docs/agent-os-runtime.md +++ /dev/null @@ -1,51 +0,0 @@ -# Agent OS Runtime Notes - -## Live Model Configuration - -Set these environment variables before running the Agent OS goal APIs: - -- `AGENT_OS_MODEL_ENDPOINT` (OpenAI-compatible chat completions endpoint) -- `AGENT_OS_MODEL_NAME` (default: `glm-4.7`) -- `AGENT_OS_MODEL_API_KEY` -- `AGENT_OS_MODEL_TIMEOUT_MS` (optional, default `30000`) - -`/api/agent/goals` uses model planning first. If model config is missing or call fails, it falls back to heuristic planning and annotates `run.planning.warning`. - -## n8n Production Workflow Mapping - -`/api/admin/resources/skills` exposes `n8nProductionWorkflows` and corresponding n8n-backed skill manifests: - -- Daily Trending discovery -- Topic discovery -- Multi-source signal aggregation -- Project description embedding refresh -- RAG similarity retrieval -- GitHub stars refresh -- Multi-source project ingest - -## Runtime Policy Patch Activation - -Policy proposals approved through: - -- `POST /api/admin/policies/proposals` -- `POST /api/admin/approvals/:id/decision` - -now immediately apply runtime patches for `targetType=agent` (policy overrides, command rules, path rules, enabled resource tags). This ensures approval changes affect subsequent runs. - -## Live E2E - -Playwright suite: - -- `e2e/agent-os-live.spec.ts` - -Required env: - -- `E2E_ADMIN_KEY` (or `ADMIN_CONSOLE_KEY`) -- one of `AGENT_OS_MODEL_API_KEY` / `BIGMODEL_API_KEY` / `ZHIPU_API_KEY` - -Run: - -```bash -pnpm test:e2e -``` - diff --git a/e2e/agent-os-live.spec.ts b/e2e/agent-os-live.spec.ts deleted file mode 100644 index 1e5d0a6..0000000 --- a/e2e/agent-os-live.spec.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { expect, test } from '@playwright/test' - -function resolveAdminKey(): string | undefined { - return process.env.E2E_ADMIN_KEY || process.env.ADMIN_CONSOLE_KEY -} - -function hasLiveModelKey(): boolean { - return Boolean( - process.env.AGENT_OS_MODEL_API_KEY || process.env.BIGMODEL_API_KEY || process.env.ZHIPU_API_KEY - ) -} - -test.describe('Agent OS Live E2E', () => { - test.skip(!resolveAdminKey(), 'E2E_ADMIN_KEY or ADMIN_CONSOLE_KEY is required') - test.skip(!hasLiveModelKey(), 'Live model API key is required for this suite') - - test('runs model planning and policy approval loop with audit records', async ({ - request, - page, - }) => { - const adminKey = resolveAdminKey()! - - const tokenResponse = await request.post('/api/admin/auth/token', { - data: { - adminKey, - operator: 'playwright-e2e', - }, - }) - expect(tokenResponse.ok()).toBeTruthy() - const tokenPayload = await tokenResponse.json() - expect(tokenPayload.success).toBe(true) - const bearerToken = tokenPayload.token as string - - const headers = { - Authorization: `Bearer ${bearerToken}`, - 'Content-Type': 'application/json', - } - - const firstRunResponse = await request.post('/api/agent/goals', { - headers, - data: { - goal: 'Please discover AI agent projects from trending signals and summarize with RAG retrieval.', - preferredAgentId: 'agent.discovery.scout', - }, - }) - expect(firstRunResponse.ok()).toBeTruthy() - const firstRun = await firstRunResponse.json() - - expect(firstRun.success).toBe(true) - expect(firstRun.planning?.source).toBe('model') - expect(Array.isArray(firstRun.tasks)).toBe(true) - expect(firstRun.tasks.length).toBeGreaterThan(0) - expect(Array.isArray(firstRun.toolCalls)).toBe(true) - expect(firstRun.toolCalls.length).toBeGreaterThan(0) - - const runId = firstRun.run?.id as string - expect(typeof runId).toBe('string') - - const runDetailResponse = await request.get(`/api/agent/runs/${runId}`, { headers }) - expect(runDetailResponse.ok()).toBeTruthy() - const runDetail = await runDetailResponse.json() - expect(runDetail.success).toBe(true) - expect(runDetail.run?.id).toBe(runId) - expect(runDetail.tasks.length).toBeGreaterThan(0) - - const proposalResponse = await request.post('/api/admin/policies/proposals', { - headers, - data: { - title: 'Deny MCP browser for discovery scout', - description: 'Policy proposal created by live e2e to validate approval effectiveness.', - targetType: 'agent', - targetId: 'agent.discovery.scout', - changes: { - policyOverrides: { - 'mcp.agent_browser.read': 'deny', - }, - }, - }, - }) - expect(proposalResponse.ok()).toBeTruthy() - const proposalPayload = await proposalResponse.json() - expect(proposalPayload.success).toBe(true) - const approvalId = proposalPayload.approvalTicket?.id as string - expect(typeof approvalId).toBe('string') - - const approveResponse = await request.post(`/api/admin/approvals/${approvalId}/decision`, { - headers, - data: { - decision: 'approved', - comment: 'approved by playwright e2e', - }, - }) - expect(approveResponse.ok()).toBeTruthy() - const approvePayload = await approveResponse.json() - expect(approvePayload.success).toBe(true) - expect(approvePayload.approvalTicket?.status).toBe('approved') - - const secondRunResponse = await request.post('/api/agent/goals', { - headers, - data: { - goal: 'Use web_read capability to read website https://example.com and extract key points.', - preferredAgentId: 'agent.discovery.scout', - }, - }) - expect(secondRunResponse.ok()).toBeTruthy() - const secondRun = await secondRunResponse.json() - expect(secondRun.success).toBe(true) - - const webReadCalls = secondRun.toolCalls.filter( - (call: { resourceId: string }) => - call.resourceId === 'mcp.agent_browser.read' || - call.resourceId === 'tool.web.read' || - call.resourceId === 'tool.web.search' - ) - expect(webReadCalls.length).toBeGreaterThan(0) - expect(webReadCalls.some((call: { resourceId: string }) => call.resourceId === 'tool.web.read')).toBe( - true - ) - - const allReasons: string[] = secondRun.toolCalls.flatMap((call: { reasons?: string[] }) => - Array.isArray(call.reasons) ? call.reasons : [] - ) - expect(allReasons.some((reason) => reason.startsWith('fallback_low_trust:'))).toBe(true) - - const secondRunId = secondRun.run?.id as string - const logResponse = await request.get(`/api/admin/logs/tool-calls?runId=${secondRunId}`, { - headers, - }) - expect(logResponse.ok()).toBeTruthy() - const logPayload = await logResponse.json() - expect(logPayload.success).toBe(true) - expect(logPayload.toolCalls.length).toBeGreaterThan(0) - - await page.goto('/en/admin/chat') - await expect(page.getByText('Agent Gateway Chat Console')).toBeVisible() - }) -}) diff --git a/src/app/[locale]/admin/chat/page.tsx b/src/app/[locale]/admin/chat/page.tsx deleted file mode 100644 index 623006b..0000000 --- a/src/app/[locale]/admin/chat/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { AdminGoalConsole } from '@/components/admin/AdminGoalConsole' - -export default function AdminChatPage() { - return -} diff --git a/src/app/[locale]/admin/layout.tsx b/src/app/[locale]/admin/layout.tsx deleted file mode 100644 index 704543c..0000000 --- a/src/app/[locale]/admin/layout.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import Link from 'next/link' -import { getTranslations } from 'next-intl/server' - -export default async function AdminLayout({ - children, - params, -}: { - children: React.ReactNode - params: Promise<{ locale: string }> -}) { - const { locale } = await params - const t = await getTranslations('admin') - - const navItems = [ - { href: `/${locale}/admin/chat`, label: t('navChat') }, - { href: `/${locale}/admin/resources`, label: t('navResources') }, - { href: `/${locale}/admin/policies/proposals`, label: t('navPolicies') }, - { href: `/${locale}/admin/runs`, label: t('navRuns') }, - { href: `/${locale}/admin/logs`, label: t('navLogs') }, - ] - - return ( -
-
-

{t('title')}

-

{t('subtitle')}

-
- - - - {children} -
- ) -} diff --git a/src/app/[locale]/admin/page.tsx b/src/app/[locale]/admin/page.tsx deleted file mode 100644 index d81505c..0000000 --- a/src/app/[locale]/admin/page.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import Link from 'next/link' -import { getTranslations } from 'next-intl/server' - -export default async function AdminHomePage({ - params, -}: { - params: Promise<{ locale: string }> -}) { - const { locale } = await params - const t = await getTranslations('admin') - - const cards = [ - { - href: `/${locale}/admin/chat`, - title: t('cardChatTitle'), - description: t('cardChatDescription'), - }, - { - href: `/${locale}/admin/resources`, - title: t('cardResourceTitle'), - description: t('cardResourceDescription'), - }, - { - href: `/${locale}/admin/policies/proposals`, - title: t('cardPolicyTitle'), - description: t('cardPolicyDescription'), - }, - { - href: `/${locale}/admin/logs`, - title: t('cardLogsTitle'), - description: t('cardLogsDescription'), - }, - ] - - return ( -
- {cards.map((card) => ( - -

{card.title}

-

{card.description}

- - ))} -
- ) -} diff --git a/src/app/[locale]/admin/policies/proposals/page.tsx b/src/app/[locale]/admin/policies/proposals/page.tsx deleted file mode 100644 index b547350..0000000 --- a/src/app/[locale]/admin/policies/proposals/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { AdminPolicyProposalForm } from '@/components/admin/AdminPolicyProposalForm' -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminPolicyProposalsPage() { - const t = await getTranslations('admin') - - return ( -
- - - -
- ) -} diff --git a/src/app/[locale]/admin/resources/agents/page.tsx b/src/app/[locale]/admin/resources/agents/page.tsx deleted file mode 100644 index e443242..0000000 --- a/src/app/[locale]/admin/resources/agents/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminAgentsPage() { - const t = await getTranslations('admin') - - return ( - - ) -} diff --git a/src/app/[locale]/admin/resources/mcp/page.tsx b/src/app/[locale]/admin/resources/mcp/page.tsx deleted file mode 100644 index aad1544..0000000 --- a/src/app/[locale]/admin/resources/mcp/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminMcpPage() { - const t = await getTranslations('admin') - - return ( - - ) -} diff --git a/src/app/[locale]/admin/resources/page.tsx b/src/app/[locale]/admin/resources/page.tsx deleted file mode 100644 index 8a62ff3..0000000 --- a/src/app/[locale]/admin/resources/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from 'next/link' -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminResourcesPage({ - params, -}: { - params: Promise<{ locale: string }> -}) { - const { locale } = await params - const t = await getTranslations('admin') - - const links = [ - { href: `/${locale}/admin/resources/tools`, label: t('resourcesTabTools') }, - { href: `/${locale}/admin/resources/mcp`, label: 'MCP' }, - { href: `/${locale}/admin/resources/skills`, label: t('resourcesTabSkills') }, - { href: `/${locale}/admin/resources/agents`, label: t('resourcesTabAgents') }, - ] - - return ( -
-
- {links.map((link) => ( - - {link.label} - - ))} -
- - -
- ) -} diff --git a/src/app/[locale]/admin/resources/skills/page.tsx b/src/app/[locale]/admin/resources/skills/page.tsx deleted file mode 100644 index 25b0536..0000000 --- a/src/app/[locale]/admin/resources/skills/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminSkillsPage() { - const t = await getTranslations('admin') - - return ( - - ) -} diff --git a/src/app/[locale]/admin/resources/tools/page.tsx b/src/app/[locale]/admin/resources/tools/page.tsx deleted file mode 100644 index 07f4f72..0000000 --- a/src/app/[locale]/admin/resources/tools/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminToolsPage() { - const t = await getTranslations('admin') - - return ( - - ) -} diff --git a/src/app/[locale]/admin/runs/[id]/page.tsx b/src/app/[locale]/admin/runs/[id]/page.tsx deleted file mode 100644 index a142d6b..0000000 --- a/src/app/[locale]/admin/runs/[id]/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminRunDetailPage({ - params, -}: { - params: Promise<{ locale: string; id: string }> -}) { - const { id } = await params - const t = await getTranslations('admin') - - return ( - - ) -} diff --git a/src/app/[locale]/admin/runs/page.tsx b/src/app/[locale]/admin/runs/page.tsx deleted file mode 100644 index 616c49d..0000000 --- a/src/app/[locale]/admin/runs/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { AdminJsonPanel } from '@/components/admin/AdminJsonPanel' -import { getTranslations } from 'next-intl/server' - -export default async function AdminRunsPage() { - const t = await getTranslations('admin') - - return ( -
- - -
- ) -} diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index ad8668e..6359fb6 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -45,8 +45,6 @@ export default async function LocaleLayout({ const tNav = await getTranslations('navigation') const tHome = await getTranslations('home') const messages = await getMessages() - const showAdminTab = process.env.NODE_ENV === 'development' - return (
@@ -96,14 +94,6 @@ export default async function LocaleLayout({ > {tNav('about')} - {showAdminTab ? ( - - {tNav('admin')} - - ) : null} {/* Right side */} diff --git a/src/app/api/admin/_lib/auth.ts b/src/app/api/admin/_lib/auth.ts deleted file mode 100644 index afbbfae..0000000 --- a/src/app/api/admin/_lib/auth.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { NextResponse } from 'next/server' -import { resolveAdminOperator } from '@/lib/agent-os/admin-auth' - -export function parseBearerToken(authHeader: string | null): string | null { - if (!authHeader) { - return null - } - - const [type, token] = authHeader.split(' ') - if (!type || !token) { - return null - } - - return type.toLowerCase() === 'bearer' ? token : null -} - -export function requireAdminAccess(request: Request): - | { authorized: true; operator: string } - | { authorized: false; response: NextResponse } { - const bearerToken = parseBearerToken(request.headers.get('authorization')) - const adminKey = request.headers.get('x-admin-key') - const operatorHint = request.headers.get('x-operator') || request.headers.get('x-client-id') - - const resolved = resolveAdminOperator({ - bearerToken, - adminKey, - fallbackOperator: operatorHint, - }) - - if (!resolved.authorized || !resolved.operator) { - return { - authorized: false, - response: NextResponse.json( - { - success: false, - error: 'Unauthorized', - details: ['Missing or invalid admin credentials'], - }, - { status: 401 } - ), - } - } - - return { - authorized: true, - operator: resolved.operator, - } -} diff --git a/src/app/api/admin/approvals/[id]/decision/route.ts b/src/app/api/admin/approvals/[id]/decision/route.ts deleted file mode 100644 index f41a023..0000000 --- a/src/app/api/admin/approvals/[id]/decision/route.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { NextResponse } from 'next/server' -import { ApprovalDecisionRequestSchema } from '@/lib/agent-os/schemas' -import { decideApproval } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../../../_lib/auth' - -export async function POST( - request: Request, - { params }: { params: Promise<{ id: string }> } -) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const { id } = await params - - try { - const body = await request.json() - const validation = ApprovalDecisionRequestSchema.safeParse(body) - - if (!validation.success) { - return NextResponse.json( - { - success: false, - error: 'Validation error', - details: validation.error.errors, - }, - { status: 400 } - ) - } - - const updated = decideApproval({ - approvalId: id, - decision: validation.data.decision, - decidedBy: auth.operator, - comment: validation.data.comment, - }) - - if (!updated) { - return NextResponse.json( - { - success: false, - error: 'Approval ticket not found', - }, - { status: 404 } - ) - } - - return NextResponse.json({ - success: true, - approvalTicket: updated.approvalTicket, - proposal: updated.proposal, - }) - } catch (error) { - console.error('[AdminApprovals] Failed to decide approval:', error) - return NextResponse.json( - { - success: false, - error: 'Internal server error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/admin/approvals/route.ts b/src/app/api/admin/approvals/route.ts deleted file mode 100644 index adbe1f5..0000000 --- a/src/app/api/admin/approvals/route.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NextResponse } from 'next/server' -import { listApprovalTickets, listPolicyProposals } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const proposals = listPolicyProposals() - const proposalById = new Map(proposals.map((proposal) => [proposal.id, proposal])) - const approvals = listApprovalTickets().map((ticket) => { - const proposal = proposalById.get(ticket.proposalId) - return { - approvalTicketId: ticket.id, - proposalId: ticket.proposalId, - approvalStatus: ticket.status, - requestedBy: ticket.requestedBy, - decidedBy: ticket.decidedBy || null, - decisionComment: ticket.decisionComment || null, - createdAt: ticket.createdAt, - updatedAt: ticket.updatedAt, - proposal: proposal - ? { - title: proposal.title, - status: proposal.status, - targetType: proposal.targetType, - targetId: proposal.targetId, - } - : null, - } - }) - - return NextResponse.json({ - success: true, - operator: auth.operator, - approvals, - }) -} diff --git a/src/app/api/admin/auth/token/route.ts b/src/app/api/admin/auth/token/route.ts deleted file mode 100644 index bd7f5ae..0000000 --- a/src/app/api/admin/auth/token/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { NextResponse } from 'next/server' -import { AdminAuthTokenRequestSchema } from '@/lib/agent-os/schemas' -import { issueAdminToken, isValidAdminKey } from '@/lib/agent-os/admin-auth' - -export async function POST(request: Request) { - try { - const body = await request.json() - const validation = AdminAuthTokenRequestSchema.safeParse(body) - - if (!validation.success) { - return NextResponse.json( - { - success: false, - error: 'Validation error', - details: validation.error.errors, - }, - { status: 400 } - ) - } - - const { adminKey, operator } = validation.data - - if (!isValidAdminKey(adminKey)) { - return NextResponse.json( - { - success: false, - error: 'Unauthorized', - details: ['Invalid admin key'], - }, - { status: 401 } - ) - } - - const token = issueAdminToken(operator || 'admin-console') - - return NextResponse.json({ - success: true, - token: token.token, - operator: token.operator, - expiresAt: new Date(token.expiresAt).toISOString(), - }) - } catch (error) { - console.error('[AdminAuth] Failed to issue token:', error) - return NextResponse.json( - { - success: false, - error: 'Internal server error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/admin/policies/proposals/route.ts b/src/app/api/admin/policies/proposals/route.ts deleted file mode 100644 index ff6fa63..0000000 --- a/src/app/api/admin/policies/proposals/route.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { NextResponse } from 'next/server' -import { CreatePolicyProposalRequestSchema } from '@/lib/agent-os/schemas' -import { createPolicyProposal, listPolicyProposals } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - return NextResponse.json({ - success: true, - operator: auth.operator, - proposals: listPolicyProposals(), - }) -} - -export async function POST(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - try { - const body = await request.json() - const validation = CreatePolicyProposalRequestSchema.safeParse(body) - - if (!validation.success) { - return NextResponse.json( - { - success: false, - error: 'Validation error', - details: validation.error.errors, - }, - { status: 400 } - ) - } - - const created = createPolicyProposal({ - ...validation.data, - createdBy: auth.operator, - }) - - return NextResponse.json({ - success: true, - proposal: created.proposal, - approvalTicket: created.approvalTicket, - }) - } catch (error) { - console.error('[AdminPolicies] Failed to create proposal:', error) - return NextResponse.json( - { - success: false, - error: 'Internal server error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/admin/resources/agents/route.ts b/src/app/api/admin/resources/agents/route.ts deleted file mode 100644 index 98be034..0000000 --- a/src/app/api/admin/resources/agents/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextResponse } from 'next/server' -import { listAgentProfiles } from '@/lib/agent-os/resource-pool' -import { requireAdminAccess } from '../../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - return NextResponse.json({ - success: true, - operator: auth.operator, - agents: listAgentProfiles(), - }) -} diff --git a/src/app/api/admin/resources/mcp/route.ts b/src/app/api/admin/resources/mcp/route.ts deleted file mode 100644 index d3c486c..0000000 --- a/src/app/api/admin/resources/mcp/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NextResponse } from 'next/server' -import { listResourceManifests } from '@/lib/agent-os/resource-pool' -import { requireAdminAccess } from '../../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const mcpResources = listResourceManifests().filter((resource) => resource.kind === 'mcp_tool') - - return NextResponse.json({ - success: true, - operator: auth.operator, - mcpResources, - }) -} diff --git a/src/app/api/admin/resources/route.ts b/src/app/api/admin/resources/route.ts deleted file mode 100644 index 1b828c3..0000000 --- a/src/app/api/admin/resources/route.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NextResponse } from 'next/server' -import { - listAgentProfiles, - listN8nProductionWorkflows, - listPolicyProfiles, - listResourceManifests, -} from '@/lib/agent-os/resource-pool' -import { requireAdminAccess } from '../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const resources = listResourceManifests() - const tools = resources.filter((item) => item.kind === 'base_tool' || item.kind === 'mcp_tool') - - return NextResponse.json({ - success: true, - operator: auth.operator, - summary: { - totalResources: resources.length, - totalTools: tools.length, - totalSkills: resources.filter((item) => item.kind === 'skill').length, - totalAgents: listAgentProfiles().length, - totalPolicyProfiles: listPolicyProfiles().length, - totalN8nProductionWorkflows: listN8nProductionWorkflows().length, - }, - resources, - n8nProductionWorkflows: listN8nProductionWorkflows(), - }) -} diff --git a/src/app/api/admin/resources/skills/route.ts b/src/app/api/admin/resources/skills/route.ts deleted file mode 100644 index 0a2df62..0000000 --- a/src/app/api/admin/resources/skills/route.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextResponse } from 'next/server' -import { - listN8nProductionWorkflows, - listResourceManifests, -} from '@/lib/agent-os/resource-pool' -import { requireAdminAccess } from '../../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const skills = listResourceManifests().filter((resource) => resource.kind === 'skill') - - return NextResponse.json({ - success: true, - operator: auth.operator, - skills, - n8nProductionWorkflows: listN8nProductionWorkflows(), - }) -} diff --git a/src/app/api/admin/resources/tools/route.ts b/src/app/api/admin/resources/tools/route.ts deleted file mode 100644 index 7e17d50..0000000 --- a/src/app/api/admin/resources/tools/route.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextResponse } from 'next/server' -import { listResourceManifests } from '@/lib/agent-os/resource-pool' -import { requireAdminAccess } from '../../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const tools = listResourceManifests().filter( - (resource) => resource.kind === 'base_tool' || resource.kind === 'mcp_tool' - ) - - return NextResponse.json({ - success: true, - operator: auth.operator, - tools, - }) -} diff --git a/src/app/api/admin/runs/route.ts b/src/app/api/admin/runs/route.ts deleted file mode 100644 index d272c91..0000000 --- a/src/app/api/admin/runs/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextResponse } from 'next/server' -import { listRuns } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../_lib/auth' - -export async function GET(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - return NextResponse.json({ - success: true, - operator: auth.operator, - runs: listRuns(), - }) -} diff --git a/src/app/api/agent/goals/route.ts b/src/app/api/agent/goals/route.ts deleted file mode 100644 index e8abd07..0000000 --- a/src/app/api/agent/goals/route.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { NextResponse } from 'next/server' -import { AgentGoalRequestSchema } from '@/lib/agent-os/schemas' -import { orchestrateGoal } from '@/lib/agent-os/orchestrator' -import { getRun, listTasks, listToolCalls } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../../admin/_lib/auth' - -export async function POST(request: Request) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - try { - const body = await request.json() - const validation = AgentGoalRequestSchema.safeParse(body) - - if (!validation.success) { - return NextResponse.json( - { - success: false, - error: 'Validation error', - details: validation.error.errors, - }, - { status: 400 } - ) - } - - const orchestration = await orchestrateGoal({ - ...validation.data, - createdBy: auth.operator, - }) - - const run = getRun(orchestration.runId) - - return NextResponse.json({ - success: true, - run, - intents: orchestration.intents, - selectedAgent: orchestration.agent, - tasks: listTasks(orchestration.runId), - toolCalls: listToolCalls(orchestration.runId), - pendingApprovals: orchestration.pendingApprovals, - planning: orchestration.planning, - approvalTicketIds: orchestration.approvalTicketIds, - }) - } catch (error) { - console.error('[AgentGoals] Failed to orchestrate goal:', error) - return NextResponse.json( - { - success: false, - error: 'Internal server error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/agent/runs/[id]/control/route.ts b/src/app/api/agent/runs/[id]/control/route.ts deleted file mode 100644 index f5a0e3c..0000000 --- a/src/app/api/agent/runs/[id]/control/route.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { NextResponse } from 'next/server' -import { RunControlRequestSchema } from '@/lib/agent-os/schemas' -import { getRun, updateRunStatus } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../../../../admin/_lib/auth' - -const ACTION_TO_STATUS = { - pause: 'PAUSED', - resume: 'TASK_DISPATCHED', - rollback: 'ROLLED_BACK', - fail: 'FAILED', - complete: 'COMPLETED', -} as const - -export async function POST( - request: Request, - { params }: { params: Promise<{ id: string }> } -) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const { id } = await params - - if (!getRun(id)) { - return NextResponse.json( - { - success: false, - error: 'Run not found', - }, - { status: 404 } - ) - } - - try { - const body = await request.json() - const validation = RunControlRequestSchema.safeParse(body) - - if (!validation.success) { - return NextResponse.json( - { - success: false, - error: 'Validation error', - details: validation.error.errors, - }, - { status: 400 } - ) - } - - const next = updateRunStatus( - id, - ACTION_TO_STATUS[validation.data.action], - validation.data.note || `Run action: ${validation.data.action}` - ) - - return NextResponse.json({ - success: true, - operator: auth.operator, - run: next, - }) - } catch (error) { - console.error('[AgentRuns] Failed to control run:', error) - return NextResponse.json( - { - success: false, - error: 'Internal server error', - }, - { status: 500 } - ) - } -} diff --git a/src/app/api/agent/runs/[id]/route.ts b/src/app/api/agent/runs/[id]/route.ts deleted file mode 100644 index 05fc294..0000000 --- a/src/app/api/agent/runs/[id]/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NextResponse } from 'next/server' -import { getRun, listTasks, listToolCalls } from '@/lib/agent-os/store' -import { requireAdminAccess } from '../../../admin/_lib/auth' - -export async function GET( - request: Request, - { params }: { params: Promise<{ id: string }> } -) { - const auth = requireAdminAccess(request) - if (!auth.authorized) { - return auth.response - } - - const { id } = await params - const run = getRun(id) - - if (!run) { - return NextResponse.json( - { - success: false, - error: 'Run not found', - }, - { status: 404 } - ) - } - - return NextResponse.json({ - success: true, - operator: auth.operator, - run, - tasks: listTasks(id), - toolCalls: listToolCalls(id), - }) -} diff --git a/src/components/admin/AdminGoalConsole.tsx b/src/components/admin/AdminGoalConsole.tsx deleted file mode 100644 index 11f58a4..0000000 --- a/src/components/admin/AdminGoalConsole.tsx +++ /dev/null @@ -1,91 +0,0 @@ -'use client' - -import { FormEvent, useState } from 'react' -import { useTranslations } from 'next-intl' - -export function AdminGoalConsole() { - const t = useTranslations('admin') - const [adminKey, setAdminKey] = useState('') - const [goal, setGoal] = useState(() => t('goalDefault')) - const [loading, setLoading] = useState(false) - const [responseText, setResponseText] = useState('') - - async function handleSubmit(event: FormEvent) { - event.preventDefault() - setLoading(true) - setResponseText('') - - try { - const response = await fetch('/api/agent/goals', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-admin-key': adminKey, - 'x-operator': 'admin-console-chat', - }, - body: JSON.stringify({ goal }), - }) - - const json = await response.json() - setResponseText(JSON.stringify(json, null, 2)) - } catch (error) { - setResponseText( - JSON.stringify( - { - success: false, - error: error instanceof Error ? error.message : t('unknownError'), - }, - null, - 2 - ) - ) - } finally { - setLoading(false) - } - } - - return ( -
-

{t('goalConsoleTitle')}

-

- {t('goalConsoleDescription')} -

- -
- - -