From d8b47e30c447b9cac08b1b9727e1ee4d977170db Mon Sep 17 00:00:00 2001 From: Vaayne Date: Fri, 19 Sep 2025 11:47:27 +0800 Subject: [PATCH 1/4] feat(session messages): implement user message persistence and retrieve last agent session ID --- .../agents/services/SessionMessageService.ts | 84 +++++++++++-------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/src/main/services/agents/services/SessionMessageService.ts b/src/main/services/agents/services/SessionMessageService.ts index b0a08e6efb..b3245edbc2 100644 --- a/src/main/services/agents/services/SessionMessageService.ts +++ b/src/main/services/agents/services/SessionMessageService.ts @@ -9,7 +9,7 @@ import type { } from '@types' import { ModelMessage, UIMessage, UIMessageChunk } from 'ai' import { convertToModelMessages, readUIMessageStream } from 'ai' -import { eq } from 'drizzle-orm' +import { desc, eq } from 'drizzle-orm' import { BaseService } from '../BaseService' import { InsertSessionMessageRow, sessionMessagesTable } from '../database/schema' @@ -170,29 +170,6 @@ export class SessionMessageService extends BaseService { return { messages } } - async saveUserMessage( - tx: any, - sessionId: string, - prompt: string, - agentSessionId: string - ): Promise { - this.ensureInitialized() - - const now = new Date().toISOString() - const insertData: InsertSessionMessageRow = { - session_id: sessionId, - role: 'user', - content: prompt, - agent_session_id: agentSessionId, - created_at: now, - updated_at: now - } - - const [saved] = await tx.insert(sessionMessagesTable).values(insertData).returning() - - return this.deserializeSessionMessage(saved) as AgentSessionMessageEntity - } - createSessionMessage(session: GetAgentSessionResponse, messageData: CreateSessionMessageRequest): EventEmitter { this.ensureInitialized() @@ -210,12 +187,8 @@ export class SessionMessageService extends BaseService { req: CreateSessionMessageRequest, sessionStream: EventEmitter ): Promise { - const previousMessages = session.messages || [] - let agentSessionId: string = '' - if (previousMessages.length > 0) { - agentSessionId = previousMessages[previousMessages.length - 1].agent_session_id - } - + const agentSessionId = await this.getLastAgentSessionId(session.id) + let newAgentSessionId = '' logger.debug('Session Message stream message data:', { message: req, session_id: agentSessionId }) if (session.agent_type !== 'claude-code') { @@ -223,7 +196,6 @@ export class SessionMessageService extends BaseService { logger.error('Unsupported agent type for streaming:', { agent_type: session.agent_type }) throw new Error('Unsupported agent type for streaming') } - let newAgentSessionId = '' // Create the streaming agent invocation (using invokeStream for streaming) const claudeStream = this.cc.invoke(req.content, session.accessible_paths[0], agentSessionId, { @@ -273,8 +245,8 @@ export class SessionMessageService extends BaseService { case 'complete': { // Then handle async persistence this.database.transaction(async (tx) => { - await this.saveUserMessage(tx, session.id, req.content, newAgentSessionId) - await this.persistSessionMessageAsync({ + await this.persistUserMessage(tx, session.id, req.content, newAgentSessionId) + await this.persistAssistantMessage({ tx, session, accumulator, @@ -304,7 +276,51 @@ export class SessionMessageService extends BaseService { }) } - private async persistSessionMessageAsync({ + private async getLastAgentSessionId(sessionId: string): Promise { + this.ensureInitialized() + + try { + const result = await this.database + .select({ agent_session_id: sessionMessagesTable.agent_session_id }) + .from(sessionMessagesTable) + .where(eq(sessionMessagesTable.session_id, sessionId)) + .orderBy(desc(sessionMessagesTable.created_at)) + .limit(1) + + return result[0]?.agent_session_id || '' + } catch (error) { + logger.error('Failed to get last agent session ID', { + sessionId, + error + }) + return '' + } + } + + async persistUserMessage( + tx: any, + sessionId: string, + prompt: string, + agentSessionId: string + ): Promise { + this.ensureInitialized() + + const now = new Date().toISOString() + const insertData: InsertSessionMessageRow = { + session_id: sessionId, + role: 'user', + content: prompt, + agent_session_id: agentSessionId, + created_at: now, + updated_at: now + } + + const [saved] = await tx.insert(sessionMessagesTable).values(insertData).returning() + + return this.deserializeSessionMessage(saved) as AgentSessionMessageEntity + } + + private async persistAssistantMessage({ tx, session, accumulator, From da3cd624861d933524c2d7c191359d2eb08f56c6 Mon Sep 17 00:00:00 2001 From: Vaayne Date: Fri, 19 Sep 2025 11:47:34 +0800 Subject: [PATCH 2/4] feat(sessions): update session creation and update requests with new session details --- tests/apis/agents/sessions.http | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/tests/apis/agents/sessions.http b/tests/apis/agents/sessions.http index 95660ae4eb..5de78ee114 100644 --- a/tests/apis/agents/sessions.http +++ b/tests/apis/agents/sessions.http @@ -2,7 +2,7 @@ @host=http://localhost:23333 @token=cs-sk-af798ed4-7cf5-4fd7-ae4b-df203b164194 @agent_id=agent_1758092281575_tn9dxio9k -@session_id=session_1758092305477_b0g0cmnkp +@session_id=session_1758252305914_9kef8yven ### List Sessions GET {{host}}/v1/agents/{{agent_id}}/sessions @@ -15,7 +15,14 @@ POST {{host}}/v1/agents/{{agent_id}}/sessions Authorization: Bearer {{token}} Content-Type: application/json -{} +{ + "name": "Story Writing Session 1", + "instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories.", + "model": "anthropic:claude-sonnet-4", + "accessible_paths": [ + "~/Documents/stories" + ] +} ### Get Session Details GET {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}} @@ -27,14 +34,28 @@ DELETE {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}} Authorization: Bearer {{token}} Content-Type: application/json -### Update Session +### Full Update Session PUT {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}} Authorization: Bearer {{token}} Content-Type: application/json { - "name": "Code Review Session 1", - "instructions": "Review the newly implemented feature for bugs and improvements" + "name": "Story Writing Session 2", + "instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories.", + "model": "anthropic:claude-sonnet-4", + "accessible_paths": [ + "~/Documents/stories" + ] +} + + +### Partial Update Session +PATCH {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}} +Authorization: Bearer {{token}} +Content-Type: application/json + +{ + "instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories. Focus on character development and plot structure.", } @@ -44,5 +65,5 @@ Authorization: Bearer {{token}} Content-Type: application/json { - "content": "a joke about programmers" + "content": "Write a short story about a robot learning to love." } From 5386716ebe6e40c18cef9c4baf10a6cab9a1dfba Mon Sep 17 00:00:00 2001 From: Vaayne Date: Fri, 19 Sep 2025 12:55:51 +0800 Subject: [PATCH 3/4] feat(session messages): enhance session message persistence with improved error handling and completion notifications --- .../agents/services/SessionMessageService.ts | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/main/services/agents/services/SessionMessageService.ts b/src/main/services/agents/services/SessionMessageService.ts index b3245edbc2..df9cf0a69f 100644 --- a/src/main/services/agents/services/SessionMessageService.ts +++ b/src/main/services/agents/services/SessionMessageService.ts @@ -233,30 +233,55 @@ export class SessionMessageService extends BaseService { sessionStream.emit('data', { type: 'error', - error: serializeError(underlyingError) + error: serializeError(underlyingError), + persistScheduled: false }) // Always emit a finish chunk at the end sessionStream.emit('data', { - type: 'finish' + type: 'finish', + persistScheduled: false }) break } case 'complete': { - // Then handle async persistence - this.database.transaction(async (tx) => { - await this.persistUserMessage(tx, session.id, req.content, newAgentSessionId) - await this.persistAssistantMessage({ - tx, - session, - accumulator, - agentSessionId: newAgentSessionId - }) - }) - // Always emit a finish chunk at the end + const completionPayload = event.result ?? accumulator.toModelMessage('assistant') + sessionStream.emit('data', { - type: 'finish' + type: 'complete', + result: completionPayload }) + + try { + const persisted = await this.database.transaction(async (tx) => { + const userMessage = await this.persistUserMessage(tx, session.id, req.content, newAgentSessionId) + const assistantMessage = await this.persistAssistantMessage({ + tx, + session, + accumulator, + agentSessionId: newAgentSessionId + }) + + return { userMessage, assistantMessage } + }) + + sessionStream.emit('data', { + type: 'persisted', + message: persisted.assistantMessage, + userMessage: persisted.userMessage + }) + } catch (persistError) { + sessionStream.emit('data', { + type: 'persist-error', + error: serializeError(persistError) + }) + } finally { + // Always emit a finish chunk at the end + sessionStream.emit('data', { + type: 'finish', + persistScheduled: true + }) + } break } @@ -330,11 +355,11 @@ export class SessionMessageService extends BaseService { session: GetAgentSessionResponse accumulator: ChunkAccumulator agentSessionId: string - }) { + }): Promise { if (!session?.id) { const missingSessionError = new Error('Missing session_id for persisted message') logger.error('error persisting session message', { error: missingSessionError }) - return + throw missingSessionError } const sessionId = session.id @@ -356,10 +381,13 @@ export class SessionMessageService extends BaseService { updated_at: now } - await tx.insert(sessionMessagesTable).values(insertData).returning() + const [saved] = await tx.insert(sessionMessagesTable).values(insertData).returning() logger.debug('Success Persisted session message') + + return this.deserializeSessionMessage(saved) as AgentSessionMessageEntity } catch (error) { logger.error('Failed to persist session message', { error }) + throw error } } From d13c25444cec2d1a69943bb58ea0b0a91c6f2447 Mon Sep 17 00:00:00 2001 From: Vaayne Date: Fri, 19 Sep 2025 12:55:58 +0800 Subject: [PATCH 4/4] feat(agents, sessions): enhance agent and session schemas with detailed properties and CRUD API documentation --- src/main/apiServer/routes/agents/index.ts | 901 +++++++++++++++++++--- 1 file changed, 804 insertions(+), 97 deletions(-) diff --git a/src/main/apiServer/routes/agents/index.ts b/src/main/apiServer/routes/agents/index.ts index 4bd249b474..5d3393dc12 100644 --- a/src/main/apiServer/routes/agents/index.ts +++ b/src/main/apiServer/routes/agents/index.ts @@ -22,76 +22,114 @@ const agentsRouter = express.Router() * @swagger * components: * schemas: + * PermissionMode: + * type: string + * enum: [default, acceptEdits, bypassPermissions, plan] + * description: Permission mode for agent operations + * + * AgentType: + * type: string + * enum: [claude-code] + * description: Type of agent + * + * AgentConfiguration: + * type: object + * properties: + * permission_mode: + * $ref: '#/components/schemas/PermissionMode' + * default: default + * max_turns: + * type: integer + * default: 10 + * description: Maximum number of interaction turns + * additionalProperties: true + * + * AgentBase: + * type: object + * properties: + * name: + * type: string + * description: Agent name + * description: + * type: string + * description: Agent description + * accessible_paths: + * type: array + * items: + * type: string + * description: Array of directory paths the agent can access + * instructions: + * type: string + * description: System prompt/instructions + * model: + * type: string + * description: Main model ID + * plan_model: + * type: string + * description: Optional planning model ID + * small_model: + * type: string + * description: Optional small/fast model ID + * mcps: + * type: array + * items: + * type: string + * description: Array of MCP tool IDs + * allowed_tools: + * type: array + * items: + * type: string + * description: Array of allowed tool IDs (whitelist) + * configuration: + * $ref: '#/components/schemas/AgentConfiguration' + * required: + * - model + * - accessible_paths + * * AgentEntity: - * type: object - * properties: - * id: - * type: string - * description: Unique agent identifier - * name: - * type: string - * description: Agent name - * description: - * type: string - * description: Agent description - * avatar: - * type: string - * description: Agent avatar URL - * instructions: - * type: string - * description: System prompt/instructions - * model: - * type: string - * description: Main model ID - * plan_model: - * type: string - * description: Optional planning model ID - * small_model: - * type: string - * description: Optional small/fast model ID - * built_in_tools: - * type: array - * items: - * type: string - * description: Built-in tool IDs - * mcps: - * type: array - * items: - * type: string - * description: MCP tool IDs - * knowledges: - * type: array - * items: - * type: string - * description: Knowledge base IDs - * configuration: - * type: object - * description: Extensible settings - * accessible_paths: - * type: array - * items: - * type: string - * description: Accessible directory paths - * permission_mode: - * type: string - * enum: [readOnly, acceptEdits, bypassPermissions] - * description: Permission mode - * max_steps: - * type: integer - * description: Maximum steps the agent can take - * created_at: - * type: string - * format: date-time - * updated_at: - * type: string - * format: date-time - * required: - * - id - * - name - * - model - * - created_at - * - updated_at + * allOf: + * - $ref: '#/components/schemas/AgentBase' + * - type: object + * properties: + * id: + * type: string + * description: Unique agent identifier + * type: + * $ref: '#/components/schemas/AgentType' + * created_at: + * type: string + * format: date-time + * description: ISO timestamp of creation + * updated_at: + * type: string + * format: date-time + * description: ISO timestamp of last update + * required: + * - id + * - type + * - created_at + * - updated_at * CreateAgentRequest: + * allOf: + * - $ref: '#/components/schemas/AgentBase' + * - type: object + * properties: + * type: + * $ref: '#/components/schemas/AgentType' + * name: + * type: string + * minLength: 1 + * description: Agent name (required) + * model: + * type: string + * minLength: 1 + * description: Main model ID (required) + * required: + * - type + * - name + * - model + * + * UpdateAgentRequest: * type: object * properties: * name: @@ -100,9 +138,11 @@ const agentsRouter = express.Router() * description: * type: string * description: Agent description - * avatar: - * type: string - * description: Agent avatar URL + * accessible_paths: + * type: array + * items: + * type: string + * description: Array of directory paths the agent can access * instructions: * type: string * description: System prompt/instructions @@ -115,53 +155,409 @@ const agentsRouter = express.Router() * small_model: * type: string * description: Optional small/fast model ID - * built_in_tools: - * type: array - * items: - * type: string - * description: Built-in tool IDs * mcps: * type: array * items: * type: string - * description: MCP tool IDs - * knowledges: + * description: Array of MCP tool IDs + * allowed_tools: * type: array * items: * type: string - * description: Knowledge base IDs + * description: Array of allowed tool IDs (whitelist) * configuration: - * type: object - * description: Extensible settings + * $ref: '#/components/schemas/AgentConfiguration' + * description: Partial update - all fields are optional + * + * ReplaceAgentRequest: + * $ref: '#/components/schemas/AgentBase' + * + * SessionEntity: + * allOf: + * - $ref: '#/components/schemas/AgentBase' + * - type: object + * properties: + * id: + * type: string + * description: Unique session identifier + * agent_id: + * type: string + * description: Primary agent ID for the session + * agent_type: + * $ref: '#/components/schemas/AgentType' + * created_at: + * type: string + * format: date-time + * description: ISO timestamp of creation + * updated_at: + * type: string + * format: date-time + * description: ISO timestamp of last update + * required: + * - id + * - agent_id + * - agent_type + * - created_at + * - updated_at + * + * CreateSessionRequest: + * allOf: + * - $ref: '#/components/schemas/AgentBase' + * - type: object + * properties: + * model: + * type: string + * minLength: 1 + * description: Main model ID (required) + * required: + * - model + * + * UpdateSessionRequest: + * type: object + * properties: + * name: + * type: string + * description: Session name + * description: + * type: string + * description: Session description * accessible_paths: * type: array * items: * type: string - * description: Accessible directory paths - * permission_mode: + * description: Array of directory paths the agent can access + * instructions: * type: string - * enum: [readOnly, acceptEdits, bypassPermissions] - * description: Permission mode - * max_steps: - * type: integer - * description: Maximum steps the agent can take + * description: System prompt/instructions + * model: + * type: string + * description: Main model ID + * plan_model: + * type: string + * description: Optional planning model ID + * small_model: + * type: string + * description: Optional small/fast model ID + * mcps: + * type: array + * items: + * type: string + * description: Array of MCP tool IDs + * allowed_tools: + * type: array + * items: + * type: string + * description: Array of allowed tool IDs (whitelist) + * configuration: + * $ref: '#/components/schemas/AgentConfiguration' + * description: Partial update - all fields are optional + * + * ReplaceSessionRequest: + * allOf: + * - $ref: '#/components/schemas/AgentBase' + * - type: object + * properties: + * model: + * type: string + * minLength: 1 + * description: Main model ID (required) + * required: + * - model + * + * CreateSessionMessageRequest: + * type: object + * properties: + * content: + * type: string + * minLength: 1 + * description: Message content * required: - * - name - * - model + * - content + * + * PaginationQuery: + * type: object + * properties: + * limit: + * type: integer + * minimum: 1 + * maximum: 100 + * default: 20 + * description: Number of items to return + * offset: + * type: integer + * minimum: 0 + * default: 0 + * description: Number of items to skip + * status: + * type: string + * enum: [idle, running, completed, failed, stopped] + * description: Filter by session status + * + * ListAgentsResponse: + * type: object + * properties: + * agents: + * type: array + * items: + * $ref: '#/components/schemas/AgentEntity' + * total: + * type: integer + * description: Total number of agents + * limit: + * type: integer + * description: Number of items returned + * offset: + * type: integer + * description: Number of items skipped + * required: + * - agents + * - total + * - limit + * - offset + * + * ListSessionsResponse: + * type: object + * properties: + * sessions: + * type: array + * items: + * $ref: '#/components/schemas/SessionEntity' + * total: + * type: integer + * description: Total number of sessions + * limit: + * type: integer + * description: Number of items returned + * offset: + * type: integer + * description: Number of items skipped + * required: + * - sessions + * - total + * - limit + * - offset + * + * ErrorResponse: + * type: object + * properties: + * error: + * type: object + * properties: + * message: + * type: string + * description: Error message + * type: + * type: string + * description: Error type + * code: + * type: string + * description: Error code + * required: + * - message + * - type + * - code + * required: + * - error */ +/** + * @swagger + * /api/agents: + * post: + * summary: Create a new agent + * tags: [Agents] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateAgentRequest' + * responses: + * 201: + * description: Agent created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/AgentEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ // Agent CRUD routes agentsRouter.post('/', validateAgent, handleValidationErrors, agentHandlers.createAgent) + +/** + * @swagger + * /api/agents: + * get: + * summary: List all agents with pagination + * tags: [Agents] + * parameters: + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * maximum: 100 + * default: 20 + * description: Number of agents to return + * - in: query + * name: offset + * schema: + * type: integer + * minimum: 0 + * default: 0 + * description: Number of agents to skip + * - in: query + * name: status + * schema: + * type: string + * enum: [idle, running, completed, failed, stopped] + * description: Filter by agent status + * responses: + * 200: + * description: List of agents + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ListAgentsResponse' + */ agentsRouter.get('/', validatePagination, handleValidationErrors, agentHandlers.listAgents) + +/** + * @swagger + * /api/agents/{agentId}: + * get: + * summary: Get agent by ID + * tags: [Agents] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * responses: + * 200: + * description: Agent details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/AgentEntity' + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ agentsRouter.get('/:agentId', validateAgentId, handleValidationErrors, agentHandlers.getAgent) -agentsRouter.put( - '/:agentId', - validateAgentId, - validateAgentReplace, - handleValidationErrors, - agentHandlers.updateAgent -) +/** + * @swagger + * /api/agents/{agentId}: + * put: + * summary: Replace agent (full update) + * tags: [Agents] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ReplaceAgentRequest' + * responses: + * 200: + * description: Agent updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/AgentEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ +agentsRouter.put('/:agentId', validateAgentId, validateAgentReplace, handleValidationErrors, agentHandlers.updateAgent) +/** + * @swagger + * /api/agents/{agentId}: + * patch: + * summary: Update agent (partial update) + * tags: [Agents] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UpdateAgentRequest' + * responses: + * 200: + * description: Agent updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/AgentEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ agentsRouter.patch('/:agentId', validateAgentId, validateAgentUpdate, handleValidationErrors, agentHandlers.patchAgent) +/** + * @swagger + * /api/agents/{agentId}: + * delete: + * summary: Delete agent + * tags: [Agents] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * responses: + * 204: + * description: Agent deleted successfully + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ agentsRouter.delete('/:agentId', validateAgentId, handleValidationErrors, agentHandlers.deleteAgent) // Create sessions router with agent context @@ -169,9 +565,175 @@ const createSessionsRouter = (): express.Router => { const sessionsRouter = express.Router({ mergeParams: true }) // Session CRUD routes (nested under agent) + /** + * @swagger + * /api/agents/{agentId}/sessions: + * post: + * summary: Create a new session for an agent + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateSessionRequest' + * responses: + * 201: + * description: Session created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SessionEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.post('/', validateSession, handleValidationErrors, sessionHandlers.createSession) + + /** + * @swagger + * /api/agents/{agentId}/sessions: + * get: + * summary: List sessions for an agent + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * maximum: 100 + * default: 20 + * description: Number of sessions to return + * - in: query + * name: offset + * schema: + * type: integer + * minimum: 0 + * default: 0 + * description: Number of sessions to skip + * - in: query + * name: status + * schema: + * type: string + * enum: [idle, running, completed, failed, stopped] + * description: Filter by session status + * responses: + * 200: + * description: List of sessions + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ListSessionsResponse' + * 404: + * description: Agent not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.get('/', validatePagination, handleValidationErrors, sessionHandlers.listSessions) + /** + * @swagger + * /api/agents/{agentId}/sessions/{sessionId}: + * get: + * summary: Get session by ID + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: path + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID + * responses: + * 200: + * description: Session details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SessionEntity' + * 404: + * description: Agent or session not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.get('/:sessionId', validateSessionId, handleValidationErrors, sessionHandlers.getSession) + /** + * @swagger + * /api/agents/{agentId}/sessions/{sessionId}: + * put: + * summary: Replace session (full update) + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: path + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ReplaceSessionRequest' + * responses: + * 200: + * description: Session updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SessionEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent or session not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.put( '/:sessionId', validateSessionId, @@ -179,6 +741,51 @@ const createSessionsRouter = (): express.Router => { handleValidationErrors, sessionHandlers.updateSession ) + /** + * @swagger + * /api/agents/{agentId}/sessions/{sessionId}: + * patch: + * summary: Update session (partial update) + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: path + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UpdateSessionRequest' + * responses: + * 200: + * description: Session updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SessionEntity' + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent or session not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.patch( '/:sessionId', validateSessionId, @@ -186,6 +793,35 @@ const createSessionsRouter = (): express.Router => { handleValidationErrors, sessionHandlers.patchSession ) + /** + * @swagger + * /api/agents/{agentId}/sessions/{sessionId}: + * delete: + * summary: Delete session + * tags: [Sessions] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: path + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID + * responses: + * 204: + * description: Session deleted successfully + * 404: + * description: Agent or session not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ sessionsRouter.delete('/:sessionId', validateSessionId, handleValidationErrors, sessionHandlers.deleteSession) return sessionsRouter @@ -196,6 +832,77 @@ const createMessagesRouter = (): express.Router => { const messagesRouter = express.Router({ mergeParams: true }) // Message CRUD routes (nested under agent/session) + /** + * @swagger + * /api/agents/{agentId}/sessions/{sessionId}/messages: + * post: + * summary: Create a new message in a session + * tags: [Messages] + * parameters: + * - in: path + * name: agentId + * required: true + * schema: + * type: string + * description: Agent ID + * - in: path + * name: sessionId + * required: true + * schema: + * type: string + * description: Session ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateSessionMessageRequest' + * responses: + * 201: + * description: Message created successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * id: + * type: number + * description: Message ID + * session_id: + * type: string + * description: Session ID + * role: + * type: string + * enum: [assistant, user, system, tool] + * description: Message role + * content: + * type: object + * description: Message content (AI SDK format) + * agent_session_id: + * type: string + * description: Agent session ID for resuming + * metadata: + * type: object + * description: Additional metadata + * created_at: + * type: string + * format: date-time + * updated_at: + * type: string + * format: date-time + * 400: + * description: Invalid request body + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * 404: + * description: Agent or session not found + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ messagesRouter.post('/', validateSessionMessage, handleValidationErrors, messageHandlers.createMessage) return messagesRouter }