Merge remote-tracking branch 'origin/main' into feat/tanstack-query-migration

# Conflicts:
#	apps/web/app/(dashboard)/agents/page.tsx
This commit is contained in:
Naiyuan Qing 2026-04-08 10:35:28 +08:00
commit 8cf78b7a47
17 changed files with 36 additions and 803 deletions

View file

@ -8,15 +8,10 @@ import {
Monitor, Monitor,
Plus, Plus,
ListTodo, ListTodo,
Wrench,
FileText, FileText,
BookOpenText, BookOpenText,
MessageSquare,
Timer,
Trash2, Trash2,
Save, Save,
Key,
Link2,
Clock, Clock,
CheckCircle2, CheckCircle2,
XCircle, XCircle,
@ -35,9 +30,6 @@ import type {
Agent, Agent,
AgentStatus, AgentStatus,
AgentVisibility, AgentVisibility,
AgentTool,
AgentTrigger,
AgentTriggerType,
AgentTask, AgentTask,
RuntimeDevice, RuntimeDevice,
CreateAgentRequest, CreateAgentRequest,
@ -151,10 +143,6 @@ function CreateAgentDialog({
description: description.trim(), description: description.trim(),
runtime_id: selectedRuntime.id, runtime_id: selectedRuntime.id,
visibility, visibility,
triggers: [
{ id: generateId(), type: "on_assign", enabled: true, config: {} },
{ id: generateId(), type: "on_comment", enabled: true, config: {} },
],
}); });
onClose(); onClose();
} catch (err) { } catch (err) {
@ -600,466 +588,6 @@ function SkillsTab({
); );
} }
// ---------------------------------------------------------------------------
// Tools Tab
// ---------------------------------------------------------------------------
function AddToolDialog({
onClose,
onAdd,
}: {
onClose: () => void;
onAdd: (tool: AgentTool) => void;
}) {
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [authType, setAuthType] = useState<"oauth" | "api_key" | "none">("api_key");
const handleAdd = () => {
if (!name.trim()) return;
onAdd({
id: generateId(),
name: name.trim(),
description: description.trim(),
auth_type: authType,
connected: false,
config: {},
});
onClose();
};
return (
<Dialog open onOpenChange={(v) => { if (!v) onClose(); }}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="text-sm">Add Tool</DialogTitle>
<DialogDescription className="text-xs">
Connect an external tool for this agent to use.
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div>
<Label className="text-xs text-muted-foreground">Tool Name</Label>
<Input
autoFocus
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. Google Search, Slack, GitHub"
className="mt-1"
onKeyDown={(e) => e.key === "Enter" && handleAdd()}
/>
</div>
<div>
<Label className="text-xs text-muted-foreground">Description</Label>
<Input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="What does this tool do?"
className="mt-1"
/>
</div>
<div>
<Label className="text-xs text-muted-foreground">Authentication</Label>
<div className="mt-1.5 flex gap-2">
{(["api_key", "oauth", "none"] as const).map((type) => (
<Button
key={type}
variant={authType === type ? "outline" : "ghost"}
size="xs"
onClick={() => setAuthType(type)}
className={`flex-1 ${
authType === type
? "border-primary bg-primary/5 font-medium"
: ""
}`}
>
{type === "api_key" ? "API Key" : type === "oauth" ? "OAuth" : "None"}
</Button>
))}
</div>
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={onClose}>
Cancel
</Button>
<Button
onClick={handleAdd}
disabled={!name.trim()}
>
Add
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
function ToolsTab({
agent,
onSave,
}: {
agent: Agent;
onSave: (tools: AgentTool[]) => Promise<void>;
}) {
const [tools, setTools] = useState<AgentTool[]>(agent.tools ?? []);
const [showAdd, setShowAdd] = useState(false);
const [saving, setSaving] = useState(false);
useEffect(() => {
setTools(agent.tools ?? []);
}, [agent.id, agent.tools]);
const isDirty = JSON.stringify(tools) !== JSON.stringify(agent.tools ?? []);
const handleSave = async () => {
setSaving(true);
try {
await onSave(tools);
} catch {
// toast handled by parent
} finally {
setSaving(false);
}
};
const toggleConnect = (toolId: string) => {
setTools((prev) =>
prev.map((t) => (t.id === toolId ? { ...t, connected: !t.connected } : t)),
);
};
const removeTool = (toolId: string) => {
setTools((prev) => prev.filter((t) => t.id !== toolId));
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-semibold">Tools</h3>
<p className="text-xs text-muted-foreground mt-0.5">
External tools and APIs this agent can use during task execution.
</p>
</div>
<div className="flex items-center gap-2">
{isDirty && (
<Button
onClick={handleSave}
disabled={saving}
size="xs"
>
<Save className="h-3 w-3" />
{saving ? "Saving..." : "Save"}
</Button>
)}
<Button
variant="outline"
size="xs"
onClick={() => setShowAdd(true)}
>
<Plus className="h-3 w-3" />
Add Tool
</Button>
</div>
</div>
{tools.length === 0 ? (
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed py-12">
<Wrench className="h-8 w-8 text-muted-foreground/40" />
<p className="mt-3 text-sm text-muted-foreground">No tools configured</p>
<Button
onClick={() => setShowAdd(true)}
size="xs"
className="mt-3"
>
<Plus className="h-3 w-3" />
Add Tool
</Button>
</div>
) : (
<div className="space-y-2">
{tools.map((tool) => (
<div
key={tool.id}
className="flex items-center gap-3 rounded-lg border px-4 py-3"
>
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted">
{tool.auth_type === "oauth" ? (
<Link2 className="h-4 w-4 text-muted-foreground" />
) : tool.auth_type === "api_key" ? (
<Key className="h-4 w-4 text-muted-foreground" />
) : (
<Wrench className="h-4 w-4 text-muted-foreground" />
)}
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">{tool.name}</div>
{tool.description && (
<div className="text-xs text-muted-foreground truncate">
{tool.description}
</div>
)}
</div>
<div className="flex items-center gap-2">
<Button
variant="ghost"
size="xs"
onClick={() => toggleConnect(tool.id)}
className={
tool.connected
? "bg-success/10 text-success"
: "bg-muted text-muted-foreground hover:bg-accent"
}
>
{tool.connected ? "Connected" : "Connect"}
</Button>
<Button
variant="ghost"
size="icon-xs"
onClick={() => removeTool(tool.id)}
className="text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
</div>
))}
</div>
)}
{showAdd && (
<AddToolDialog
onClose={() => setShowAdd(false)}
onAdd={(tool) => setTools((prev) => [...prev, tool])}
/>
)}
</div>
);
}
// ---------------------------------------------------------------------------
// Triggers Tab
// ---------------------------------------------------------------------------
function TriggersTab({
agent,
onSave,
}: {
agent: Agent;
onSave: (triggers: AgentTrigger[]) => Promise<void>;
}) {
const [triggers, setTriggers] = useState<AgentTrigger[]>(agent.triggers ?? []);
const [saving, setSaving] = useState(false);
useEffect(() => {
setTriggers(agent.triggers ?? []);
}, [agent.id, agent.triggers]);
const isDirty = JSON.stringify(triggers) !== JSON.stringify(agent.triggers ?? []);
const handleSave = async () => {
setSaving(true);
try {
await onSave(triggers);
} catch {
// toast handled by parent
} finally {
setSaving(false);
}
};
const toggleTrigger = (triggerId: string) => {
setTriggers((prev) =>
prev.map((t) => (t.id === triggerId ? { ...t, enabled: !t.enabled } : t)),
);
};
const removeTrigger = (triggerId: string) => {
setTriggers((prev) => prev.filter((t) => t.id !== triggerId));
};
const addTrigger = (type: AgentTriggerType) => {
const newTrigger: AgentTrigger = {
id: generateId(),
type,
enabled: true,
config: type === "scheduled" ? { cron: "0 9 * * 1-5", timezone: "UTC" } : {},
};
setTriggers((prev) => [...prev, newTrigger]);
};
const updateTriggerConfig = (triggerId: string, config: Record<string, unknown>) => {
setTriggers((prev) =>
prev.map((t) => (t.id === triggerId ? { ...t, config } : t)),
);
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-semibold">Triggers</h3>
<p className="text-xs text-muted-foreground mt-0.5">
Configure when this agent should start working.
</p>
</div>
<div className="flex items-center gap-2">
{isDirty && (
<Button
onClick={handleSave}
disabled={saving}
size="xs"
>
<Save className="h-3 w-3" />
{saving ? "Saving..." : "Save"}
</Button>
)}
</div>
</div>
<div className="space-y-2">
{triggers.map((trigger) => {
const scheduledConfig = (trigger.config ?? {}) as {
cron?: string;
timezone?: string;
};
return (
<div
key={trigger.id}
className="rounded-lg border px-4 py-3"
>
<div className="flex items-center gap-3">
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted">
{trigger.type === "on_assign" ? (
<Bot className="h-4 w-4 text-muted-foreground" />
) : trigger.type === "on_comment" ? (
<MessageSquare className="h-4 w-4 text-muted-foreground" />
) : (
<Timer className="h-4 w-4 text-muted-foreground" />
)}
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">
{trigger.type === "on_assign"
? "On Issue Assign"
: trigger.type === "on_comment"
? "On Comment"
: "Scheduled"}
</div>
<div className="text-xs text-muted-foreground">
{trigger.type === "on_assign"
? "Runs when an issue is assigned to this agent"
: trigger.type === "on_comment"
? "Runs when a member comments on the agent's issue"
: `Cron: ${scheduledConfig.cron ?? "Not set"}`}
</div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => toggleTrigger(trigger.id)}
className={`relative h-5 w-9 rounded-full transition-colors ${
trigger.enabled ? "bg-primary" : "bg-muted"
}`}
>
<span
className={`absolute top-0.5 h-4 w-4 rounded-full bg-white shadow-sm transition-transform ${
trigger.enabled ? "left-4.5" : "left-0.5"
}`}
/>
</button>
<Button
variant="ghost"
size="icon-xs"
onClick={() => removeTrigger(trigger.id)}
className="text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
</div>
{trigger.type === "scheduled" && (
<div className="mt-3 grid grid-cols-2 gap-3 pl-12">
<div>
<Label className="text-xs text-muted-foreground">
Cron Expression
</Label>
<Input
type="text"
value={scheduledConfig.cron ?? ""}
onChange={(e) =>
updateTriggerConfig(trigger.id, {
...(trigger.config ?? {}),
cron: e.target.value,
})
}
placeholder="0 9 * * 1-5"
className="mt-1 text-xs font-mono"
/>
</div>
<div>
<Label className="text-xs text-muted-foreground">
Timezone
</Label>
<Input
type="text"
value={scheduledConfig.timezone ?? ""}
onChange={(e) =>
updateTriggerConfig(trigger.id, {
...(trigger.config ?? {}),
timezone: e.target.value,
})
}
placeholder="UTC"
className="mt-1 text-xs"
/>
</div>
</div>
)}
</div>
);
})}
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="xs"
onClick={() => addTrigger("on_assign")}
className="border-dashed text-muted-foreground hover:text-foreground"
>
<Bot className="h-3 w-3" />
Add On Assign
</Button>
<Button
variant="outline"
size="xs"
onClick={() => addTrigger("on_comment")}
className="border-dashed text-muted-foreground hover:text-foreground"
>
<MessageSquare className="h-3 w-3" />
Add On Comment
</Button>
<Button
variant="outline"
size="xs"
onClick={() => addTrigger("scheduled")}
className="border-dashed text-muted-foreground hover:text-foreground"
>
<Timer className="h-3 w-3" />
Add Scheduled
</Button>
</div>
</div>
);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Tasks Tab // Tasks Tab
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -1371,13 +899,11 @@ function SettingsTab({
// Agent Detail // Agent Detail
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
type DetailTab = "instructions" | "skills" | "tools" | "triggers" | "tasks" | "settings"; type DetailTab = "instructions" | "skills" | "tasks" | "settings";
const detailTabs: { id: DetailTab; label: string; icon: typeof FileText }[] = [ const detailTabs: { id: DetailTab; label: string; icon: typeof FileText }[] = [
{ id: "instructions", label: "Instructions", icon: FileText }, { id: "instructions", label: "Instructions", icon: FileText },
{ id: "skills", label: "Skills", icon: BookOpenText }, { id: "skills", label: "Skills", icon: BookOpenText },
{ id: "tools", label: "Tools", icon: Wrench },
{ id: "triggers", label: "Triggers", icon: Timer },
{ id: "tasks", label: "Tasks", icon: ListTodo }, { id: "tasks", label: "Tasks", icon: ListTodo },
{ id: "settings", label: "Settings", icon: Settings }, { id: "settings", label: "Settings", icon: Settings },
]; ];
@ -1491,18 +1017,6 @@ function AgentDetail({
{activeTab === "skills" && ( {activeTab === "skills" && (
<SkillsTab agent={agent} /> <SkillsTab agent={agent} />
)} )}
{activeTab === "tools" && (
<ToolsTab
agent={agent}
onSave={(tools) => onUpdate(agent.id, { tools })}
/>
)}
{activeTab === "triggers" && (
<TriggersTab
agent={agent}
onSave={(triggers) => onUpdate(agent.id, { triggers })}
/>
)}
{activeTab === "tasks" && <TasksTab agent={agent} />} {activeTab === "tasks" && <TasksTab agent={agent} />}
{activeTab === "settings" && ( {activeTab === "settings" && (
<SettingsTab <SettingsTab

View file

@ -131,7 +131,7 @@ export const en: LandingDict = {
{ {
title: "Create your first agent", title: "Create your first agent",
description: description:
"Give it a name, write instructions, attach skills, and set triggers. Choose when it activates: on assignment, on comment, or on mention.", "Give it a name, write instructions, and attach skills. Agents automatically activate on assignment, on comment, or on mention.",
}, },
{ {
title: "Assign an issue and watch it work", title: "Assign an issue and watch it work",
@ -385,7 +385,7 @@ export const en: LandingDict = {
title: "Core Platform", title: "Core Platform",
changes: [ changes: [
"Multi-workspace switching and creation", "Multi-workspace switching and creation",
"Agent management UI with skills, tools, and triggers", "Agent management UI with skills",
"Unified agent SDK supporting Claude Code and Codex backends", "Unified agent SDK supporting Claude Code and Codex backends",
"Comment CRUD with real-time WebSocket updates", "Comment CRUD with real-time WebSocket updates",
"Task service layer and daemon REST protocol", "Task service layer and daemon REST protocol",

View file

@ -4,8 +4,6 @@ export type AgentRuntimeMode = "local" | "cloud";
export type AgentVisibility = "workspace" | "private"; export type AgentVisibility = "workspace" | "private";
export type AgentTriggerType = "on_assign" | "on_comment" | "scheduled";
export interface RuntimeDevice { export interface RuntimeDevice {
id: string; id: string;
workspace_id: string; workspace_id: string;
@ -23,22 +21,6 @@ export interface RuntimeDevice {
export type AgentRuntime = RuntimeDevice; export type AgentRuntime = RuntimeDevice;
export interface AgentTool {
id: string;
name: string;
description: string;
auth_type: "oauth" | "api_key" | "none";
connected: boolean;
config: Record<string, unknown>;
}
export interface AgentTrigger {
id: string;
type: AgentTriggerType;
enabled: boolean;
config: Record<string, unknown> | null;
}
export interface AgentTask { export interface AgentTask {
id: string; id: string;
agent_id: string; agent_id: string;
@ -69,8 +51,6 @@ export interface Agent {
max_concurrent_tasks: number; max_concurrent_tasks: number;
owner_id: string | null; owner_id: string | null;
skills: Skill[]; skills: Skill[];
tools: AgentTool[];
triggers: AgentTrigger[];
created_at: string; created_at: string;
updated_at: string; updated_at: string;
archived_at: string | null; archived_at: string | null;
@ -86,8 +66,6 @@ export interface CreateAgentRequest {
runtime_config?: Record<string, unknown>; runtime_config?: Record<string, unknown>;
visibility?: AgentVisibility; visibility?: AgentVisibility;
max_concurrent_tasks?: number; max_concurrent_tasks?: number;
tools?: AgentTool[];
triggers?: AgentTrigger[];
} }
export interface UpdateAgentRequest { export interface UpdateAgentRequest {
@ -100,8 +78,6 @@ export interface UpdateAgentRequest {
visibility?: AgentVisibility; visibility?: AgentVisibility;
status?: AgentStatus; status?: AgentStatus;
max_concurrent_tasks?: number; max_concurrent_tasks?: number;
tools?: AgentTool[];
triggers?: AgentTrigger[];
} }
// Skills // Skills

View file

@ -4,9 +4,6 @@ export type {
AgentStatus, AgentStatus,
AgentRuntimeMode, AgentRuntimeMode,
AgentVisibility, AgentVisibility,
AgentTriggerType,
AgentTool,
AgentTrigger,
AgentTask, AgentTask,
AgentRuntime, AgentRuntime,
RuntimeDevice, RuntimeDevice,

View file

@ -58,8 +58,6 @@ export const mockAgents: Agent[] = [
max_concurrent_tasks: 3, max_concurrent_tasks: 3,
owner_id: null, owner_id: null,
skills: [], skills: [],
tools: [],
triggers: [],
created_at: "2026-01-01T00:00:00Z", created_at: "2026-01-01T00:00:00Z",
updated_at: "2026-01-01T00:00:00Z", updated_at: "2026-01-01T00:00:00Z",
archived_at: null, archived_at: null,

View file

@ -139,9 +139,9 @@ func setupIntegrationTestFixture(ctx context.Context, pool *pgxpool.Pool) (strin
if _, err := pool.Exec(ctx, ` if _, err := pool.Exec(ctx, `
INSERT INTO agent ( INSERT INTO agent (
workspace_id, name, description, runtime_mode, runtime_config, workspace_id, name, description, runtime_mode, runtime_config,
runtime_id, visibility, max_concurrent_tasks, owner_id, tools, triggers runtime_id, visibility, max_concurrent_tasks, owner_id
) )
VALUES ($1, $2, '', 'cloud', '{}'::jsonb, $3, 'workspace', 1, $4, '[]'::jsonb, '[]'::jsonb) VALUES ($1, $2, '', 'cloud', '{}'::jsonb, $3, 'workspace', 1, $4)
`, workspaceID, "Integration Test Agent", runtimeID, userID); err != nil { `, workspaceID, "Integration Test Agent", runtimeID, userID); err != nil {
return "", "", err return "", "", err
} }

View file

@ -28,8 +28,6 @@ type AgentResponse struct {
MaxConcurrentTasks int32 `json:"max_concurrent_tasks"` MaxConcurrentTasks int32 `json:"max_concurrent_tasks"`
OwnerID *string `json:"owner_id"` OwnerID *string `json:"owner_id"`
Skills []SkillResponse `json:"skills"` Skills []SkillResponse `json:"skills"`
Tools any `json:"tools"`
Triggers any `json:"triggers"`
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
ArchivedAt *string `json:"archived_at"` ArchivedAt *string `json:"archived_at"`
@ -45,22 +43,6 @@ func agentToResponse(a db.Agent) AgentResponse {
rc = map[string]any{} rc = map[string]any{}
} }
var tools any
if a.Tools != nil {
json.Unmarshal(a.Tools, &tools)
}
if tools == nil {
tools = []any{}
}
var triggers any
if a.Triggers != nil {
json.Unmarshal(a.Triggers, &triggers)
}
if triggers == nil {
triggers = []any{}
}
return AgentResponse{ return AgentResponse{
ID: uuidToString(a.ID), ID: uuidToString(a.ID),
WorkspaceID: uuidToString(a.WorkspaceID), WorkspaceID: uuidToString(a.WorkspaceID),
@ -76,8 +58,6 @@ func agentToResponse(a db.Agent) AgentResponse {
MaxConcurrentTasks: a.MaxConcurrentTasks, MaxConcurrentTasks: a.MaxConcurrentTasks,
OwnerID: uuidToPtr(a.OwnerID), OwnerID: uuidToPtr(a.OwnerID),
Skills: []SkillResponse{}, Skills: []SkillResponse{},
Tools: tools,
Triggers: triggers,
CreatedAt: timestampToString(a.CreatedAt), CreatedAt: timestampToString(a.CreatedAt),
UpdatedAt: timestampToString(a.UpdatedAt), UpdatedAt: timestampToString(a.UpdatedAt),
ArchivedAt: timestampToPtr(a.ArchivedAt), ArchivedAt: timestampToPtr(a.ArchivedAt),
@ -221,8 +201,6 @@ type CreateAgentRequest struct {
RuntimeConfig any `json:"runtime_config"` RuntimeConfig any `json:"runtime_config"`
Visibility string `json:"visibility"` Visibility string `json:"visibility"`
MaxConcurrentTasks int32 `json:"max_concurrent_tasks"` MaxConcurrentTasks int32 `json:"max_concurrent_tasks"`
Tools any `json:"tools"`
Triggers any `json:"triggers"`
} }
func (h *Handler) CreateAgent(w http.ResponseWriter, r *http.Request) { func (h *Handler) CreateAgent(w http.ResponseWriter, r *http.Request) {
@ -268,16 +246,6 @@ func (h *Handler) CreateAgent(w http.ResponseWriter, r *http.Request) {
rc = []byte("{}") rc = []byte("{}")
} }
tools, _ := json.Marshal(req.Tools)
if req.Tools == nil {
tools = []byte("[]")
}
triggers, _ := json.Marshal(req.Triggers)
if req.Triggers == nil {
triggers = defaultAgentTriggers()
}
agent, err := h.Queries.CreateAgent(r.Context(), db.CreateAgentParams{ agent, err := h.Queries.CreateAgent(r.Context(), db.CreateAgentParams{
WorkspaceID: parseUUID(workspaceID), WorkspaceID: parseUUID(workspaceID),
Name: req.Name, Name: req.Name,
@ -290,8 +258,6 @@ func (h *Handler) CreateAgent(w http.ResponseWriter, r *http.Request) {
Visibility: req.Visibility, Visibility: req.Visibility,
MaxConcurrentTasks: req.MaxConcurrentTasks, MaxConcurrentTasks: req.MaxConcurrentTasks,
OwnerID: parseUUID(ownerID), OwnerID: parseUUID(ownerID),
Tools: tools,
Triggers: triggers,
}) })
if err != nil { if err != nil {
slog.Warn("create agent failed", append(logger.RequestAttrs(r), "error", err, "workspace_id", workspaceID)...) slog.Warn("create agent failed", append(logger.RequestAttrs(r), "error", err, "workspace_id", workspaceID)...)
@ -323,8 +289,6 @@ type UpdateAgentRequest struct {
Visibility *string `json:"visibility"` Visibility *string `json:"visibility"`
Status *string `json:"status"` Status *string `json:"status"`
MaxConcurrentTasks *int32 `json:"max_concurrent_tasks"` MaxConcurrentTasks *int32 `json:"max_concurrent_tasks"`
Tools any `json:"tools"`
Triggers any `json:"triggers"`
} }
// canManageAgent checks whether the current user can update or archive an agent. // canManageAgent checks whether the current user can update or archive an agent.
@ -401,14 +365,6 @@ func (h *Handler) UpdateAgent(w http.ResponseWriter, r *http.Request) {
if req.MaxConcurrentTasks != nil { if req.MaxConcurrentTasks != nil {
params.MaxConcurrentTasks = pgtype.Int4{Int32: *req.MaxConcurrentTasks, Valid: true} params.MaxConcurrentTasks = pgtype.Int4{Int32: *req.MaxConcurrentTasks, Valid: true}
} }
if req.Tools != nil {
tools, _ := json.Marshal(req.Tools)
params.Tools = tools
}
if req.Triggers != nil {
triggers, _ := json.Marshal(req.Triggers)
params.Triggers = triggers
}
agent, err := h.Queries.UpdateAgent(r.Context(), params) agent, err := h.Queries.UpdateAgent(r.Context(), params)
if err != nil { if err != nil {

View file

@ -418,10 +418,6 @@ func (h *Handler) enqueueMentionedAgentTasks(ctx context.Context, issue db.Issue
} }
} }
} }
// Check if the agent has on_mention trigger enabled.
if !agentHasTriggerEnabled(agent.Triggers, "on_mention") {
continue
}
// Dedup: skip if this agent already has a pending task for this issue. // Dedup: skip if this agent already has a pending task for this issue.
hasPending, err := h.Queries.HasPendingTaskForIssueAndAgent(ctx, db.HasPendingTaskForIssueAndAgentParams{ hasPending, err := h.Queries.HasPendingTaskForIssueAndAgent(ctx, db.HasPendingTaskForIssueAndAgentParams{
IssueID: issue.ID, IssueID: issue.ID,

View file

@ -118,9 +118,9 @@ func setupHandlerTestFixture(ctx context.Context, pool *pgxpool.Pool) (string, s
if _, err := pool.Exec(ctx, ` if _, err := pool.Exec(ctx, `
INSERT INTO agent ( INSERT INTO agent (
workspace_id, name, description, runtime_mode, runtime_config, workspace_id, name, description, runtime_mode, runtime_config,
runtime_id, visibility, max_concurrent_tasks, owner_id, tools, triggers runtime_id, visibility, max_concurrent_tasks, owner_id
) )
VALUES ($1, $2, '', 'cloud', '{}'::jsonb, $3, 'workspace', 1, $4, '[]'::jsonb, '[]'::jsonb) VALUES ($1, $2, '', 'cloud', '{}'::jsonb, $3, 'workspace', 1, $4)
`, workspaceID, "Handler Test Agent", runtimeID, userID); err != nil { `, workspaceID, "Handler Test Agent", runtimeID, userID); err != nil {
return "", "", err return "", "", err
} }

View file

@ -39,23 +39,6 @@ type IssueResponse struct {
Attachments []AttachmentResponse `json:"attachments,omitempty"` Attachments []AttachmentResponse `json:"attachments,omitempty"`
} }
type agentTriggerSnapshot struct {
Type string `json:"type"`
Enabled bool `json:"enabled"`
Config map[string]any `json:"config"`
}
// defaultAgentTriggers returns the default trigger config for new agents:
// all three triggers explicitly enabled.
func defaultAgentTriggers() []byte {
b, _ := json.Marshal([]agentTriggerSnapshot{
{Type: "on_assign", Enabled: true},
{Type: "on_comment", Enabled: true},
{Type: "on_mention", Enabled: true},
})
return b
}
func issueToResponse(i db.Issue, issuePrefix string) IssueResponse { func issueToResponse(i db.Issue, issuePrefix string) IssueResponse {
identifier := issuePrefix + "-" + strconv.Itoa(int(i.Number)) identifier := issuePrefix + "-" + strconv.Itoa(int(i.Number))
return IssueResponse{ return IssueResponse{
@ -549,8 +532,9 @@ func (h *Handler) canAssignAgent(ctx context.Context, r *http.Request, agentID,
// the assigned agent. No status gate — assignment is an explicit human action, // the assigned agent. No status gate — assignment is an explicit human action,
// so it should trigger regardless of issue status (e.g. assigning an agent to // so it should trigger regardless of issue status (e.g. assigning an agent to
// a done issue to fix a discovered problem). // a done issue to fix a discovered problem).
// All trigger types (on_assign, on_comment, on_mention) are always enabled.
func (h *Handler) shouldEnqueueAgentTask(ctx context.Context, issue db.Issue) bool { func (h *Handler) shouldEnqueueAgentTask(ctx context.Context, issue db.Issue) bool {
return h.isAgentTriggerEnabled(ctx, issue, "on_assign") return h.isAgentAssigneeReady(ctx, issue)
} }
// shouldEnqueueOnComment returns true if a member comment on this issue should // shouldEnqueueOnComment returns true if a member comment on this issue should
@ -561,7 +545,7 @@ func (h *Handler) shouldEnqueueOnComment(ctx context.Context, issue db.Issue) bo
if issue.Status == "done" || issue.Status == "cancelled" { if issue.Status == "done" || issue.Status == "cancelled" {
return false return false
} }
if !h.isAgentTriggerEnabled(ctx, issue, "on_comment") { if !h.isAgentAssigneeReady(ctx, issue) {
return false return false
} }
// Coalescing queue: allow enqueue when a task is running (so the agent // Coalescing queue: allow enqueue when a task is running (so the agent
@ -574,10 +558,9 @@ func (h *Handler) shouldEnqueueOnComment(ctx context.Context, issue db.Issue) bo
return true return true
} }
// isAgentTriggerEnabled checks if an issue is assigned to an agent with a // isAgentAssigneeReady checks if an issue is assigned to an active agent
// specific trigger type enabled. Returns true if the agent has no triggers // with a valid runtime.
// configured (default-enabled behavior for backwards compatibility). func (h *Handler) isAgentAssigneeReady(ctx context.Context, issue db.Issue) bool {
func (h *Handler) isAgentTriggerEnabled(ctx context.Context, issue db.Issue, triggerType string) bool {
if !issue.AssigneeType.Valid || issue.AssigneeType.String != "agent" || !issue.AssigneeID.Valid { if !issue.AssigneeType.Valid || issue.AssigneeType.String != "agent" || !issue.AssigneeID.Valid {
return false return false
} }
@ -587,43 +570,7 @@ func (h *Handler) isAgentTriggerEnabled(ctx context.Context, issue db.Issue, tri
return false return false
} }
return agentHasTriggerEnabled(agent.Triggers, triggerType) return true
}
// isAgentMentionTriggerEnabled checks if a specific agent has the on_mention
// trigger enabled. Unlike isAgentTriggerEnabled, this takes an explicit agent
// ID rather than deriving it from the issue assignee.
func (h *Handler) isAgentMentionTriggerEnabled(ctx context.Context, agentID pgtype.UUID) bool {
agent, err := h.Queries.GetAgent(ctx, agentID)
if err != nil || !agent.RuntimeID.Valid {
return false
}
return agentHasTriggerEnabled(agent.Triggers, "on_mention")
}
// agentHasTriggerEnabled checks if a trigger type is enabled in the agent's
// trigger config. Returns true (default-enabled) when the triggers list is
// empty or does not contain the requested type — for backwards compatibility
// with agents created before explicit trigger config was introduced.
func agentHasTriggerEnabled(raw []byte, triggerType string) bool {
if raw == nil || len(raw) == 0 {
return true
}
var triggers []agentTriggerSnapshot
if err := json.Unmarshal(raw, &triggers); err != nil {
return false
}
if len(triggers) == 0 {
return true // Empty array = default-enabled (backwards compat)
}
for _, trigger := range triggers {
if trigger.Type == triggerType {
return trigger.Enabled
}
}
return true // Trigger type not configured = enabled by default
} }
func (h *Handler) DeleteIssue(w http.ResponseWriter, r *http.Request) { func (h *Handler) DeleteIssue(w http.ResponseWriter, r *http.Request) {

View file

@ -1,7 +1,6 @@
package handler package handler
import ( import (
"encoding/json"
"fmt" "fmt"
"testing" "testing"
@ -268,119 +267,3 @@ func TestOnCommentTriggerDecision(t *testing.T) {
} }
} }
// -------------------------------------------------------------------
// agentHasTriggerEnabled
// -------------------------------------------------------------------
func TestAgentHasTriggerEnabled(t *testing.T) {
tests := []struct {
name string
raw []byte
triggerType string
want bool
}{
{
name: "nil triggers → enabled (backwards compat)",
raw: nil,
triggerType: "on_comment",
want: true,
},
{
name: "empty byte slice → enabled",
raw: []byte{},
triggerType: "on_comment",
want: true,
},
{
name: "empty JSON array → enabled (backwards compat)",
raw: []byte("[]"),
triggerType: "on_comment",
want: true,
},
{
name: "on_comment explicitly enabled",
raw: mustJSON([]agentTriggerSnapshot{{Type: "on_comment", Enabled: true}}),
triggerType: "on_comment",
want: true,
},
{
name: "on_comment explicitly disabled",
raw: mustJSON([]agentTriggerSnapshot{{Type: "on_comment", Enabled: false}}),
triggerType: "on_comment",
want: false,
},
{
name: "on_mention not configured but others are → enabled by default",
raw: mustJSON([]agentTriggerSnapshot{{Type: "on_comment", Enabled: true}}),
triggerType: "on_mention",
want: true,
},
{
name: "invalid JSON → disabled (fail safe)",
raw: []byte("{bad json"),
triggerType: "on_comment",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := agentHasTriggerEnabled(tt.raw, tt.triggerType)
if got != tt.want {
t.Errorf("agentHasTriggerEnabled() = %v, want %v", got, tt.want)
}
})
}
}
// -------------------------------------------------------------------
// defaultAgentTriggers
// -------------------------------------------------------------------
func TestDefaultAgentTriggers(t *testing.T) {
raw := defaultAgentTriggers()
var triggers []agentTriggerSnapshot
if err := json.Unmarshal(raw, &triggers); err != nil {
t.Fatalf("failed to unmarshal default triggers: %v", err)
}
if len(triggers) != 3 {
t.Fatalf("expected 3 default triggers, got %d", len(triggers))
}
expected := map[string]bool{
"on_assign": true,
"on_comment": true,
"on_mention": true,
}
for _, tr := range triggers {
want, ok := expected[tr.Type]
if !ok {
t.Errorf("unexpected trigger type: %s", tr.Type)
continue
}
if tr.Enabled != want {
t.Errorf("trigger %s: enabled = %v, want %v", tr.Type, tr.Enabled, want)
}
delete(expected, tr.Type)
}
for typ := range expected {
t.Errorf("missing trigger type: %s", typ)
}
// Verify all triggers are enabled via agentHasTriggerEnabled
for _, typ := range []string{"on_assign", "on_comment", "on_mention"} {
if !agentHasTriggerEnabled(raw, typ) {
t.Errorf("agentHasTriggerEnabled(default, %q) = false, want true", typ)
}
}
}
func mustJSON(v any) []byte {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
return b
}

View file

@ -531,14 +531,6 @@ func agentToMap(a db.Agent) map[string]any {
if a.RuntimeConfig != nil { if a.RuntimeConfig != nil {
json.Unmarshal(a.RuntimeConfig, &rc) json.Unmarshal(a.RuntimeConfig, &rc)
} }
var tools any
if a.Tools != nil {
json.Unmarshal(a.Tools, &tools)
}
var triggers any
if a.Triggers != nil {
json.Unmarshal(a.Triggers, &triggers)
}
return map[string]any{ return map[string]any{
"id": util.UUIDToString(a.ID), "id": util.UUIDToString(a.ID),
"workspace_id": util.UUIDToString(a.WorkspaceID), "workspace_id": util.UUIDToString(a.WorkspaceID),
@ -553,8 +545,6 @@ func agentToMap(a db.Agent) map[string]any {
"max_concurrent_tasks": a.MaxConcurrentTasks, "max_concurrent_tasks": a.MaxConcurrentTasks,
"owner_id": util.UUIDToPtr(a.OwnerID), "owner_id": util.UUIDToPtr(a.OwnerID),
"skills": []any{}, "skills": []any{},
"tools": tools,
"triggers": triggers,
"created_at": util.TimestampToString(a.CreatedAt), "created_at": util.TimestampToString(a.CreatedAt),
"updated_at": util.TimestampToString(a.UpdatedAt), "updated_at": util.TimestampToString(a.UpdatedAt),
"archived_at": util.TimestampToPtr(a.ArchivedAt), "archived_at": util.TimestampToPtr(a.ArchivedAt),

View file

@ -0,0 +1,3 @@
-- Re-add the triggers and tools columns to agent table.
ALTER TABLE agent ADD COLUMN triggers JSONB NOT NULL DEFAULT '[]';
ALTER TABLE agent ADD COLUMN tools JSONB NOT NULL DEFAULT '[]';

View file

@ -0,0 +1,5 @@
-- Remove the triggers and tools columns from agent table.
-- Trigger behavior (on_assign, on_comment, on_mention) is now always enabled (hardcoded).
-- Tools was a placeholder field never used at runtime.
ALTER TABLE agent DROP COLUMN IF EXISTS triggers;
ALTER TABLE agent DROP COLUMN IF EXISTS tools;

View file

@ -14,7 +14,7 @@ import (
const archiveAgent = `-- name: ArchiveAgent :one const archiveAgent = `-- name: ArchiveAgent :one
UPDATE agent SET archived_at = now(), archived_by = $2, updated_at = now() UPDATE agent SET archived_at = now(), archived_by = $2, updated_at = now()
WHERE id = $1 WHERE id = $1
RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by
` `
type ArchiveAgentParams struct { type ArchiveAgentParams struct {
@ -39,8 +39,6 @@ func (q *Queries) ArchiveAgent(ctx context.Context, arg ArchiveAgentParams) (Age
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -207,9 +205,9 @@ const createAgent = `-- name: CreateAgent :one
INSERT INTO agent ( INSERT INTO agent (
workspace_id, name, description, avatar_url, runtime_mode, workspace_id, name, description, avatar_url, runtime_mode,
runtime_config, runtime_id, visibility, max_concurrent_tasks, owner_id, runtime_config, runtime_id, visibility, max_concurrent_tasks, owner_id,
tools, triggers, instructions instructions
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by
` `
type CreateAgentParams struct { type CreateAgentParams struct {
@ -223,8 +221,6 @@ type CreateAgentParams struct {
Visibility string `json:"visibility"` Visibility string `json:"visibility"`
MaxConcurrentTasks int32 `json:"max_concurrent_tasks"` MaxConcurrentTasks int32 `json:"max_concurrent_tasks"`
OwnerID pgtype.UUID `json:"owner_id"` OwnerID pgtype.UUID `json:"owner_id"`
Tools []byte `json:"tools"`
Triggers []byte `json:"triggers"`
Instructions string `json:"instructions"` Instructions string `json:"instructions"`
} }
@ -240,8 +236,6 @@ func (q *Queries) CreateAgent(ctx context.Context, arg CreateAgentParams) (Agent
arg.Visibility, arg.Visibility,
arg.MaxConcurrentTasks, arg.MaxConcurrentTasks,
arg.OwnerID, arg.OwnerID,
arg.Tools,
arg.Triggers,
arg.Instructions, arg.Instructions,
) )
var i Agent var i Agent
@ -259,8 +253,6 @@ func (q *Queries) CreateAgent(ctx context.Context, arg CreateAgentParams) (Agent
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -392,7 +384,7 @@ func (q *Queries) FailStaleTasks(ctx context.Context, arg FailStaleTasksParams)
} }
const getAgent = `-- name: GetAgent :one const getAgent = `-- name: GetAgent :one
SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by FROM agent SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by FROM agent
WHERE id = $1 WHERE id = $1
` `
@ -413,8 +405,6 @@ func (q *Queries) GetAgent(ctx context.Context, id pgtype.UUID) (Agent, error) {
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -424,7 +414,7 @@ func (q *Queries) GetAgent(ctx context.Context, id pgtype.UUID) (Agent, error) {
} }
const getAgentInWorkspace = `-- name: GetAgentInWorkspace :one const getAgentInWorkspace = `-- name: GetAgentInWorkspace :one
SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by FROM agent SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by FROM agent
WHERE id = $1 AND workspace_id = $2 WHERE id = $1 AND workspace_id = $2
` `
@ -450,8 +440,6 @@ func (q *Queries) GetAgentInWorkspace(ctx context.Context, arg GetAgentInWorkspa
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -650,7 +638,7 @@ func (q *Queries) ListAgentTasks(ctx context.Context, agentID pgtype.UUID) ([]Ag
} }
const listAgents = `-- name: ListAgents :many const listAgents = `-- name: ListAgents :many
SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by FROM agent SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by FROM agent
WHERE workspace_id = $1 AND archived_at IS NULL WHERE workspace_id = $1 AND archived_at IS NULL
ORDER BY created_at ASC ORDER BY created_at ASC
` `
@ -678,8 +666,6 @@ func (q *Queries) ListAgents(ctx context.Context, workspaceID pgtype.UUID) ([]Ag
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -696,7 +682,7 @@ func (q *Queries) ListAgents(ctx context.Context, workspaceID pgtype.UUID) ([]Ag
} }
const listAllAgents = `-- name: ListAllAgents :many const listAllAgents = `-- name: ListAllAgents :many
SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by FROM agent SELECT id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by FROM agent
WHERE workspace_id = $1 WHERE workspace_id = $1
ORDER BY created_at ASC ORDER BY created_at ASC
` `
@ -724,8 +710,6 @@ func (q *Queries) ListAllAgents(ctx context.Context, workspaceID pgtype.UUID) ([
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -830,7 +814,7 @@ func (q *Queries) ListTasksByIssue(ctx context.Context, issueID pgtype.UUID) ([]
const restoreAgent = `-- name: RestoreAgent :one const restoreAgent = `-- name: RestoreAgent :one
UPDATE agent SET archived_at = NULL, archived_by = NULL, updated_at = now() UPDATE agent SET archived_at = NULL, archived_by = NULL, updated_at = now()
WHERE id = $1 WHERE id = $1
RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by
` `
func (q *Queries) RestoreAgent(ctx context.Context, id pgtype.UUID) (Agent, error) { func (q *Queries) RestoreAgent(ctx context.Context, id pgtype.UUID) (Agent, error) {
@ -850,8 +834,6 @@ func (q *Queries) RestoreAgent(ctx context.Context, id pgtype.UUID) (Agent, erro
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -902,12 +884,10 @@ UPDATE agent SET
visibility = COALESCE($8, visibility), visibility = COALESCE($8, visibility),
status = COALESCE($9, status), status = COALESCE($9, status),
max_concurrent_tasks = COALESCE($10, max_concurrent_tasks), max_concurrent_tasks = COALESCE($10, max_concurrent_tasks),
tools = COALESCE($11, tools), instructions = COALESCE($11, instructions),
triggers = COALESCE($12, triggers),
instructions = COALESCE($13, instructions),
updated_at = now() updated_at = now()
WHERE id = $1 WHERE id = $1
RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by
` `
type UpdateAgentParams struct { type UpdateAgentParams struct {
@ -921,8 +901,6 @@ type UpdateAgentParams struct {
Visibility pgtype.Text `json:"visibility"` Visibility pgtype.Text `json:"visibility"`
Status pgtype.Text `json:"status"` Status pgtype.Text `json:"status"`
MaxConcurrentTasks pgtype.Int4 `json:"max_concurrent_tasks"` MaxConcurrentTasks pgtype.Int4 `json:"max_concurrent_tasks"`
Tools []byte `json:"tools"`
Triggers []byte `json:"triggers"`
Instructions pgtype.Text `json:"instructions"` Instructions pgtype.Text `json:"instructions"`
} }
@ -938,8 +916,6 @@ func (q *Queries) UpdateAgent(ctx context.Context, arg UpdateAgentParams) (Agent
arg.Visibility, arg.Visibility,
arg.Status, arg.Status,
arg.MaxConcurrentTasks, arg.MaxConcurrentTasks,
arg.Tools,
arg.Triggers,
arg.Instructions, arg.Instructions,
) )
var i Agent var i Agent
@ -957,8 +933,6 @@ func (q *Queries) UpdateAgent(ctx context.Context, arg UpdateAgentParams) (Agent
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,
@ -970,7 +944,7 @@ func (q *Queries) UpdateAgent(ctx context.Context, arg UpdateAgentParams) (Agent
const updateAgentStatus = `-- name: UpdateAgentStatus :one const updateAgentStatus = `-- name: UpdateAgentStatus :one
UPDATE agent SET status = $2, updated_at = now() UPDATE agent SET status = $2, updated_at = now()
WHERE id = $1 WHERE id = $1
RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, tools, triggers, runtime_id, instructions, archived_at, archived_by RETURNING id, workspace_id, name, avatar_url, runtime_mode, runtime_config, visibility, status, max_concurrent_tasks, owner_id, created_at, updated_at, description, runtime_id, instructions, archived_at, archived_by
` `
type UpdateAgentStatusParams struct { type UpdateAgentStatusParams struct {
@ -995,8 +969,6 @@ func (q *Queries) UpdateAgentStatus(ctx context.Context, arg UpdateAgentStatusPa
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Description, &i.Description,
&i.Tools,
&i.Triggers,
&i.RuntimeID, &i.RuntimeID,
&i.Instructions, &i.Instructions,
&i.ArchivedAt, &i.ArchivedAt,

View file

@ -33,8 +33,6 @@ type Agent struct {
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Description string `json:"description"` Description string `json:"description"`
Tools []byte `json:"tools"`
Triggers []byte `json:"triggers"`
RuntimeID pgtype.UUID `json:"runtime_id"` RuntimeID pgtype.UUID `json:"runtime_id"`
Instructions string `json:"instructions"` Instructions string `json:"instructions"`
ArchivedAt pgtype.Timestamptz `json:"archived_at"` ArchivedAt pgtype.Timestamptz `json:"archived_at"`

View file

@ -20,8 +20,8 @@ WHERE id = $1 AND workspace_id = $2;
INSERT INTO agent ( INSERT INTO agent (
workspace_id, name, description, avatar_url, runtime_mode, workspace_id, name, description, avatar_url, runtime_mode,
runtime_config, runtime_id, visibility, max_concurrent_tasks, owner_id, runtime_config, runtime_id, visibility, max_concurrent_tasks, owner_id,
tools, triggers, instructions instructions
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING *; RETURNING *;
-- name: UpdateAgent :one -- name: UpdateAgent :one
@ -35,8 +35,6 @@ UPDATE agent SET
visibility = COALESCE(sqlc.narg('visibility'), visibility), visibility = COALESCE(sqlc.narg('visibility'), visibility),
status = COALESCE(sqlc.narg('status'), status), status = COALESCE(sqlc.narg('status'), status),
max_concurrent_tasks = COALESCE(sqlc.narg('max_concurrent_tasks'), max_concurrent_tasks), max_concurrent_tasks = COALESCE(sqlc.narg('max_concurrent_tasks'), max_concurrent_tasks),
tools = COALESCE(sqlc.narg('tools'), tools),
triggers = COALESCE(sqlc.narg('triggers'), triggers),
instructions = COALESCE(sqlc.narg('instructions'), instructions), instructions = COALESCE(sqlc.narg('instructions'), instructions),
updated_at = now() updated_at = now()
WHERE id = $1 WHERE id = $1