feat: Add agent framework with agent creator, listing, and runner#9
feat: Add agent framework with agent creator, listing, and runner#9saifsoub wants to merge 3 commits into
Conversation
- Add AgentConfig types with tools, input fields, and system prompts - Add agent CRUD operations to store (getAgents, addAgent, updateAgent, deleteAgent) - Create predefined action system (search_empire, create_opportunity, etc.) - Add generic agent runner API with Anthropic tool-use loop - Add agent listing page with built-in and custom agent sections - Add agent creator page with full form for tools, inputs, and prompts - Add agent runner page with dynamic form, trace, and output display - Seed 5 built-in agents: Content Strategist, Opportunity Scorer, Decision Advisor, Lead Qualifier, Asset Builder - Add Agents to sidebar navigation Co-authored-by: Seif <saifsoub@users.noreply.github.com>
Co-authored-by: Seif <saifsoub@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
❌ Deploy Preview for personalcommandcenter failed.
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive AI agent framework, featuring UI components for agent management, API routes for execution via the Anthropic SDK, and a tool-calling system integrated with the local JSON store. Critical feedback identifies an invalid Anthropic model name and a bug where agent output is overwritten instead of concatenated. Further improvements address potential runtime errors in decision option parsing, regex safety in message templating, and a formatting error in the seeded database options.
|
|
||
| for (let turn = 0; turn < 10; turn++) { | ||
| const response = await client.messages.create({ | ||
| model: "claude-sonnet-4-6", |
There was a problem hiding this comment.
| for (const block of response.content) { | ||
| assistantContent.push(block); | ||
| if (block.type === "text") { | ||
| finalText = block.text; |
There was a problem hiding this comment.
The finalText variable is being overwritten in each iteration. If the assistant returns multiple text blocks (for example, some introductory text followed by a tool call and then a concluding summary), only the last text block will be captured in the final output. It should be concatenated instead.
| finalText = block.text; | |
| finalText += block.text; |
| return analyzeDecision({ | ||
| title: (input.title as string) ?? "", | ||
| context: (input.context as string) ?? "", | ||
| options: (input.options as string[]) ?? [], |
There was a problem hiding this comment.
The options parameter for analyze_decision is expected to be an array of strings. However, the tool definition in demo-db.json specifies it as a string (containing a JSON array). At runtime, if the LLM sends a string, the cast as string[] will not convert it, and subsequent array access (like options[0] in analyzeDecision) will return individual characters. It is safer to parse the string if it is not already an array.
| options: (input.options as string[]) ?? [], | |
| options: typeof input.options === "string" ? JSON.parse(input.options) : (input.options as string[]), |
| let msg = template; | ||
| for (const [key, value] of Object.entries(input)) { | ||
| msg = msg.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(value ?? "")); | ||
| } | ||
| return msg; |
There was a problem hiding this comment.
Using new RegExp with user-provided keys can lead to errors if keys contain special regex characters. Additionally, sequential replacement can lead to unexpected results if a value contains a placeholder for another key (e.g., if one value is {{other_key}}). A single-pass replacement using a global regex is safer and more robust.
| let msg = template; | |
| for (const [key, value] of Object.entries(input)) { | |
| msg = msg.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(value ?? "")); | |
| } | |
| return msg; | |
| return template.replace(/{{(.*?)}}/g, (_, key) => String(input[key] ?? "")); |
| "placeholder": "", | ||
| "required": false, | ||
| "options": [ | ||
| "ThisweekThismonthThisquarter" |
There was a problem hiding this comment.
Simple conflict:
- sidebar.tsx: both sides added Bot import and Agents nav item (identical intent, took ours)
Conflicting intent — accepted main's versions:
- lib/types.ts: main replaced AgentConfig with StoredAgent + approval/run types
- lib/store.ts: main rewrote persistence to Netlify Blobs with new agent CRUD
- app/agents/page.tsx: main replaced our card-based UI with integrated editor/runner
- app/api/agents/route.ts: main uses { ok, agents } envelope + createAgent
- app/api/agents/[id]/route.ts: main uses archiveAgent instead of deleteAgent
Co-authored-by: Seif <saifsoub@users.noreply.github.com>
Summary
Adds a complete agent framework to Personal Empire OS, including an agent creator UI, agent listing page, and dynamic agent runner — enabling users to build, manage, and run custom AI agents.
What's new
Agent Infrastructure
AgentConfigtype system with tools, input fields, system prompts, and user message templatesgetAgents,addAgent,updateAgent,deleteAgent)/api/agent/run) with Anthropic Claude tool-use loop and mock fallbackPages & UI
/agents— Agent listing page with built-in and custom agent sections/agents/create— Full agent creator with form for identity, system prompt, input fields, and tool definitions/agents/[id]— Dynamic agent runner page with input form, real-time trace, and output display5 Built-in Agents (seeded)
Demo
demo_agent_creator_and_runner.mp4
Creating a custom "Market Pulse Reporter" agent and running the built-in Content Strategist agent.
Agents listing page
Custom agent created
Agent runner with trace output
Testing
npm run typecheck)npm test)To show artifacts inline, enable in settings.