refactor: Rename message stream handler and update session creation logic

This commit is contained in:
Vaayne 2025-09-17 14:10:10 +08:00
parent 219d162e1a
commit d1ff8591a6
20 changed files with 147 additions and 269 deletions

View File

@ -24,13 +24,13 @@ const verifyAgentAndSession = async (agentId: string, sessionId: string) => {
return session
}
export const createMessageStream = async (req: Request, res: Response): Promise<void> => {
export const createMessage = async (req: Request, res: Response): Promise<void> => {
try {
const { agentId, sessionId } = req.params
const session = await verifyAgentAndSession(agentId, sessionId)
const messageData = { ...req.body, session_id: sessionId }
const messageData = req.body
logger.info(`Creating streaming message for session: ${sessionId}`)
logger.debug('Streaming message data:', messageData)
@ -45,7 +45,7 @@ export const createMessageStream = async (req: Request, res: Response): Promise<
// Send initial connection event
res.write('data: {"type":"start"}\n\n')
const messageStream = sessionMessageService.createSessionMessageStream(session, messageData)
const messageStream = sessionMessageService.createSessionMessage(session, messageData)
// Track if the response has ended to prevent further writes
let responseEnded = false

View File

@ -1,19 +1,18 @@
import { loggerService } from '@logger'
import { sessionMessageService, sessionService } from '@main/services/agents'
import { Request, Response } from 'express'
import { sessionMessageService, sessionService } from '../../../../services/agents'
const logger = loggerService.withContext('ApiServerSessionsHandlers')
export const createSession = async (req: Request, res: Response): Promise<Response> => {
try {
const { agentId } = req.params
const sessionData = { ...req.body, main_agent_id: agentId }
const sessionData = req.body
logger.info(`Creating new session for agent: ${agentId}`)
logger.debug('Session data:', sessionData)
const session = await sessionService.createSession(sessionData)
const session = await sessionService.createSession(agentId, sessionData)
logger.info(`Session created successfully: ${session.id}`)
return res.status(201).json(session)
@ -38,7 +37,7 @@ export const listSessions = async (req: Request, res: Response): Promise<Respons
logger.info(`Listing sessions for agent: ${agentId} with limit=${limit}, offset=${offset}, status=${status}`)
const result = await sessionService.listSessions(agentId, { limit, offset, status })
const result = await sessionService.listSessions(agentId, { limit, offset })
logger.info(`Retrieved ${result.sessions.length} sessions (total: ${result.total}) for agent: ${agentId}`)
return res.json({
@ -77,16 +76,16 @@ export const getSession = async (req: Request, res: Response): Promise<Response>
})
}
// Verify session belongs to the agent
logger.warn(`Session ${sessionId} does not belong to agent ${agentId}`)
return res.status(404).json({
error: {
message: 'Session not found for this agent',
type: 'not_found',
code: 'session_not_found'
}
})
}
// // Verify session belongs to the agent
// logger.warn(`Session ${sessionId} does not belong to agent ${agentId}`)
// return res.status(404).json({
// error: {
// message: 'Session not found for this agent',
// type: 'not_found',
// code: 'session_not_found'
// }
// })
// }
// Fetch session messages
logger.info(`Fetching messages for session: ${sessionId}`)
@ -261,7 +260,7 @@ export const listAllSessions = async (req: Request, res: Response): Promise<Resp
logger.info(`Listing all sessions with limit=${limit}, offset=${offset}, status=${status}`)
const result = await sessionService.listSessions(undefined, { limit, offset, status })
const result = await sessionService.listSessions(undefined, { limit, offset })
logger.info(`Retrieved ${result.sessions.length} sessions (total: ${result.total})`)
return res.json({

View File

@ -188,7 +188,7 @@ const createMessagesRouter = (): express.Router => {
const messagesRouter = express.Router({ mergeParams: true })
// Message CRUD routes (nested under agent/session)
messagesRouter.post('/', validateSessionMessage, handleValidationErrors, messageHandlers.createMessageStream)
messagesRouter.post('/', validateSessionMessage, handleValidationErrors, messageHandlers.createMessage)
return messagesRouter
}

View File

@ -1,6 +1,5 @@
import { body } from 'express-validator'
export const validateSessionMessage = [
body('role').notEmpty().isIn(['user', 'agent', 'system', 'tool']).withMessage('Valid role is required'),
body('content').notEmpty().isString().withMessage('Content must be a valid string')
]

View File

@ -1,11 +1,11 @@
import { type Client, createClient } from '@libsql/client'
import { loggerService } from '@logger'
import { drizzle } from 'drizzle-orm/libsql'
import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'
import fs from 'fs'
import path from 'path'
import * as schema from './database/schema'
import { MigrationService } from './database/MigrationService'
import * as schema from './database/schema'
import { dbPath } from './drizzle.config'
const logger = loggerService.withContext('BaseService')
@ -24,7 +24,7 @@ const logger = loggerService.withContext('BaseService')
*/
export abstract class BaseService {
protected static client: Client | null = null
protected static db: ReturnType<typeof drizzle> | null = null
protected static db: LibSQLDatabase<typeof schema> | null = null
protected static isInitialized = false
protected static initializationPromise: Promise<void> | null = null
protected jsonFields: string[] = ['built_in_tools', 'mcps', 'configuration', 'accessible_paths']
@ -110,7 +110,7 @@ export abstract class BaseService {
}
}
protected get database(): ReturnType<typeof drizzle> {
protected get database(): LibSQLDatabase<typeof schema> {
this.ensureInitialized()
return BaseService.db!
}

View File

@ -3,6 +3,6 @@
*/
export * from './agents.schema'
export * from './sessions.schema'
export * from './messages.schema'
export * from './migrations.schema'
export * from './sessions.schema'

View File

@ -1,4 +1,5 @@
import { foreignKey, index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { sessionsTable } from './sessions.schema'
// session_messages table to log all messages, thoughts, actions, observations in a session

View File

@ -3,10 +3,12 @@
*/
import { foreignKey, index, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { agentsTable } from './agents.schema'
export const sessionsTable = sqliteTable('sessions', {
id: text('id').primaryKey(),
agent_type: text('agent_type').notNull(),
agent_id: text('agent_id').notNull(), // Primary agent ID for the session
name: text('name').notNull(),
description: text('description'),

View File

@ -16,12 +16,16 @@ function getDbPath() {
return path.join(app.getPath('userData'), 'agents.db')
}
const resolvedDbPath = getDbPath()
export const dbPath = resolvedDbPath
export default defineConfig({
dialect: 'sqlite',
schema: './src/main/services/agents/database/schema/index.ts',
out: './src/main/services/agents/database/drizzle',
dbCredentials: {
url: `file:${getDbPath()}`
url: `file:${resolvedDbPath}`
},
verbose: true,
strict: true

View File

@ -11,7 +11,7 @@ import { UIMessageChunk } from 'ai'
import { count, eq } from 'drizzle-orm'
import { BaseService } from '../BaseService'
import { type InsertSessionMessageRow, sessionMessagesTable } from '../database/schema'
import { sessionMessagesTable } from '../database/schema'
import ClaudeCodeService from './claudecode'
const logger = loggerService.withContext('SessionMessageService')
@ -76,19 +76,19 @@ export class SessionMessageService extends BaseService {
return { messages, total }
}
createSessionMessageStream(session: GetAgentSessionResponse, messageData: CreateSessionMessageRequest): EventEmitter {
createSessionMessage(session: GetAgentSessionResponse, messageData: CreateSessionMessageRequest): EventEmitter {
this.ensureInitialized()
// Create a new EventEmitter to manage the session message lifecycle
const sessionStream = new EventEmitter()
// No parent validation needed, start immediately
this.startClaudeCodeStream(session, messageData, sessionStream)
this.startSessionMessageStream(session, messageData, sessionStream)
return sessionStream
}
private startClaudeCodeStream(
private startSessionMessageStream(
session: GetAgentSessionResponse,
req: CreateSessionMessageRequest,
sessionStream: EventEmitter
@ -99,7 +99,12 @@ export class SessionMessageService extends BaseService {
session_id = previousMessages[0].session_id
}
logger.debug('Claude Code stream message data:', { message: req, session_id })
logger.debug('Session Message stream message data:', { message: req, session_id })
if (session.agent_type !== 'claude-code') {
logger.error('Unsupported agent type for streaming:', { agent_type: session.agent_type })
throw new Error('Unsupported agent type for streaming')
}
// Create the streaming agent invocation (using invokeStream for streaming)
const claudeStream = this.cc.invoke(req.content, session.accessible_paths[0], session_id, {
@ -107,7 +112,6 @@ export class SessionMessageService extends BaseService {
maxTurns: session.configuration?.maxTurns || 10
})
let sessionMessage: AgentSessionMessageEntity | null = null
const streamedChunks: UIMessageChunk[] = []
const rawAgentMessages: any[] = [] // Generic agent messages storage
@ -202,6 +206,10 @@ export class SessionMessageService extends BaseService {
// error: new Error('Failed to save session message to database')
// })
// }
sessionStream.emit('data', {
type: 'complete',
result: structuredContent
})
break
}

View File

@ -1,4 +1,5 @@
import type {
AgentEntity,
AgentSessionEntity,
CreateSessionRequest,
GetAgentSessionResponse,
@ -25,18 +26,18 @@ export class SessionService extends BaseService {
await BaseService.initialize()
}
async createSession(req: CreateSessionRequest): Promise<AgentSessionEntity> {
async createSession(agentId: string, req: CreateSessionRequest): Promise<AgentSessionEntity> {
this.ensureInitialized()
// Validate agent exists - we'll need to import AgentService for this check
// For now, we'll skip this validation to avoid circular dependencies
// The database foreign key constraint will handle this
const agents = await this.database.select().from(agentsTable).where(eq(agentsTable.id, req.agent_id)).limit(1)
const agents = await this.database.select().from(agentsTable).where(eq(agentsTable.id, agentId)).limit(1)
if (!agents[0]) {
throw new Error('Agent not found')
}
const agent = this.deserializeJsonFields(agents[0]) as AgentSessionEntity
const agent = this.deserializeJsonFields(agents[0]) as AgentEntity
const id = `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
const now = new Date().toISOString()
@ -51,15 +52,17 @@ export class SessionService extends BaseService {
const insertData: InsertSessionRow = {
id,
agent_id: agentId,
agent_type: agent.type,
name: serializedData.name || null,
agent_id: serializedData.agent_id,
description: serializedData.description || null,
accessible_paths: serializedData.accessible_paths || null,
instructions: serializedData.instructions || null,
model: serializedData.model || null,
plan_model: serializedData.plan_model || null,
small_model: serializedData.small_model || null,
mcps: serializedData.mcps || null,
configuration: serializedData.configuration || null,
accessible_paths: serializedData.accessible_paths || null,
created_at: now,
updated_at: now
}

View File

@ -242,155 +242,6 @@ class ClaudeCodeService implements AgentServiceInterface {
process.on('error', () => clearTimeout(timeout))
}
/**
* Set up process event handlers and return a promise that resolves with complete output
*/
private setupProcessHandlers(process: ChildProcess): Promise<ClaudeCodeResult> {
return new Promise((resolve, reject) => {
let stdoutData = ''
let stderrData = ''
const jsonOutput: any[] = []
let hasResolved = false
const startTime = Date.now()
// Handle stdout with proper encoding and buffering
if (process.stdout) {
process.stdout.setEncoding('utf8')
process.stdout.on('data', (data: string) => {
stdoutData += data
logger.debug('Agent stdout chunk:', { length: data.length })
// Parse JSON stream output line by line
const lines = data.split('\n')
for (const line of lines) {
if (line.trim()) {
try {
const parsed = JSON.parse(line.trim())
jsonOutput.push(parsed)
logger.silly('Parsed JSON output:', parsed)
} catch (e) {
// Not JSON, might be plain text output
logger.debug('Non-JSON stdout line:', { line: line.trim() })
}
}
}
})
process.stdout.on('end', () => {
logger.debug('Agent stdout stream ended')
})
}
// Handle stderr with proper encoding
if (process.stderr) {
process.stderr.setEncoding('utf8')
process.stderr.on('data', (data: string) => {
stderrData += data
logger.warn('Agent stderr chunk:', { data: data.trim() })
})
process.stderr.on('end', () => {
logger.debug('Agent stderr stream ended')
})
}
// Handle process exit
process.on('exit', (code, signal) => {
const duration = Date.now() - startTime
const success = code === 0
const status = success ? 'completed' : 'failed'
logger.info('Agent process exited', {
code,
signal,
success,
status,
duration,
stdoutLength: stdoutData.length,
stderrLength: stderrData.length,
jsonItems: jsonOutput.length
})
if (!hasResolved) {
hasResolved = true
resolve({
success,
stdout: stdoutData,
stderr: stderrData,
jsonOutput,
exitCode: code || undefined
})
}
})
// Handle process errors
process.on('error', (error) => {
const duration = Date.now() - startTime
logger.error('Agent process error:', {
error: error.message,
duration,
stdoutLength: stdoutData.length,
stderrLength: stderrData.length
})
if (!hasResolved) {
hasResolved = true
reject({
success: false,
stdout: stdoutData,
stderr: stderrData,
jsonOutput,
error
})
}
})
// Handle close event as a fallback
process.on('close', (code, signal) => {
const duration = Date.now() - startTime
logger.debug('Agent process closed', { code, signal, duration })
// Only resolve here if exit event hasn't fired
if (!hasResolved) {
hasResolved = true
const success = code === 0
resolve({
success,
stdout: stdoutData,
stderr: stderrData,
jsonOutput,
exitCode: code || undefined
})
}
})
// Set a timeout to prevent hanging indefinitely (reduced for debugging)
const timeout = setTimeout(() => {
if (!hasResolved) {
hasResolved = true
logger.error('Agent process timeout after 30 seconds', {
pid: process.pid,
stdoutLength: stdoutData.length,
stderrLength: stderrData.length,
jsonItems: jsonOutput.length
})
process.kill('SIGTERM')
reject({
success: false,
stdout: stdoutData,
stderr: stderrData,
jsonOutput,
error: new Error('Process timeout after 30 seconds')
})
}
}, 30 * 1000) // 30 seconds timeout for debugging
// Clear timeout when process ends
process.on('exit', () => clearTimeout(timeout))
process.on('error', () => clearTimeout(timeout))
})
}
}
export default ClaudeCodeService

View File

@ -16,5 +16,10 @@ export { sessionMessageService } from './SessionMessageService'
export { sessionService } from './SessionService'
// Type definitions for service requests and responses
export type { CreateSessionRequest, ListSessionsOptions, UpdateSessionRequest } from './SessionService'
export type { AgentEntity, AgentSessionEntity,CreateAgentRequest, UpdateAgentRequest } from '@types'
export type {
AgentSessionMessageEntity,
CreateSessionRequest,
GetAgentSessionResponse,
ListOptions as SessionListOptions,
UpdateSessionRequest} from '@types'

View File

@ -23,7 +23,7 @@ import { useTimer } from '@renderer/hooks/useTimer'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { AgentEntity, AgentType, isAgentType } from '@renderer/types'
import { uuid } from '@renderer/utils'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useMemo, useRef, useState } from 'react'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ErrorBoundary } from '../ErrorBoundary'
@ -45,13 +45,23 @@ interface AgentTypeOption extends Option {
type ModelOption = Option
type AgentForm = {
type: AgentEntity['type']
name: AgentEntity['name']
description?: AgentEntity['description']
instructions?: AgentEntity['instructions']
model?: AgentEntity['model']
type: AgentType
name: string
description?: string
instructions?: string
model: string
accessible_paths: string[]
}
const buildAgentForm = (existing?: AgentEntity): AgentForm => ({
type: existing?.type ?? 'claude-code',
name: existing?.name ?? 'Claude Code',
description: existing?.description,
instructions: existing?.instructions,
model: existing?.model ?? 'claude-4-sonnet',
accessible_paths: existing?.accessible_paths ? [...existing.accessible_paths] : []
})
interface BaseProps {
agent?: AgentEntity
}
@ -88,16 +98,13 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
const { addAgent, updateAgent } = useAgents()
const isEditing = (agent?: AgentEntity) => agent !== undefined
// default values. may change to undefined.
const [form, setForm] = useState<AgentForm>(
isEditing(agent)
? agent
: {
type: 'claude-code',
name: 'Claude Code',
model: 'claude-4-sonnet'
}
)
const [form, setForm] = useState<AgentForm>(() => buildAgentForm(agent))
useEffect(() => {
if (isOpen) {
setForm(buildAgentForm(agent))
}
}, [agent, isOpen])
const Option = useCallback(
({ option }: { option?: Option | null }) => {
@ -222,44 +229,51 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
// Additional validation check besides native HTML validation to ensure security
if (!isAgentType(form.type)) {
window.toast.error(t('agent.add.error.invalid_agent'))
loadingRef.current = false
return
}
if (form.model === undefined) {
if (!form.model) {
window.toast.error(t('error.model.not_exists'))
loadingRef.current = false
return
}
let _agent: AgentEntity
let resultAgent: AgentEntity
if (isEditing(agent)) {
_agent = {
...agent,
// type: form.type,
if (!agent) {
throw new Error('Agent is required for editing mode')
}
const updatePayload: Partial<AgentEntity> & { id: string } = {
id: agent.id,
name: form.name,
description: form.description,
instructions: form.instructions,
updated_at: new Date().toISOString(),
model: form.model
// avatar: getAvatar(form.type)
} satisfies AgentEntity
updateAgent(_agent)
}
updateAgent(updatePayload)
resultAgent = { ...agent, ...updatePayload }
window.toast.success(t('common.update_success'))
} else {
_agent = {
const now = new Date().toISOString()
resultAgent = {
id: uuid(),
type: form.type,
name: form.name,
description: form.description,
instructions: form.instructions,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
created_at: now,
updated_at: now,
model: form.model,
avatar: getAvatar(form.type)
} satisfies AgentEntity
addAgent(_agent)
accessible_paths: [...form.accessible_paths]
}
addAgent(resultAgent)
window.toast.success(t('common.add_success'))
}
logger.debug('Agent', _agent)
logger.debug('Agent mutation payload', { agent: resultAgent })
loadingRef.current = false
setTimeoutTimer('onCreateAgent', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
@ -271,6 +285,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
form.name,
form.description,
form.instructions,
form.accessible_paths,
agent,
setTimeoutTimer,
onClose,
@ -339,8 +354,16 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
</SelectItem>
)}
</Select>
<Textarea label={t('common.description')} value={form.description} onValueChange={onDescChange} />
<Textarea label={t('common.prompt')} value={form.instructions} onValueChange={onInstChange} />
<Textarea
label={t('common.description')}
value={form.description ?? ''}
onValueChange={onDescChange}
/>
<Textarea
label={t('common.prompt')}
value={form.instructions ?? ''}
onValueChange={onInstChange}
/>
</ModalBody>
<ModalFooter className="w-full">
<Button onPress={onClose}>{t('common.close')}</Button>
@ -356,11 +379,3 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
</ErrorBoundary>
)
}
const getAvatar = (type: AgentType) => {
switch (type) {
case 'claude-code':
return ClaudeIcon
}
return undefined
}

View File

@ -1,7 +1,9 @@
import { AgentBase } from '@renderer/types'
// base agent config. no default config for now.
const DEFAULT_AGENT_CONFIG: Omit<AgentBase, 'model'> = {} as const
const DEFAULT_AGENT_CONFIG: Omit<AgentBase, 'model'> = {
accessible_paths: []
} as const
// no default config for now.
export const DEFAULT_CLAUDE_CODE_CONFIG: Omit<AgentBase, 'model'> = {

View File

@ -6,8 +6,9 @@ export const useUpdateAgent = () => {
// TODO: use api
return useMutation({
// @ts-expect-error not-implemented
mutationFn: async ({}: Partial<AgentEntity> & { id: string }) => {},
mutationFn: async (agentUpdate: Partial<AgentEntity> & { id: string }) => {
throw new Error(`useUpdateAgent mutationFn not implemented for agent ${agentUpdate.id}`)
},
onSuccess: (updated: AgentEntity) => {
qc.setQueryData<AgentEntity[]>(['todos'], (old) =>
old ? old.map((t) => (t.id === updated.id ? updated : t)) : []

View File

@ -22,13 +22,14 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete }) => {
// const { agents } = useAgents()
const AgentLabel = useCallback(() => {
const displayName = agent.name ?? agent.id
return (
<>
{agent.avatar && <Avatar className="h-6 w-6" src={agent.avatar} />}
<span className="text-sm">{agent.name}</span>
<Avatar className="h-6 w-6" name={displayName} />
<span className="text-sm">{displayName}</span>
</>
)
}, [agent.avatar, agent.name])
}, [agent.id, agent.name])
const handleClick = () => logger.debug('not implemented')
@ -37,7 +38,7 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete }) => {
<ContextMenu modal={false}>
<ContextMenuTrigger>
<Container onClick={handleClick} className={isActive ? 'active' : ''}>
<AssistantNameRow className="name" title={agent.name}>
<AssistantNameRow className="name" title={agent.name ?? agent.id}>
<AgentLabel />
</AssistantNameRow>
</Container>

View File

@ -2,7 +2,7 @@
* Database entity types for Agent, Session, and SessionMessage
* Shared between main and renderer processes
*/
import { TextStreamPart, UIMessageChunk, ModelMessage } from 'ai'
import { ModelMessage, TextStreamPart, UIMessageChunk } from 'ai'
export type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan'
export type SessionMessageRole = ModelMessage['role']
export type AgentType = 'claude-code'
@ -75,15 +75,14 @@ export interface ListOptions {
export interface AgentSessionEntity extends AgentBase {
id: string
agent_id: string // Primary agent ID for the session
agent_type: AgentType
// sub_agent_ids?: string[] // Array of sub-agent IDs involved in the session
created_at: string
updated_at: string
}
export interface CreateSessionRequest extends AgentBase {
agent_id: string // Primary agent ID for the session
}
export type CreateSessionRequest = AgentBase
export interface UpdateSessionRequest extends Partial<AgentBase> {}

View File

@ -16,13 +16,14 @@ Content-Type: application/json
"type": "claude-code",
"model": "anthropic:claude-sonnet-4",
"description": "An AI assistant specialized in code review and debugging",
"avatar": "https://example.com/avatar.png",
"instructions": "You are a helpful coding assistant. Focus on writing clean, maintainable code and providing constructive feedback.",
"accessible_paths": [
"/tmp/workspace"
],
"permission_mode": "acceptEdits",
"max_steps": 10
"configuration": {
"permission_mode": "acceptEdits",
"max_turns": 5
}
}
### Get Agent Details
@ -42,14 +43,16 @@ Content-Type: application/json
{
"name": "Claude Code",
"type": "claude-code",
"model": "anthropic:claude-sonnet-4",
"description": "An AI assistant specialized in code review and debugging",
"avatar": "https://example.com/avatar.png",
"instructions": "You are a helpful coding assistant. Focus on writing clean, maintainable code and providing constructive feedback.",
"accessible_paths": [
"/tmp/workspace"
],
"permission_mode": "acceptEdits",
"max_steps": 10
"configuration": {
"permission_mode": "acceptEdits",
"max_turns": 5
}
}

View File

@ -1,8 +1,8 @@
@host=http://localhost:23333
@token=cs-sk-af798ed4-7cf5-4fd7-ae4b-df203b164194
@agent_id=agent_1757947603408_t1y2mbnq4
@session_id=session_1757947684264_z2wcwn8t7
@agent_id=agent_1758084953648_my4oxnbm3
@session_id=session_1758087299124_2etavjo2x
### List Sessions
GET {{host}}/v1/agents/{{agent_id}}/sessions
@ -15,29 +15,26 @@ POST {{host}}/v1/agents/{{agent_id}}/sessions
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Joke telling Session",
"user_goal": "Tell me a funny joke"
}
{}
### Get Session Details
GET {{host}}/v1/agents/{{agent_id}}/sessions/session_1757815260195_eldvompnv
GET {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}}
Authorization: Bearer {{token}}
Content-Type: application/json
### Delete Session
DELETE {{host}}/v1/agents/{{agent_id}}/sessions/session_1757815245456_tfs6oogl0
DELETE {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}}
Authorization: Bearer {{token}}
Content-Type: application/json
### Update Session
PUT {{host}}/v1/agents/{{agent_id}}/sessions/session_1757815281790_q4yxgdk74
PUT {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}}
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Code Review Session 1",
"user_goal": "Review the newly implemented feature for bugs and improvements"
"instructions": "Review the newly implemented feature for bugs and improvements"
}
@ -47,17 +44,5 @@ Authorization: Bearer {{token}}
Content-Type: application/json
{
"role": "user",
"content": "a joke about programmers"
}
### Create Session Message Stream
POST {{host}}/v1/agents/{{agent_id}}/sessions/{{session_id}}/messages/stream
Authorization: Bearer {{token}}
Content-Type: application/json
{
"role": "user",
"content": "a joke about programmers"
}