diff --git a/docs/prd/_scan-manifest.json b/docs/prd/_scan-manifest.json new file mode 100644 index 0000000..009d3a0 --- /dev/null +++ b/docs/prd/_scan-manifest.json @@ -0,0 +1,492 @@ +{ + "manifestVersion": "1.0", + "generatedAt": "2026-05-12", + "scope": "LemonBoss only — multi-agent autonomous dev system (Company / OKR / Jobs / Pipeline / Tasks / Agent Chat / Templates / Quota). ArenaSK content (Skill marketplace, Arena ELO, Benchmark, Master /api/master, Innovator, Open API, Evolution) is EXCLUDED.", + + "project": { + "name": "LemonBoss", + "legacyName": "ArenaSK", + "rootPath": "/Users/yibo/Documents/lemonAI/lemonboss", + "nodeVersion": ">=18", + "packageManager": "pnpm (workspace)", + "moduleAlias": { "@server": "server" } + }, + + "entryPoints": { + "agentServer": { + "file": "server/agent-server.js", + "port": 3010, + "startCommand": "bash start.sh", + "responsibilities": [ + "express app with body-sanitizer middleware (strips \\x00-\\x1F before JSON.parse)", + "CORS allow http://localhost:5173", + "Mounts /api/agent /api/company /api/goals /api/tasks /api/health" + ], + "mountedRoutes": [ + { "prefix": "/api/agent", "file": "server/routes/agent-chat.js" }, + { "prefix": "/api/company", "file": "server/routes/company.js" }, + { "prefix": "/api/goals", "file": "server/routes/goals.js" }, + { "prefix": "/api/tasks", "file": "server/routes/tasks.js" }, + { "prefix": "/api/health", "inline": "GET → { status: ok, service: agent-chat }" } + ], + "notMounted": "auth, users, plugins (only in server/index.js, out of LemonBoss-subset scope)" + }, + "binAutoDev": { + "file": "bin/auto-dev or server/src/cli/auto-dev.js", + "purpose": "Standalone CLI to run an end-to-end autonomous dev task (e.g. `auto-dev \"implement fibonacci\"`)" + }, + "startScript": { + "file": "start.sh", + "steps": [ + "load nvm", + "kill any process on :3010 and :5173", + "PORT=3010 node server/agent-server.js &", + "cd web && npx vite --host &" + ], + "note": "Does NOT build frontend, sync skills, or start bots — those are separate scripts." + } + }, + + "configFiles": { + "rootPackage": "package.json (workspaces: packages/*, agent-bridge, server, web)", + "scripts": { + "setup": "cp -n .env.example .env && npm install && cd server && npm install && cd ../web && npm install", + "dev": "concurrently 'npm run server' 'npm run web' (full server/index.js — NOT LemonBoss-only)", + "server": "cd server && npm start", + "web": "cd web && npm run dev", + "test:unit": "vitest run", + "test:multi-cli": "node server/src/tests/scripts/test-multi-cli.js", + "test:browser-logger": "node server/src/modules/browser-logger/__tests__/test-runner.js", + "test:design-validator": "node server/src/modules/design-validator/__tests__/test-runner.js", + "example": "node server/src/cli/auto-dev.js '...'" + }, + "serverConfig": { + "file": "server/config.js", + "fields": ["port (3010)", "host (0.0.0.0)", "workspacesDir", "skillsCodeDir (LemonBoss IGNORES — ArenaSK)", "trainedSkillsDir", "conversationsDir", "cors (5173, 3010)", "openApi.apiKeys (ArenaSK)"] + }, + "env": ".env from .env.example (port, CORS, GITHUB/GOOGLE OAuth client IDs, OPEN_API_KEYS, dir overrides)" + }, + + "routes": { + "_summary": "LemonBoss-relevant prefixes only. Full route listing per file below.", + + "agentChat": { + "file": "server/routes/agent-chat.js", + "prefix": "/api/agent", + "endpoints": [ + { "method": "GET", "path": "/list", "purpose": "list all agents (system + per company)" }, + { "method": "GET", "path": "/quota", "purpose": "token consumption & cost stats" }, + { "method": "POST", "path": "/:agentId/chat", "purpose": "chat with agent (SSE stream)" }, + { "method": "POST", "path": "/:agentId/chat/:convId/stop", "purpose": "interrupt running chat" }, + { "method": "GET", "path": "/:agentId/info", "purpose": "agent metadata + config" }, + { "method": "PUT", "path": "/:agentId/heartbeat", "purpose": "update heartbeat config" }, + { "method": "GET", "path": "/:agentId/cron", "purpose": "read cron.json" }, + { "method": "PUT", "path": "/:agentId/cron", "purpose": "update cron.json" }, + { "method": "GET", "path": "/:agentId/todo", "purpose": "read todo.md" }, + { "method": "PUT", "path": "/:agentId/todo", "purpose": "update todo.md" }, + { "method": "GET", "path": "/:agentId/plan", "purpose": "read plan.md" }, + { "method": "PUT", "path": "/:agentId/plan", "purpose": "update plan.md" }, + { "method": "GET", "path": "/:agentId/conversations", "purpose": "list conversations" }, + { "method": "GET", "path": "/:agentId/conversations/:convId", "purpose": "get conversation history" }, + { "method": "DELETE", "path": "/:agentId/conversations/:convId", "purpose": "delete conversation" }, + { "method": "GET", "path": "/:agentId/conversations/:convId/sub-logs", "purpose": "list sub-agent logs" }, + { "method": "GET", "path": "/:agentId/conversations/:convId/sub-logs/:logFile", "purpose": "fetch a sub-log file" }, + { "method": "GET", "path": "/:agentId/files", "purpose": "list agent dir files" }, + { "method": "GET", "path": "/:agentId/file/*", "purpose": "read a file in agent dir" }, + { "method": "PUT", "path": "/:agentId/file/*", "purpose": "write a file in agent dir" }, + { "method": "DELETE", "path": "/:agentId/file/*", "purpose": "delete a file in agent dir" }, + { "method": "GET", "path": "/:agentId/bootstrap/:filename", "purpose": "read bootstrap file (agent.md, soul.md, ...)" }, + { "method": "POST", "path": "/:agentId/memory/cerebellum", "purpose": "save cerebellum memory snippet" }, + { "method": "POST", "path": "/:agentId/memory/consolidate", "purpose": "consolidate session into long-term memory" }, + { "method": "POST", "path": "/:agentId/memory/daily-report", "purpose": "generate daily memory report" }, + { "method": "POST", "path": "/:agentId/upload", "purpose": "multipart upload (multer, ≤20 files)" }, + { "method": "GET", "path": "/:agentId/uploads/:convId", "purpose": "list uploads in conversation" }, + { "method": "GET", "path": "/:agentId/uploads/:convId/:filename", "purpose": "download upload" }, + { "method": "DELETE", "path": "/:agentId/uploads/:convId/:filename", "purpose": "delete upload" }, + { "method": "POST", "path": "/scaffold", "purpose": "scaffold a new agent skeleton" }, + { "method": "POST", "path": "/task/dispatch", "purpose": "Master dispatches a task to a sub-agent" }, + { "method": "POST", "path": "/task/report", "purpose": "agent reports task done/failed" }, + { "method": "POST", "path": "/:agentId/task/transition", "purpose": "agent transitions to a stage state" }, + { "method": "POST", "path": "/task/reject", "purpose": "supervisor rejects a task output" }, + { "method": "POST", "path": "/human-request", "purpose": "agent escalates to human" } + ], + "notes": [ + "Heavy file ~2470 lines — many endpoints share helpers (resolveAgentDir, getConvDir).", + "Chat streams as SSE with token-level events.", + "Task dispatch/report endpoints bridge into PipelineService.advanceStep when callbacks match a pipeline run." + ] + }, + + "company": { + "file": "server/routes/company.js", + "prefix": "/api/company", + "endpoints": [ + { "method": "POST", "path": "/create", "purpose": "create company" }, + { "method": "GET", "path": "/list", "purpose": "list companies" }, + { "method": "GET", "path": "/templates", "purpose": "list agent templates" }, + { "method": "POST", "path": "/templates", "purpose": "create template" }, + { "method": "PATCH", "path": "/templates/:templateId", "purpose": "update template" }, + { "method": "DELETE", "path": "/templates/:templateId", "purpose": "delete template" }, + { "method": "POST", "path": "/templates/:templateId/generate", "purpose": "scaffold from template" }, + { "method": "POST", "path": "/templates/:templateId/train", "purpose": "train template (skill evolution loop)" }, + { "method": "POST", "path": "/templates/:templateId/skills/upload", "purpose": "upload skill files to template (≤50)" }, + { "method": "POST", "path": "/templates/:templateId/provision", "purpose": "provision template runtime" }, + { "method": "GET", "path": "/templates/:templateId/logs", "purpose": "fetch training logs" }, + { "method": "GET", "path": "/templates/:templateId/files", "purpose": "list template files" }, + { "method": "GET", "path": "/templates/:templateId/files/content", "purpose": "read template file" }, + { "method": "PUT", "path": "/templates/:templateId/files/content", "purpose": "write template file" }, + { "method": "GET", "path": "/:companyId", "purpose": "company detail" }, + { "method": "PUT", "path": "/:companyId", "purpose": "update company" }, + { "method": "DELETE", "path": "/:companyId", "purpose": "delete company" }, + { "method": "POST", "path": "/:companyId/shutdown", "purpose": "soft-shutdown company" }, + { "method": "POST", "path": "/:companyId/start", "purpose": "start running" }, + { "method": "GET", "path": "/:companyId/status", "purpose": "status + heartbeat info" }, + { "method": "PUT", "path": "/:companyId/agents/:agentId/heartbeat", "purpose": "update agent heartbeat config" }, + { "method": "POST", "path": "/:companyId/agents/:agentId/heartbeat/start", "purpose": "start agent heartbeat" }, + { "method": "POST", "path": "/:companyId/agents/:agentId/heartbeat/stop", "purpose": "stop agent heartbeat" }, + { "method": "GET", "path": "/:companyId/agents/:agentId/skills", "purpose": "list agent skills" }, + { "method": "POST", "path": "/:companyId/hire", "purpose": "hire agent from template or scaffold" }, + { "method": "POST", "path": "/:companyId/fire", "purpose": "suspend agent" }, + { "method": "POST", "path": "/:companyId/deposit-template", "purpose": "deposit trained agent (≥95) as new template" }, + { "method": "GET", "path": "/:companyId/agents", "purpose": "list company agents" }, + { "method": "GET", "path": "/:companyId/goals", "purpose": "company OKR list" }, + { "method": "POST", "path": "/:companyId/goals", "purpose": "create goal" }, + { "method": "GET", "path": "/:companyId/goals/:goalId", "purpose": "goal detail" }, + { "method": "PUT", "path": "/:companyId/goals/:goalId", "purpose": "update goal" }, + { "method": "POST", "path": "/:companyId/goals/:goalId/nodes", "purpose": "add OKR node" }, + { "method": "PUT", "path": "/:companyId/goals/:goalId/nodes/:nodeId","purpose": "update OKR node" }, + { "method": "GET", "path": "/:companyId/goals/:goalId/progress", "purpose": "OKR progress %" }, + { "method": "POST", "path": "/:companyId/pipelines", "purpose": "create pipeline" }, + { "method": "GET", "path": "/:companyId/pipelines", "purpose": "list pipelines" }, + { "method": "GET", "path": "/:companyId/pipelines/:pipelineId", "purpose": "pipeline detail" }, + { "method": "PUT", "path": "/:companyId/pipelines/:pipelineId", "purpose": "update pipeline" }, + { "method": "POST", "path": "/:companyId/pipelines/:pipelineId/run", "purpose": "start a run" }, + { "method": "GET", "path": "/:companyId/pipelines/runs/list", "purpose": "list runs" }, + { "method": "GET", "path": "/:companyId/pipelines/runs/:runId", "purpose": "run detail" }, + { "method": "POST", "path": "/:companyId/pipelines/runs/:runId/advance", "purpose": "advance to next step" }, + { "method": "GET", "path": "/:companyId/pipelines/runs/:runId/current", "purpose": "current ready step" }, + { "method": "POST", "path": "/:companyId/pipelines/runs/:runId/reject", "purpose": "COO rejects a step output" }, + { "method": "GET", "path": "/:companyId/schedule", "purpose": "list schedule slots" }, + { "method": "POST", "path": "/:companyId/schedule/slots", "purpose": "add slot" }, + { "method": "PUT", "path": "/:companyId/schedule/slots/:slotId", "purpose": "update slot" }, + { "method": "DELETE", "path": "/:companyId/schedule/slots/:slotId", "purpose": "delete slot" }, + { "method": "GET", "path": "/:companyId/data/metrics", "purpose": "raw metrics" }, + { "method": "GET", "path": "/:companyId/data/trending", "purpose": "trending" }, + { "method": "GET", "path": "/:companyId/data/attribution", "purpose": "attribution" }, + { "method": "GET", "path": "/:companyId/files", "purpose": "company file list" }, + { "method": "GET", "path": "/:companyId/files/content", "purpose": "read file" }, + { "method": "PUT", "path": "/:companyId/files/content", "purpose": "write file" }, + { "method": "GET", "path": "/:companyId/workspace/files", "purpose": "workspace file list" }, + { "method": "GET", "path": "/:companyId/workspace/files/content", "purpose": "workspace read" }, + { "method": "PUT", "path": "/:companyId/workspace/files/content", "purpose": "workspace write" }, + { "method": "GET", "path": "/:companyId/messages", "purpose": "activity / message log" }, + { "method": "GET", "path": "/:companyId/stats", "purpose": "company stats" }, + { "method": "GET", "path": "/:companyId/jobs", "purpose": "list pipeline-run-derived jobs (paged, filtered)" }, + { "method": "GET", "path": "/:companyId/jobs/:jobId", "purpose": "job detail" } + ] + }, + + "goals": { + "file": "server/routes/goals.js", + "prefix": "/api/goals", + "endpoints": [ + { "method": "GET", "path": "/", "purpose": "list goals (global / index.json)" }, + { "method": "POST", "path": "/", "purpose": "create goal" }, + { "method": "GET", "path": "/:goalId", "purpose": "full goal tree" }, + { "method": "PUT", "path": "/:goalId", "purpose": "update goal" }, + { "method": "POST", "path": "/:goalId/nodes", "purpose": "add node" }, + { "method": "GET", "path": "/:goalId/nodes/:nodeId", "purpose": "get node + parent context" }, + { "method": "PUT", "path": "/:goalId/nodes/:nodeId", "purpose": "update node status" }, + { "method": "GET", "path": "/:goalId/progress", "purpose": "leaf-aggregated progress" } + ], + "idScheme": "Dot-separated hierarchy: G-001 → G-001.1 (Objective) → G-001.1.1 (KeyResult) → G-001.1.1.1 (SubTask)" + }, + + "tasks": { + "file": "server/routes/tasks.js", + "prefix": "/api/tasks", + "endpoints": [ + { "method": "GET", "path": "/", "purpose": "aggregated cross-agent task list (10s TTL cache)" }, + { "method": "GET", "path": "/:taskId/timeline", "purpose": "merged timeline of conversation events for task (and optional children)" } + ] + } + }, + + "services": { + "_lemonbossRelevant": { + "company-service.js": "Company CRUD + Agent hiring (template-first → scaffold fallback) + depositTemplate + resolveAgentDir static", + "company-heartbeat-service.js":"Company-level heartbeat: scans active pipelines (cron), drives running pipeline steps, executes schedule slots, triggers CEO heartbeat, generates daily report, sends Master alerts", + "heartbeat-service.js": "Agent-level heartbeat: setInterval(intervalMinutes) → POST /api/agent/{id}/chat with system heartbeat payload", + "goals.js": "GoalsService: file-backed goal tree (data/companies/{id}/goals/ or data/goals/), progress recalc, change log", + "pipeline-service.js": "Pipeline CRUD + Run lifecycle (startRun → markStepStarted → advanceStep → loop|completed) + rejectStep + findActiveRunStep", + "task-service.js": "Aggregates conversations across system/agents + data/companies/*/agents into task groups; infers parent/child via 4 strategies", + "schedule-service.js": "Schedule slot CRUD + getDueSlots(companyId)", + "memory-service.js": "Cerebellum + consolidation + daily-report endpoints", + "datasource-service.js": "External data source connections", + "file-parser.js": "Markdown/PDF/Docx parsing for uploads", + "conversation-logger.js": "Append-only messages.log writer", + "user-db.js": "users.sqlite — single users table (used by auth/users routes — only via server/index.js, not agent-server.js)", + "job-manager.js": "Job execution queue (LemonBoss: pipeline-run-jobs)", + "schedule-service.js": "Cron / schedule slot storage", + "task-queue.js": "Queue primitive used by job-manager", + "skill-task-manager.js": "[ArenaSK-leaning] tracks skill execution tasks — touched if /api/company/{id}/agents/{id}/skills is hit" + }, + "_arenaSkOnly_skipInDocs": [ + "skill-db.js", "skill-registry.js", "skill-dir-manager.js", "skill-packager.js", + "benchmark-db.js", "benchmark-runner.js", "evolution-loop.js", + "retrain-pipeline.js", "retrain-watchdog.js", "innovator-service.js", + "claude-chat-service.js" + ] + }, + + "core": { + "_dir": "server/src/core/", + "orchestrator.js": "Multi-agent loop: Master ↔ Sub-agent, format validation, maxRepairs, completion detection", + "agent-manager.js": "Unified Agent call gateway: config lookup → hook orchestration → protocol → retry", + "scheduler.js": "Task scheduling / timing primitive", + "session-pool.js": "Acquire / release / cleanup persistent ACP sessions", + "prompt-builder.js": "Build system prompt from agent.md + soul.md + identity.md + user.md + tools.md + heartbeat.md + memory + cron", + "runtime-resolver.js": "Dynamic agent type + env inference at run-time", + "master-session.js": "Master Agent persistent Claude CLI session — multi-turn conversation + tool dispatch (call_agent, orchestrate, ...)", + "stream-executor.js": "Stream prompt through chosen ACP protocol", + "parser.js": "Parse [TOOL_CALL:], JSON, markdown agent output", + "result-tracker.js": "Execution state machine + checkpoint", + "hook-runner.js": "Bootstrap hooks (on_init / on_failure / on_complete)", + "protocol-factory.js": "Create ClaudeCliProtocol / WebSocketProtocol instances", + "skill-config-loader.js": "Scan skill dir + merge config.json + inject into prompt", + "prompts.js": "Master/Sub-agent prompt templates", + "evolution-memory.js": "Training-history store (shared with ArenaSK)", + "logging/": { + "LogManager.js": "Multi-session log dispatch", + "LogSession.js": "Per-session log buffer", + "backends": ["BaseBackend.js", "ConsoleBackend.js", "FileBackend.js", "CompositeBackend.js"] + } + }, + + "engine": { + "_dir": "server/src/engine/", + "index.js": "Barrel exports", + "process-guard.js": "Two-layer timeout guard (fresh / resume defaults) for Claude CLI subprocess", + "run-registry.js": "Active run table (runId → meta, output stream refs, status); DEFAULT_MAX_EXITED retention", + "command-queue.js": "Multi-lane FIFO queue with generation-based cancellation (CommandLaneClearedError on clearAll)", + "failover-error.js": "Error classification → FailoverReason, RETRYABLE_REASONS", + "idle-timer.js": "Streaming idle-watchdog (per-byte reset)" + }, + + "plugins": { + "_dir": "server/src/plugins/", + "plugin-manager.js": "Discover / load / enable / disable plugins; emit hooks", + "agent-bridge.js": "Cross-plugin agent message relay", + "config-bridge.js": "Plugin config merge + validation", + "bootstrap.js": "Plugin init sequence", + "openclaw.plugin.json": "OpenClaw plugin manifest (sibling to plugins/)" + }, + + "tools": { + "_dir": "server/src/tools/", + "call-agent-tool.js": "Master tool: call_sub_agent / list_agents", + "orchestrate-tool.js": "Master tool: orchestrate (multi-step plan)", + "train-skill-tool.js": "Master tool: trigger skill training (Hunt → Digest → Evolving) [ArenaSK-leaning]", + "read-tools.js": "read_checkpoint / read_log / list_retrain_jobs", + "tool-registry.js": "Tool register & dispatch", + "marketplace-tools.js":"[ArenaSK only — skip in LemonBoss docs]" + }, + + "packages": { + "acp-core": "ACP Protocol Stack — Protocol × Transport × Launcher 4D architecture (ClaudeCliProtocol over stdio, WebSocketProtocol, transports, launchers).", + "agent-core": "Agent class + AgentScaffold; directory-driven agent bootstrap unified across system/agents and data/companies/*/agents (ESM).", + "plugin-bootstrap": "OpenClaw plugin loader — discovery + load sequence.", + "evolution": "[ArenaSK only — skip]" + }, + + "middleware": { + "_dir": "server/middleware/", + "jwt-auth.js": "requireAuth + optionalAuth + requireAdmin guards; signAndSetCookie / clearCookie", + "api-key-auth.js": "X-API-Key validation (ArenaSK Open API)", + "api-access-logger.js": "Request/response log line" + }, + + "frontend": { + "_dir": "web/src/", + "entry": "main.js → createApp(App) → router", + "router": { + "file": "web/src/router/index.js", + "routes": [ + { "path": "/", "redirect": "/agent/master" }, + { "path": "/agents/:agentId?", "redirect": "/agent/$agentId or /agent/agent-creator" }, + { "path": "/agent/:agentId/:convId?", "name": "AgentChat", "component": "AgentChat.vue" }, + { "path": "/:id/agents/:agentId?/:convId?", "name": "CompanyAgentChat", "component": "AgentChat.vue" }, + { "path": "/templates/:agentId?", "name": "TemplateAgents", "component": "TemplateAgents.vue" }, + { "path": "/company", "name": "CompanyList", "component": "CompanyDashboard.vue" }, + { "path": "/:id/:tab(overview|agents|pipelines|goals|sop|okr|team|files|messages|skills|jobs|workspace)", "name": "CompanyDetail", "component": "CompanyDashboard.vue" }, + { "path": "/okr", "name": "OkrDashboard", "component": "OkrDashboard.vue" }, + { "path": "/skills", "name": "SkillsMarket", "component": "SkillsMarket.vue", "lemonBoss": false }, + { "path": "/jobs", "name": "JobsPanel", "component": "JobsPanel.vue" }, + { "path": "/tasks/:taskId?", "name": "TaskTimeline", "component": "TaskTimeline.vue" }, + { "path": "/pipelines", "name": "PipelineEditor", "component": "PipelineEditor.vue" }, + { "path": "/quota", "name": "QuotaMonitor", "component": "QuotaMonitor.vue" } + ] + }, + "composables": [ + "useTheme.js — dark / light theme state", + "useCompanies.js — companies list cache + active company selection" + ], + "i18n": "[N/A] No vue-i18n integration found; UI strings are inline Chinese.", + "views": { + "_pagesInScope": 9, + "AgentChat.vue": { "lines": 1289, "purpose": "Multi-agent chat — 3-col (agent sidebar / conv list / message stream)", "apis": ["/api/agent/list", "/api/agent/{id}/chat", "/api/agent/{id}/conversations", "/api/agent/{id}/info"] }, + "AgentList.vue": { "lines": 300, "purpose": "Agent registry browser with file tree + preview", "apis": ["/api/agent/list", "/api/agent/{id}/files", "/api/agent/{id}/file/*"] }, + "CompanyDashboard.vue": { "lines": 2396, "purpose": "Company control center — tabs: Overview / Agents / Pipelines / Goals / SOP / OKR / Team / Files / Messages / Skills / Jobs / Workspace", "apis": ["/api/company/*"] }, + "JobsPanel.vue": { "lines": 710, "purpose": "Job queue (pipeline-derived) with filters & detail modal", "apis": ["/api/company/{id}/jobs", "/api/company/{id}/jobs/:jobId"] }, + "OkrDashboard.vue": { "lines": 352, "purpose": "OKR list + decomposition tree with progress", "apis": ["/api/goals", "/api/company/{id}/goals"] }, + "PipelineEditor.vue": { "lines": 791, "purpose": "Pipeline definition grid + run controls", "apis": ["/api/company/{id}/pipelines", "/api/company/{id}/pipelines/runs/*"] }, + "QuotaMonitor.vue": { "lines": 526, "purpose": "Token / cost analytics with trend charts + agent leaderboard", "apis": ["/api/agent/quota"] }, + "SkillsMarket.vue": { "lines": 571, "purpose": "[ArenaSK — out of scope]" }, + "TaskTimeline.vue": { "lines": 736, "purpose": "Cross-agent task list + merged event timeline (children expandable)", "apis": ["/api/tasks", "/api/tasks/:taskId/timeline"] }, + "TemplateAgents.vue": { "lines": 1019, "purpose": "Template library — list, search, filter, sort; detail viewer with editable bootstrap files", "apis": ["/api/company/templates", "/api/company/templates/:templateId/*"] } + } + }, + + "data": { + "users.sqlite": { + "table": "users", + "columns": [ + "id INTEGER PRIMARY KEY", + "email TEXT UNIQUE", + "password_hash TEXT", + "display_name TEXT", + "avatar_url TEXT", + "provider TEXT (local | github | google)", + "provider_id TEXT", + "github_login TEXT", + "role TEXT (user | admin)", + "api_key TEXT", + "created_at TEXT", + "last_login TEXT" + ], + "indexes": ["email", "api_key", "(provider, provider_id)"] + }, + "fileSystem": { + "data/companies/{companyId}/": { + "company.json": "Company metadata (id, name, status, ceo, agents[], heartbeat, config)", + "agents/{agentId}/": "Per-agent dir (bootstrap files + memory + conversations)", + "goals/{G-id}/": "OKR tree (goal.json + progress.json + history.log)", + "pipelines/": "Pipeline definitions + runs/", + "pipelines/runs/{R-id}.json": "Pipeline run state", + "workspace/": "Company shared workspace (assets/ + output/)", + "docs/": "Company documentation", + "logs/pipeline_runs/":"Pipeline execution traces", + "logs/audit/": "State change audit" + }, + "data/goals/{G-id}/": "Global (companyId-less) goals (back-compat)", + "system/agents/{agentId}/": "System-level agent (master, hunter, digester, evolver, agent-creator, innovator, screenwriter, ...)", + "system/agents/.tasks/{taskId}.json": "Task metadata used by TaskService", + "system/templates/{templateId}/": "Agent skeleton template (agent.md, soul.md, identity.md, user.md, tools.md, heartbeat.md, plan.md, todo.md, cron.json, config.json + skills/ + .claude/)", + "system/templates/_registry.json": "Template registry (benchmarkScore, usedCount, ...)", + "server/conversations/": "Reserved — actual conversations stored under each agent dir" + }, + "agentDir": { + "bootstrapFiles": ["agent.md", "soul.md", "identity.md", "user.md", "tools.md", "heartbeat.md", "plan.md", "todo.md", "cron.json", "config.json"], + "runtimeSubdirs": ["memory/", "conversations/", "skills/", ".claude/", "workspace/"], + "memoryStructure": ["memory.md (working memory)", "evolving/v{n}/ (versioned skill iterations)", "benchmark/v{n}/ (benchmark snapshots)"], + "conversationLayout": "{convId}/{meta.json, history.json, messages.log, sub-logs/, uploads/}" + } + }, + + "protocols": { + "agentChatSSE": { + "path": "POST /api/agent/:agentId/chat", + "transport": "Server-Sent Events (text/event-stream)", + "frames": [ + "{ type: 'start', conversationId }", + "{ type: 'token', delta }", + "{ type: 'tool', name, input }", + "{ type: 'result', output }", + "{ type: 'done', reason }", + "{ type: 'error', message }" + ], + "interrupt": "POST /api/agent/:agentId/chat/:convId/stop" + }, + "bodySanitizer": "Strips \\x00-\\x1F (preserves \\n/\\r/\\t as escapes) from JSON body before JSON.parse. Reasons: external agents posting curl payloads with raw control chars." + }, + + "engineSubsystems": { + "agenticLoop": "Master agent → propose plan → call_sub_agent → sub-agent runs → result → Master decides next → completion", + "hiringPipeline": "Template found → copy skeleton + inject company context → mark active. Template not found → scaffold (AgentScaffold.create) → status=training. After score ≥95 → depositTemplate.", + "companyHeartbeat": "setInterval(intervalMinutes) → _onHeartbeat → (a) _checkAndStartPipelines (cron) (b) _driveActiveRuns (trigger ready steps & timeout) (c) _executeScheduledTasks (d) _triggerCeoHeartbeat (e) _checkDailyReport (08:00). Errors → _sendAlertToMaster.", + "pipelineLifecycle": "startRun (step[0]=ready, rest=waiting) → markStepStarted (in_progress) → advanceStep (done → next ready / completed; failed → run.status=failed; loop → new run) → rejectStep (step.rejections.push, reset to ready, inject __rejection.instruction)", + "okrIdScheme": "Dot-separated: G-001 (Goal, L1) → G-001.1 (Objective, L2) → G-001.1.1 (KeyResult, L3) → G-001.1.1.1 (SubTask, L4+). parseNodeId returns {goalId, level, parentId, levelName}.", + "taskInference": "TaskService._inferParentChild — 4 strategies: (1) explicit .tasks meta parentTaskId (2) T-* from active fromAgent's tpl-train (3) T-* from master via recent conv-* (4) tpl-train-*/tpl-gen-* from time-adjacent master conv-*." + }, + + "architectureDocs": { + "docs/LEMONBOSS-SYSTEM-DESIGN.md": [ + "System Vision (道-法-术-器)", + "Four-layer Agent Architecture (Master → CXO → Role → Skill)", + "Agent Atomic Model (6 bootstrap files)", + "Agentic Loop Engine", + "Stratified Meta-Capabilities", + "Agent Sandbox Autonomy", + "Inter-Agent Communication", + "Self-Iteration Closed Loop", + "Initialization Flow", + "Runtime Concurrency", + "Directory Structure", + "Data Model", + "API Design", + "Innovator Meta-Agent", + "Agent Conversation Architecture", + "Skill Orchestration", + "Local-File-First Encapsulation", + "Tool Isolation", + "Scaffold-First Agent Creation", + "FDE Training Pipeline (Hunt→Digest→Evolving)", + "Agent Memory System", + "Active Heartbeat", + "Company Layer", + "Bootstrap Mechanism", + "Automated Hiring Orchestration" + ], + "docs/architecture-v5.md": [ + "Cognitive Stack (Cerebrum / Cerebellum / Hippocampus)", + "OKR Goal Management (dot-separated ID hierarchy)", + "Multi-Tenant Company Architecture", + "Phase 1-6 status (Phase 7 Cognitive pending)" + ], + "docs/task_timeline_design.md": "Task timeline cross-agent merge design", + "docs/system-design/": "Subsystem deep-dives", + "docs/user-guides/": "User-facing docs" + }, + + "outputPlan": { + "siteRoot": "docs/prd/", + "chapters": [ + "01-overview", + "02-prd", + "03-feature-tree", + "04-workflows", + "05-prototype", + "06-sdd-model", + "07-sdd-view", + "08-sdd-control", + "09-sdd-process", + "10-reproducibility" + ], + "indexShell": "index.html + assets/{app.css, app.js, lunr.min.js, lunr.zh.js, search-index.json}", + "interaction": "left 280px 3-level menu + right iframe + top search → results tab; hash router (#/06-sdd-model/company-entity); dark/light toggle; mobile burger", + "diagrams": "Mermaid inline + SVG (no CDN)", + "search": "lunr.js with Chinese bigram tokenizer; index built from chapter HTML titles + body text", + "verification": "python3 -m http.server 8080 -d docs/prd → manual click-through 9 page menus; search 'Company / OKR / Pipeline / Agent / Task' all hit" + }, + + "scopeGuard_DoNotMention": [ + "skill-db / skill-registry / skill-dir-manager / skill-packager", + "benchmark-db / benchmark-runner", + "evolution-loop / retrain-pipeline / retrain-watchdog", + "/api/open/* /api/arena/* /api/innovator/* /api/master/* /api/evolution", + "Skill marketplace UI (SkillsMarket.vue)", + "ELO ranking, six-dimensional benchmark", + "WhatsApp bot, Lark integration", + "Master Agent chat at /api/master/chat (Master tools still used internally via agent-server's /api/agent/master/chat — keep that)" + ] +} diff --git a/docs/prd/assets/app.css b/docs/prd/assets/app.css new file mode 100644 index 0000000..c18b6a9 --- /dev/null +++ b/docs/prd/assets/app.css @@ -0,0 +1,395 @@ +/* ============================================================ + LemonBoss PRD/SDD doc site — global styles + ============================================================ */ + +:root { + --bg: #fafaf7; + --bg-2: #ffffff; + --bg-3: #f3f3ee; + --fg: #1d1d1f; + --fg-2: #5a5a60; + --fg-3: #8a8a92; + --border: #e3e3dd; + --border-2: #d4d4cd; + --accent: #f5a524; /* lemon */ + --accent-2: #e69500; + --accent-soft: #fff4dc; + --link: #0a66c2; + --code-bg: #f5f5ef; + --code-fg: #2d2d33; + --shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 12px rgba(0,0,0,.04); + --shadow-lg: 0 12px 32px rgba(0,0,0,.08); + --radius: 8px; + --radius-sm: 4px; + --side-w: 280px; + --top-h: 52px; + --mono: ui-monospace, "SF Mono", Menlo, Consolas, "Roboto Mono", monospace; + --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", + "Hiragino Sans GB", "Microsoft YaHei", Helvetica, Arial, sans-serif; +} + +[data-theme="dark"] { + --bg: #16161a; + --bg-2: #1d1d22; + --bg-3: #25252b; + --fg: #e8e8ea; + --fg-2: #b0b0b8; + --fg-3: #76767e; + --border: #2d2d34; + --border-2: #3a3a42; + --accent: #f5a524; + --accent-2: #ffbe3d; + --accent-soft: #3a2f15; + --link: #6db4ff; + --code-bg: #25252b; + --code-fg: #e6e6e8; + --shadow: 0 1px 2px rgba(0,0,0,.4), 0 4px 12px rgba(0,0,0,.3); + --shadow-lg: 0 12px 32px rgba(0,0,0,.6); +} + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + height: 100%; + font-family: var(--sans); + font-size: 14px; + background: var(--bg); + color: var(--fg); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { color: var(--link); text-decoration: none; } +a:hover { text-decoration: underline; } + +button { + background: none; + border: none; + color: inherit; + font: inherit; + cursor: pointer; +} + +/* ─── Topbar ─── */ +.topbar { + position: fixed; + top: 0; left: 0; right: 0; + height: var(--top-h); + background: var(--bg-2); + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + gap: 12px; + padding: 0 16px; + z-index: 50; +} + +.burger { + display: none; + padding: 6px; + border-radius: var(--radius-sm); + color: var(--fg-2); +} +.burger:hover { background: var(--bg-3); color: var(--fg); } + +.brand { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + color: var(--fg); + flex-shrink: 0; +} +.brand .lemon { font-size: 20px; } +.brand .title { font-size: 15px; } +.brand .subtitle { + font-size: 12px; + font-weight: 400; + color: var(--fg-3); + margin-left: 6px; + padding-left: 10px; + border-left: 1px solid var(--border); +} + +.search-wrap { + flex: 1; + max-width: 520px; + margin: 0 auto; + position: relative; +} +.search-wrap input { + width: 100%; + height: 32px; + padding: 0 36px 0 12px; + border: 1px solid var(--border); + background: var(--bg); + border-radius: var(--radius-sm); + color: var(--fg); + font-family: var(--sans); + outline: none; + transition: border-color .15s, box-shadow .15s; +} +.search-wrap input:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.kbd { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + font-family: var(--mono); + font-size: 11px; + color: var(--fg-3); + background: var(--bg-3); + border: 1px solid var(--border); + border-radius: 3px; + padding: 1px 5px; + pointer-events: none; +} + +.theme-toggle { + padding: 6px; + border-radius: var(--radius-sm); + color: var(--fg-2); +} +.theme-toggle:hover { background: var(--bg-3); color: var(--fg); } +.theme-toggle .ico-moon { display: none; } +[data-theme="dark"] .theme-toggle .ico-sun { display: none; } +[data-theme="dark"] .theme-toggle .ico-moon { display: block; } + +/* ─── Layout ─── */ +.layout { + position: fixed; + top: var(--top-h); + left: 0; right: 0; bottom: 0; + display: flex; +} + +/* ─── Sidebar ─── */ +.sidebar { + width: var(--side-w); + background: var(--bg-2); + border-right: 1px solid var(--border); + overflow-y: auto; + overflow-x: hidden; + flex-shrink: 0; +} +.sidebar::-webkit-scrollbar { width: 8px; } +.sidebar::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 4px; } + +.menu { padding: 8px 0; } +.menu-chapter { margin-bottom: 2px; } +.menu-chapter > .menu-link { + font-weight: 600; + color: var(--fg); +} +.menu-section > .menu-link { + font-weight: 500; + color: var(--fg-2); + padding-left: 28px; +} +.menu-sub > .menu-link { + font-weight: 400; + color: var(--fg-3); + padding-left: 44px; + font-size: 13px; +} + +.menu-link { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + cursor: pointer; + text-decoration: none; + user-select: none; + border-radius: 4px; + margin: 1px 6px; + transition: background .12s; + line-height: 1.4; +} +.menu-link:hover { + background: var(--bg-3); + text-decoration: none; +} +.menu-link.active { + background: var(--accent-soft); + color: var(--accent-2); + font-weight: 600; +} +[data-theme="dark"] .menu-link.active { color: var(--accent); } + +.caret { + width: 12px; + text-align: center; + font-size: 10px; + color: var(--fg-3); + flex-shrink: 0; + transition: transform .15s; +} +.menu-chapter.collapsed > .menu-link .caret, +.menu-section.collapsed > .menu-link .caret { + transform: rotate(-90deg); +} + +.menu-chapter > .children, +.menu-section > .children { + overflow: hidden; + max-height: 4000px; + transition: max-height .2s ease-out; +} +.menu-chapter.collapsed > .children, +.menu-section.collapsed > .children { + max-height: 0; +} + +.menu-num { + font-family: var(--mono); + font-size: 11px; + color: var(--fg-3); + margin-right: 4px; + flex-shrink: 0; +} +.menu-link.active .menu-num { color: var(--accent-2); } + +/* ─── Content ─── */ +.content { + flex: 1; + display: flex; + flex-direction: column; + background: var(--bg); + min-width: 0; +} + +.tabs { + height: 36px; + display: flex; + border-bottom: 1px solid var(--border); + background: var(--bg-2); + padding: 0 12px; + gap: 4px; + flex-shrink: 0; +} +.tab { + padding: 0 14px; + height: 100%; + display: flex; + align-items: center; + gap: 6px; + border-bottom: 2px solid transparent; + color: var(--fg-2); + font-size: 13px; + transition: color .12s, border-color .12s; +} +.tab:hover { color: var(--fg); } +.tab.active { + color: var(--accent-2); + border-bottom-color: var(--accent); + font-weight: 500; +} +[data-theme="dark"] .tab.active { color: var(--accent); } +.tab .badge { + background: var(--bg-3); + color: var(--fg-3); + font-size: 11px; + padding: 1px 6px; + border-radius: 9px; + font-family: var(--mono); +} +.tab.active .badge { + background: var(--accent); + color: #fff; +} + +.pane { + display: none; + flex: 1; + overflow: hidden; +} +.pane.active { display: flex; flex-direction: column; } + +.pane-reader iframe { + flex: 1; + width: 100%; + border: none; + background: var(--bg); +} + +/* ─── Search results pane ─── */ +.search-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 20px; + border-bottom: 1px solid var(--border); + background: var(--bg-2); + color: var(--fg-2); + font-size: 13px; +} +.clear-btn { + padding: 4px 10px; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + color: var(--fg-2); + font-size: 12px; +} +.clear-btn:hover { background: var(--bg-3); color: var(--fg); } + +.results { + list-style: none; + margin: 0; + padding: 8px 0; + overflow-y: auto; + flex: 1; +} +.result-item { + padding: 10px 20px; + border-bottom: 1px solid var(--border); + cursor: pointer; + transition: background .1s; +} +.result-item:hover { background: var(--bg-3); } +.result-path { + font-size: 11px; + color: var(--fg-3); + font-family: var(--mono); + margin-bottom: 4px; +} +.result-title { + font-size: 14px; + font-weight: 600; + color: var(--fg); + margin-bottom: 4px; +} +.result-snippet { + font-size: 12px; + color: var(--fg-2); + line-height: 1.5; +} +.result-snippet mark { + background: var(--accent-soft); + color: var(--accent-2); + padding: 0 2px; + border-radius: 2px; + font-weight: 600; +} +[data-theme="dark"] .result-snippet mark { color: var(--accent); } + +/* ─── Responsive ─── */ +@media (max-width: 768px) { + .burger { display: flex; } + .brand .subtitle { display: none; } + .sidebar { + position: fixed; + left: 0; top: var(--top-h); bottom: 0; + z-index: 40; + transform: translateX(-100%); + transition: transform .2s; + box-shadow: var(--shadow-lg); + } + body.sidebar-open .sidebar { transform: translateX(0); } + .search-wrap { max-width: none; } +} diff --git a/docs/prd/assets/app.js b/docs/prd/assets/app.js new file mode 100644 index 0000000..03a61c6 --- /dev/null +++ b/docs/prd/assets/app.js @@ -0,0 +1,325 @@ +/* ============================================================ + LemonBoss doc-site shell — menu + hash router + search + ============================================================ */ + +(async () => { + // ── Menu (loaded synchronously via separate script tag if available) ── + await loadScript('./assets/menu.js'); + + const MENU = window.LEMONBOSS_MENU || []; + const menuEl = document.getElementById('menu'); + const readerEl = document.getElementById('reader'); + const searchEl = document.getElementById('search'); + const resultsEl = document.getElementById('results'); + const summaryEl = document.getElementById('search-summary'); + const tabBtns = document.querySelectorAll('.tab'); + const tabSearch = document.getElementById('tab-search'); + const countEl = document.getElementById('search-count'); + const burger = document.getElementById('burger'); + const themeBtn = document.getElementById('theme'); + + // ────────────────────────────────────────────── + // THEME + // ────────────────────────────────────────────── + const storedTheme = localStorage.getItem('lemonboss-theme'); + if (storedTheme) document.documentElement.dataset.theme = storedTheme; + themeBtn.addEventListener('click', () => { + const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; + document.documentElement.dataset.theme = next; + localStorage.setItem('lemonboss-theme', next); + }); + + // ────────────────────────────────────────────── + // MENU RENDER + // ────────────────────────────────────────────── + function renderMenu() { + MENU.forEach(ch => menuEl.appendChild(renderNode(ch, 'chapter'))); + } + function renderNode(node, kind) { + const wrap = document.createElement('div'); + wrap.className = `menu-${kind}`; + wrap.dataset.id = node.id; + + const link = document.createElement('a'); + link.className = 'menu-link'; + link.href = '#/' + node.href.replace(/#.*$/, '') + (node.href.includes('#') ? node.href.slice(node.href.indexOf('#')) : ''); + link.dataset.href = node.href; + + if (node.children) { + const caret = document.createElement('span'); + caret.className = 'caret'; + caret.textContent = '▾'; + caret.addEventListener('click', (e) => { + e.preventDefault(); e.stopPropagation(); + wrap.classList.toggle('collapsed'); + }); + link.appendChild(caret); + } else { + const dot = document.createElement('span'); + dot.className = 'caret'; + dot.textContent = '·'; + link.appendChild(dot); + } + + const num = document.createElement('span'); + num.className = 'menu-num'; + num.textContent = node.num; + link.appendChild(num); + + const txt = document.createElement('span'); + txt.textContent = node.title; + link.appendChild(txt); + + link.addEventListener('click', (e) => { + e.preventDefault(); + navigateTo(node.href); + if (window.innerWidth < 768) document.body.classList.remove('sidebar-open'); + }); + + wrap.appendChild(link); + + if (node.children) { + const cs = document.createElement('div'); + cs.className = 'children'; + const childKind = kind === 'chapter' ? 'section' : 'sub'; + node.children.forEach(c => cs.appendChild(renderNode(c, childKind))); + wrap.appendChild(cs); + } + return wrap; + } + + function highlightMenu(href) { + document.querySelectorAll('.menu-link').forEach(a => a.classList.remove('active')); + const target = stripHash(href); + let best = null; + document.querySelectorAll('.menu-link').forEach(a => { + const ah = a.dataset.href || ''; + if (stripHash(ah) === target) { + if (!best || (ah.includes('#') && !best.dataset.href.includes('#'))) best = a; + if (ah === href) best = a; // exact match wins + } + }); + if (best) { + best.classList.add('active'); + // expand all ancestors + let p = best.parentElement; + while (p && p !== menuEl) { + if (p.classList.contains('collapsed')) p.classList.remove('collapsed'); + p = p.parentElement; + } + best.scrollIntoView({ block: 'nearest' }); + } + } + const stripHash = (h) => h.split('#')[0]; + + // ────────────────────────────────────────────── + // HASH ROUTER + // ────────────────────────────────────────────── + function navigateTo(href) { + location.hash = '#/' + href; + } + function onHashChange() { + const raw = location.hash.replace(/^#\//, ''); + const href = raw || '01-overview/index.html'; + readerEl.src = './chapters/' + href; + highlightMenu(href); + // switch back to reader pane + switchTab('reader'); + } + window.addEventListener('hashchange', onHashChange); + + // ────────────────────────────────────────────── + // TABS + // ────────────────────────────────────────────── + function switchTab(name) { + tabBtns.forEach(b => b.classList.toggle('active', b.dataset.pane === name)); + document.querySelectorAll('.pane').forEach(p => + p.classList.toggle('active', p.classList.contains('pane-' + name)) + ); + } + tabBtns.forEach(b => b.addEventListener('click', () => switchTab(b.dataset.pane))); + + // ────────────────────────────────────────────── + // SEARCH + // ─ Loads search-index.json (built by scripts/build-search-index.js) + // ─ Tokenization: lowercase, split on whitespace+punct, + Chinese bigram + // ────────────────────────────────────────────── + let INDEX = null; + async function loadIndex() { + if (INDEX) return INDEX; + try { + const r = await fetch('./assets/search-index.json'); + INDEX = await r.json(); + } catch (e) { + INDEX = { docs: [], invIndex: {} }; + console.warn('[search] no index available — run scripts/build-search-index.js'); + } + return INDEX; + } + + function tokenize(text) { + const tokens = []; + const lower = text.toLowerCase(); + // ASCII words / digits + (lower.match(/[a-z0-9_]+/g) || []).forEach(w => tokens.push(w)); + // CJK bigrams (一-鿿 plus extensions) + const cjk = lower.match(/[㐀-鿿]+/g) || []; + cjk.forEach(run => { + if (run.length === 1) tokens.push(run); + else { + for (let i = 0; i < run.length - 1; i++) tokens.push(run.slice(i, i + 2)); + } + }); + return tokens; + } + + async function runSearch(query) { + if (!query || query.trim().length === 0) { + resultsEl.innerHTML = ''; + summaryEl.textContent = '输入关键词开始搜索'; + countEl.textContent = '0'; + return; + } + const idx = await loadIndex(); + const terms = tokenize(query); + if (terms.length === 0) { + resultsEl.innerHTML = ''; + summaryEl.textContent = `「${query}」无可识别 token`; + countEl.textContent = '0'; + return; + } + + // score docs by term frequency sum + const scores = new Map(); + let hitDocs = null; + for (const t of terms) { + const posting = idx.invIndex[t]; + if (!posting) continue; + const docs = new Set(); + for (const [docId, tf] of posting) { + scores.set(docId, (scores.get(docId) || 0) + tf); + docs.add(docId); + } + hitDocs = hitDocs ? new Set([...hitDocs].filter(d => docs.has(d))) : docs; + } + // fallback: if intersection empty, use union + const candidates = (hitDocs && hitDocs.size > 0) ? [...hitDocs] : [...scores.keys()]; + candidates.sort((a, b) => scores.get(b) - scores.get(a)); + const top = candidates.slice(0, 50); + + renderResults(top, query, idx); + } + + function renderResults(docIds, query, idx) { + resultsEl.innerHTML = ''; + const docs = idx.docs; + if (docIds.length === 0) { + summaryEl.textContent = `「${query}」没有命中`; + countEl.textContent = '0'; + return; + } + summaryEl.textContent = `「${query}」命中 ${docIds.length} 条`; + countEl.textContent = String(docIds.length); + + docIds.forEach(id => { + const d = docs[id]; + if (!d) return; + const li = document.createElement('li'); + li.className = 'result-item'; + li.innerHTML = ` +
LemonBoss 是一个 多智能体自主开发系统 —— 用户像"开公司"一样组织 AI Agent:创建公司、招聘成员、设定 OKR、编排 Pipeline、追踪任务时间线,最终让 Agent 自主把目标推进到落地。
+ +server/agent-server.js 入口及其挂载的
+ /api/agent /api/company /api/goals /api/tasks 路由族 + 9 个前端页面)。
+ 历史品牌 ArenaSK 的 Skill 市场 / Arena ELO / Benchmark / Master Agent 不在本文档范围。
+ 占位中。完整内容将在下一轮章节生成时由 subagent 并行写入。
+ +占位中。
+ +占位中。
+ +占位中。
+本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。本章占位中 — 完整内容将在下一阶段生成。
+docs/prd/_scan-manifest.json。