Merge remote-tracking branch 'origin/main' into feat/tanstack-query-migration
# Conflicts: # apps/web/app/(dashboard)/agents/page.tsx
This commit is contained in:
commit
8cf78b7a47
17 changed files with 36 additions and 803 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ export type {
|
||||||
AgentStatus,
|
AgentStatus,
|
||||||
AgentRuntimeMode,
|
AgentRuntimeMode,
|
||||||
AgentVisibility,
|
AgentVisibility,
|
||||||
AgentTriggerType,
|
|
||||||
AgentTool,
|
|
||||||
AgentTrigger,
|
|
||||||
AgentTask,
|
AgentTask,
|
||||||
AgentRuntime,
|
AgentRuntime,
|
||||||
RuntimeDevice,
|
RuntimeDevice,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
3
server/migrations/032_drop_agent_triggers.down.sql
Normal file
3
server/migrations/032_drop_agent_triggers.down.sql
Normal 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 '[]';
|
||||||
5
server/migrations/032_drop_agent_triggers.up.sql
Normal file
5
server/migrations/032_drop_agent_triggers.up.sql
Normal 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;
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue