refactor: bind container lifecycle to bot and improve schedule trigger flow

- Add SetupBotContainer to ContainerLifecycle interface so containers
  are automatically created when a bot is created, matching the existing
  cleanup-on-delete behavior.
- Refactor schedule tools to use bot-scoped API paths and pass identity
  context for proper authorization.
- Introduce dedicated trigger-schedule endpoint in chat resolver with
  explicit schedule payload instead of reusing the generic chat path.
- Generate short-lived JWT tokens for schedule trigger callbacks with
  resolved bot owner identity.
- Validate required parameters in NewLLMClient and NewOpenAIEmbedder
  constructors, returning errors instead of falling back to defaults.
- Add unit tests for schedule token generation and chat resolver.
This commit is contained in:
BBQ
2026-02-07 12:03:24 +08:00
parent a9596ab3a8
commit 83b6ee608c
16 changed files with 583 additions and 72 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ export const getTools = (
Object.assign(tools, webTools)
}
if (actions.includes(AgentAction.Schedule)) {
const scheduleTools = getScheduleTools({ fetch })
const scheduleTools = getScheduleTools({ fetch, identity })
Object.assign(tools, scheduleTools)
}
if (actions.includes(AgentAction.Memory)) {
+11 -6
View File
@@ -1,9 +1,11 @@
import { tool } from 'ai'
import { z } from 'zod'
import { AuthFetcher } from '..'
import type { IdentityContext } from '../types'
export type ScheduleToolParams = {
fetch: AuthFetcher
identity: IdentityContext
}
const ScheduleSchema = z.object({
@@ -15,12 +17,15 @@ const ScheduleSchema = z.object({
command: z.string(),
})
export const getScheduleTools = ({ fetch }: ScheduleToolParams) => {
export const getScheduleTools = ({ fetch, identity }: ScheduleToolParams) => {
const botId = identity.botId.trim()
const base = `/bots/${botId}/schedule`
const listSchedules = tool({
description: 'List schedules for current user',
inputSchema: z.object({}),
execute: async () => {
const response = await fetch('/schedule', { method: 'GET' })
const response = await fetch(base, { method: 'GET' })
return response.json()
},
})
@@ -31,7 +36,7 @@ export const getScheduleTools = ({ fetch }: ScheduleToolParams) => {
id: z.string().describe('Schedule ID'),
}),
execute: async ({ id }) => {
const response = await fetch(`/schedule/${id}`, { method: 'GET' })
const response = await fetch(`${base}/${id}`, { method: 'GET' })
return response.json()
},
})
@@ -47,7 +52,7 @@ export const getScheduleTools = ({ fetch }: ScheduleToolParams) => {
command: z.string(),
}),
execute: async (payload) => {
const response = await fetch('/schedule', {
const response = await fetch(base, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
@@ -63,7 +68,7 @@ export const getScheduleTools = ({ fetch }: ScheduleToolParams) => {
}),
execute: async (payload) => {
const { id, ...body } = payload
const response = await fetch(`/schedule/${id}`, {
const response = await fetch(`${base}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
@@ -78,7 +83,7 @@ export const getScheduleTools = ({ fetch }: ScheduleToolParams) => {
id: z.string(),
}),
execute: async ({ id }) => {
const response = await fetch(`/schedule/${id}`, { method: 'DELETE' })
const response = await fetch(`${base}/${id}`, { method: 'DELETE' })
return response.status === 204 ? { success: true } : response.json()
},
})