feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: add runAgent functionality to execute agent commands and update… - #6

Open
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery
Open

feat: add runAgent functionality to execute agent commands and update…#6
ducheharsh wants to merge 1 commit into
mainfrom
feat/agent-gallery

Conversation

@ducheharsh

Copy link
Copy Markdown
Contributor

This pull request introduces support for tracking and updating user credit balances when interacting with agents, as well as a new endpoint for running agents. The main changes include schema updates for user credits, a new API route and controller method for running agents, and logic to decrement user credits based on agent usage.

User credit management:

  • Added creditBalance and creditBalanceLastUpdated fields to the user table and Prisma schema, with defaults set for new users. [1][2]

Agent execution and credit deduction:

  • Implemented runAgent method in AgentService to execute an agent, calculate credit usage based on agent costs and token usage, and update the user's credit balance accordingly.
  • Added runAgent method to AgentController and exposed a new POST endpoint /agents/:id/run in agent.route.ts for running agents. [1][2]

Agent CRUD improvements:

  • Updated agent deletion and lookup logic to ensure operations are only performed by the agent's owner by matching both id and userId. [1][2]

Type consistency and validation:

  • Improved handling of agent cost fields by ensuring numeric conversion and string consistency in controller logic. [1][2]… user credit balance

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a credit-based usage system for agent execution, enabling users to run agents with automatic cost calculation and balance deduction. It introduces a new /agents/:id/run endpoint, schema changes to track user credit balances, and updates to agent ownership validation in deletion operations.

  • Added credit balance tracking with creditBalance (default 100) and creditBalanceLastUpdated fields to the user table
  • Implemented runAgent functionality with cost calculation based on agent base cost plus input/output token costs
  • Enhanced agent deletion security by attempting compound key filtering (though this has implementation issues)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
prisma/schema.prismaAdded creditBalance and creditBalanceLastUpdated fields to user model for tracking credit usage
prisma/migrations/20251127123756_ag_credits/migration.sqlDatabase migration adding credit balance columns with default values
src/services/agent.service.tsAdded runAgent method for agent execution and credit deduction; updated deleteAgent with compound where clauses
src/controllers/agent.controller.tsAdded runAgent controller method with basic error handling; improved type handling for agentCost
src/routes/agent.route.tsExposed new POST endpoint /agents/:id/run for running agents

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

try {
const { deployedUrl, default_agent_name } =(await c.req.json()) as IAgentNameVerification;
const agent_id = c.req.param('id');
const { message } = await c.req.json();

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for required parameters. The agent_id and message parameters are not validated before use. Add validation to ensure these required fields exist and are non-empty strings, similar to other endpoints like deleteAgent:

if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){returnc.json(api_response({message: "agent_id is required and must be a non-empty string",is_error: true}),400);}if(!message||typeofmessage!=='string'||message.trim().length===0){returnc.json(api_response({message: "message is required and must be a non-empty string",is_error: true}),400);}
Suggested change
const{ message }=awaitc.req.json();
const{ message }=awaitc.req.json();
// Validate agent_id
if(!agent_id||typeofagent_id!=='string'||agent_id.trim().length===0){
returnc.json(
api_response({
message: "agent_id is required and must be a non-empty string",
is_error: true
}),
400
);
}
// Validate message
if(!message||typeofmessage!=='string'||message.trim().length===0){
returnc.json(
api_response({
message: "message is required and must be a non-empty string",
is_error: true
}),
400
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +132
await prisma.user.update({
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),
},
creditBalanceLastUpdated: new Date(),
},
});

const user = await prisma.user.findUnique({
where: { id: user_id },
});
if (!user) {
throw new Error("User not found");
}

return {
response: response.response_content,
creditBalance: user.creditBalance,
};

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk of race condition in credit balance update. The credit balance is read after the agent call and update operation, which could lead to stale data if multiple concurrent requests are made. The balance could be decremented multiple times before the user query returns the updated value. Consider using an atomic transaction or returning the updated user data from the update operation:

constupdatedUser=awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: /* credit calculation */,},creditBalanceLastUpdated: newDate(),},});return{response: response.response_content,creditBalance: updatedUser.creditBalance,};

Copilot uses AI. Check for mistakes.
where: { id: user_id },
data: {
creditBalance: {
decrement: Number(agent.agentCost) + (agent.inputTokenCost > 0 ? Number(agent.inputTokenCost) * (response.input_tokens || 0) : 0) + (agent.outputTokenCost > 0 ? Number(agent.outputTokenCost) * (response.output_tokens || 0) : 0),

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex credit calculation embedded in update statement reduces maintainability. The inline credit calculation is difficult to read, debug, and test. Extract this calculation into a separate, testable function:

constcalculateAgentCost=(agent: {agentCost: string;inputTokenCost: number;outputTokenCost: number},response: {input_tokens: number|null;output_tokens: number|null}): number=>{constbaseCost=Number(agent.agentCost);constinputCost=agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*(response.input_tokens||0) : 0;constoutputCost=agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*(response.output_tokens||0) : 0;returnbaseCost+inputCost+outputCost;};// Then use it in the update:consttotalCost=calculateAgentCost(agent,response);awaitprisma.user.update({where: {id: user_id},data: {creditBalance: {decrement: totalCost},creditBalanceLastUpdated: newDate(),},});

Copilot uses AI. Check for mistakes.
});
if (!agent) {
throw new Error("Agent not found");
}

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing authorization check for agent ownership. The method doesn't verify that the user is authorized to run this agent. If the agent is private (not public), only the owner should be able to run it, or there should be proper access control. Add a check similar to deleteAgent:

constagent=awaitprisma.agent.findUnique({where: {id: agent_id},});if(!agent){thrownewError("Agent not found");}// Check if user can access this agentif(!agent.isPublic&&agent.userId!==user_id){thrownewError("Unauthorized to run this agent");}
Suggested change
}
}
// Check if user can access this agent
if(!agent.isPublic&&agent.userId!==user_id){
thrownewError("Unauthorized to run this agent");
}

Copilot uses AI. Check for mistakes.
const agent_id = c.req.param('id');
const { message } = await c.req.json();
const user = await c.get('user');
const agent = await AgentService.runAgent(agent_id.trim(), message.trim(), user?.id);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing user authentication check. The user object retrieved from context may be undefined, but there's no validation before accessing user?.id. This could result in undefined being passed to AgentService.runAgent(). Add a check similar to other endpoints (e.g., createAgent, deleteAgent) that verifies the user is authenticated before proceeding:

constuser=awaitc.get('user');if(!user||!user.id){returnc.json(api_response({message: "User authentication required",is_error: true}),401);}
Suggested change
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user?.id);
if(!user||!user.id){
returnc.json(
api_response({
message: "User authentication required",
is_error: true
}),
401
);
}
constagent=awaitAgentService.runAgent(agent_id.trim(),message.trim(),user.id);

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +110
public static readonly runAgent = async (agent_id: string, message: string, user_id: string) => {
const agent = await prisma.agent.findUnique({
where: { id: agent_id },
});
if (!agent) {
throw new Error("Agent not found");
}
const session_id = crypto.randomUUID();

const response = await callProxiedAgent(
agent.deployedUrl,
agent.default_agent_name || "",
(agent.framework_used as AgentFrameWorks) || AgentFrameWorks.google_adk,
message,
session_id,
user_id
);

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for sufficient credit balance before running the agent. The service will decrement the user's credit balance without first verifying that they have sufficient credits. This could result in negative credit balances. Add a check to ensure the user has enough credits before calling the agent:

constuser=awaitprisma.user.findUnique({where: {id: user_id},});if(!user){thrownewError("User not found");}constestimatedCost=Number(agent.agentCost)+(agent.inputTokenCost>0 ? Number(agent.inputTokenCost)*1000 : 0)+// Estimate max tokens(agent.outputTokenCost>0 ? Number(agent.outputTokenCost)*1000 : 0);if(user.creditBalance<estimatedCost){thrownewError("Insufficient credit balance");}

Copilot uses AI. Check for mistakes.
// First check if the agent exists and user has permission
const existingAgent = await prisma.agent.findUnique({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Prisma's where clause on findUnique requires a unique constraint, but the agent model only has id as a unique field, not a compound (id, userId) constraint. This will cause a runtime error. The authorization check on line 319 is already sufficient, so the where clause should only use id:

constexistingAgent=awaitprisma.agent.findUnique({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.

const agent = await prisma.agent.delete({
where: { id: agent_id },
where: { id: agent_id, userId: user_id },

CopilotAINov 27, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Prisma where clause. Same issue as in findUnique above - Prisma's delete requires a unique constraint, but (id, userId) is not a compound unique key. Use only id:

constagent=awaitprisma.agent.delete({where: {id: agent_id},});

Copilot uses AI. Check for mistakes.
@ducheharsh

ducheharsh commented Nov 27, 2025

Copy link
Copy Markdown
ContributorAuthor

resolves #1

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ducheharsh