diff --git a/apps/rowboat/app/lib/client_utils.ts b/apps/rowboat/app/lib/client_utils.ts
index d47545b2..c74ac367 100644
--- a/apps/rowboat/app/lib/client_utils.ts
+++ b/apps/rowboat/app/lib/client_utils.ts
@@ -1,4 +1,5 @@
import { WorkflowTool, WorkflowAgent, WorkflowPrompt, WorkflowPipeline } from "./types/workflow_types";
+import { Message } from "./types/types";
import { z } from "zod";
const ZFallbackSchema = z.object({}).passthrough();
@@ -62,6 +63,36 @@ export function validateConfigChanges(configType: string, configChanges: Record<
testObject = {};
break;
}
+ case 'one_time_trigger': {
+ testObject = {
+ scheduledTime: new Date(0).toISOString(),
+ input: {
+ messages: [],
+ },
+ };
+ schema = z.object({
+ scheduledTime: z.string().min(1),
+ input: z.object({
+ messages: z.array(Message),
+ }),
+ }).passthrough();
+ break;
+ }
+ case 'recurring_trigger': {
+ testObject = {
+ cron: '* * * * *',
+ input: {
+ messages: [],
+ },
+ };
+ schema = z.object({
+ cron: z.string().min(1),
+ input: z.object({
+ messages: z.array(Message),
+ }),
+ }).passthrough();
+ break;
+ }
default:
return { error: `Unknown config type: ${configType}` };
}
diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx
index 55f950ae..850c3b5e 100644
--- a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx
+++ b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx
@@ -211,7 +211,7 @@ export function Action({
{action.config_type === 'tool' && toolkitLogo ? (
) : (
- action.config_type === 'agent' ? 'π§βπΌ' : action.config_type === 'tool' ? 'π οΈ' : action.config_type === 'pipeline' ? 'βοΈ' : action.config_type === 'start_agent' ? 'π' : action.config_type === 'prompt' ? 'π¬' : 'π¬'
+ action.config_type === 'agent' ? 'π§βπΌ' : action.config_type === 'tool' ? 'π οΈ' : action.config_type === 'pipeline' ? 'βοΈ' : action.config_type === 'start_agent' ? 'π' : action.config_type === 'prompt' ? 'π¬' : action.config_type === 'one_time_trigger' ? 'β°' : action.config_type === 'recurring_trigger' ? 'π' : 'π¬'
)}
@@ -379,7 +379,7 @@ export function StreamingAction({
}: {
action: {
action?: 'create_new' | 'edit' | 'delete';
- config_type?: 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent';
+ config_type?: 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent' | 'one_time_trigger' | 'recurring_trigger';
name?: string;
};
loading: boolean;
@@ -418,7 +418,7 @@ export function StreamingAction({
'bg-gray-200 text-gray-600': !action.action,
}
)}>
- {action.config_type === 'agent' ? 'π§βπΌ' : action.config_type === 'tool' ? 'π οΈ' : action.config_type === 'pipeline' ? 'βοΈ' : action.config_type === 'start_agent' ? 'π' : 'π¬'}
+ {action.config_type === 'agent' ? 'π§βπΌ' : action.config_type === 'tool' ? 'π οΈ' : action.config_type === 'pipeline' ? 'βοΈ' : action.config_type === 'start_agent' ? 'π' : action.config_type === 'one_time_trigger' ? 'β°' : action.config_type === 'recurring_trigger' ? 'π' : 'π¬'}
{action.action === 'create_new' ? 'Add' : action.action === 'edit' ? 'Edit' : 'Delete'} {action.config_type}: {action.name}
@@ -444,4 +444,4 @@ export function StreamingAction({
);
-}
\ No newline at end of file
+}
diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx
index 6ca63979..c3cb2c1b 100644
--- a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx
+++ b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx
@@ -71,7 +71,7 @@ function enrich(response: string): z.infer {
type: 'action',
action: {
action: metadata.action as 'create_new' | 'edit' | 'delete',
- config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent',
+ config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent' | 'one_time_trigger' | 'recurring_trigger',
name: metadata.name,
change_description: jsonData.change_description || '',
config_changes: {},
@@ -84,7 +84,7 @@ function enrich(response: string): z.infer {
type: 'action',
action: {
action: metadata.action as 'create_new' | 'edit' | 'delete',
- config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent',
+ config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent' | 'one_time_trigger' | 'recurring_trigger',
name: metadata.name,
change_description: jsonData.change_description || '',
config_changes: result.changes
@@ -100,7 +100,7 @@ function enrich(response: string): z.infer {
type: 'streaming_action',
action: {
action: (metadata.action as 'create_new' | 'edit' | 'delete') || undefined,
- config_type: (metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent') || undefined,
+ config_type: (metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline' | 'start_agent' | 'one_time_trigger' | 'recurring_trigger') || undefined,
name: metadata.name
}
};
@@ -262,6 +262,26 @@ function AssistantMessage({
fromCopilot: true
});
break;
+ case 'one_time_trigger':
+ dispatch({
+ type: 'add_one_time_trigger',
+ trigger: {
+ name: action.name,
+ ...action.config_changes
+ },
+ fromCopilot: true
+ });
+ break;
+ case 'recurring_trigger':
+ dispatch({
+ type: 'add_recurring_trigger',
+ trigger: {
+ name: action.name,
+ ...action.config_changes
+ },
+ fromCopilot: true
+ });
+ break;
}
} else if (action.action === 'edit') {
switch (action.config_type) {
@@ -603,4 +623,4 @@ export function Messages({
);
-}
\ No newline at end of file
+}
diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
index 476d8366..4f70fe98 100644
--- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
+++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx
@@ -103,6 +103,15 @@ interface StateItem {
lastUpdatedAt: string;
isLive: boolean;
agentInstructionsChanged: boolean;
+ pendingTriggers?: Array<{
+ type: 'one_time' | 'recurring';
+ name: string;
+ scheduledTime?: string;
+ cron?: string;
+ input: {
+ messages: z.infer[];
+ };
+ }>;
}
interface State {
@@ -144,6 +153,28 @@ export type Action = {
pipeline: Partial>;
defaultModel?: string;
fromCopilot?: boolean;
+} | {
+ type: "add_one_time_trigger";
+ trigger: {
+ name: string;
+ scheduledTime: string;
+ input: {
+ messages: z.infer[];
+ };
+ };
+ fromCopilot?: boolean;
+} | {
+ type: "add_recurring_trigger";
+ trigger: {
+ name: string;
+ cron: string;
+ input: {
+ messages: z.infer[];
+ };
+ };
+ fromCopilot?: boolean;
+} | {
+ type: "clear_pending_triggers";
} | {
type: "select_agent";
name: string;
@@ -587,6 +618,30 @@ function reducer(state: State, action: Action): State {
draft.chatKey++;
break;
}
+ case "add_one_time_trigger": {
+ // Mark that we need to create a trigger - the actual API call will be handled outside the reducer
+ draft.pendingTriggers = draft.pendingTriggers || [];
+ draft.pendingTriggers.push({
+ type: 'one_time',
+ ...action.trigger
+ });
+ draft.pendingChanges = true;
+ break;
+ }
+ case "add_recurring_trigger": {
+ // Mark that we need to create a trigger - the actual API call will be handled outside the reducer
+ draft.pendingTriggers = draft.pendingTriggers || [];
+ draft.pendingTriggers.push({
+ type: 'recurring',
+ ...action.trigger
+ });
+ draft.pendingChanges = true;
+ break;
+ }
+ case "clear_pending_triggers": {
+ draft.pendingTriggers = [];
+ break;
+ }
case "delete_agent":
// Remove the agent
draft.workflow.agents = draft.workflow.agents.filter(
@@ -1315,6 +1370,38 @@ export function WorkflowEditor({
}
}, [state.present.agentInstructionsChanged, markAgentInstructionsChanged]);
+ // Handle pending trigger creation
+ useEffect(() => {
+ if (state.present.pendingTriggers && state.present.pendingTriggers.length > 0) {
+ const processTriggers = async () => {
+ for (const trigger of state.present.pendingTriggers!) {
+ try {
+ if (trigger.type === 'one_time') {
+ const { createScheduledJobRule } = await import('@/app/actions/scheduled-job-rules.actions');
+ await createScheduledJobRule({
+ projectId,
+ input: trigger.input,
+ scheduledTime: trigger.scheduledTime!
+ });
+ } else if (trigger.type === 'recurring') {
+ const { createRecurringJobRule } = await import('@/app/actions/recurring-job-rules.actions');
+ await createRecurringJobRule({
+ projectId,
+ input: trigger.input,
+ cron: trigger.cron!
+ });
+ }
+ } catch (error) {
+ console.error(`Failed to create ${trigger.type} trigger:`, error);
+ }
+ }
+ // Clear pending triggers after processing
+ dispatch({ type: "clear_pending_triggers" });
+ };
+ processTriggers();
+ }
+ }, [state.present.pendingTriggers, projectId]);
+
function handleSelectAgent(name: string) {
dispatch({ type: "select_agent", name });
}
diff --git a/apps/rowboat/src/application/lib/copilot/copilot_multi_agent_build.ts b/apps/rowboat/src/application/lib/copilot/copilot_multi_agent_build.ts
index b5a7b215..4a078a42 100644
--- a/apps/rowboat/src/application/lib/copilot/copilot_multi_agent_build.ts
+++ b/apps/rowboat/src/application/lib/copilot/copilot_multi_agent_build.ts
@@ -13,6 +13,8 @@ You can perform the following tasks:
5. Add, edit, or remove tools
6. Adding RAG data sources to agents
7. Create and manage pipelines (sequential agent workflows)
+8. Create One-Time Triggers (scheduled to run once at a specific time)
+9. Create Recurring Triggers (scheduled to run repeatedly using cron expressions)
Always aim to fully resolve the user's query before yielding. Only ask for clarification once, using up to 4 concise, bullet-point questions to understand the userβs objective and what they want the workflow to achieve.
@@ -193,6 +195,72 @@ Note: The agents have access to a tool called 'Generate Image'. This won't show
+
+
+## Section: Creating Triggers
+
+Triggers are automated mechanisms that activate your agents at specific times or intervals. You can create two types of triggers:
+
+### One-Time Triggers
+- Execute once at a specific date and time
+- Use config_type: "one_time_trigger"
+- Require scheduledTime (ISO datetime string) in config_changes
+- Require input.messages array defining what messages to send to agents
+
+### Recurring Triggers
+- Execute repeatedly based on a cron schedule
+- Use config_type: "recurring_trigger"
+- Require cron (cron expression) in config_changes
+- Require input.messages array defining what messages to send to agents
+
+### When to Create Triggers
+- User asks for scheduled automation (daily reports, weekly summaries)
+- User mentions specific times ("every morning at 9 AM", "next Friday at 2 PM")
+- User wants periodic tasks (monitoring, maintenance, data syncing)
+
+### Common Cron Patterns
+- "0 9 * * *" - Daily at 9:00 AM
+- "0 8 * * 1" - Every Monday at 8:00 AM
+- "*/15 * * * *" - Every 15 minutes
+- "0 0 1 * *" - First day of month at midnight
+
+### Example Trigger Actions
+
+CRITICAL: When creating triggers, follow the EXACT format shown below with comments above the JSON:
+- Put "action", "config_type", and "name" as comments (starting with //) ABOVE the JSON
+- The JSON should contain "change_description" and "config_changes"
+- Always use "action: create_new" for new triggers
+
+One-time trigger example (COPY THIS EXACT FORMAT):
+// action: create_new
+// config_type: one_time_trigger
+// name: Weekly Report - Dec 15
+{
+ "change_description": "Create a one-time trigger to generate weekly report on December 15th at 2 PM",
+ "config_changes": {
+ "scheduledTime": "2024-12-15T14:00:00Z",
+ "input": {
+ "messages": [{"role": "user", "content": "Generate the weekly performance report"}]
+ }
+ }
+}
+
+Recurring trigger example (COPY THIS EXACT FORMAT):
+// action: create_new
+// config_type: recurring_trigger
+// name: Daily Status Check
+{
+ "change_description": "Create a recurring trigger to check system status every morning at 9 AM",
+ "config_changes": {
+ "cron": "0 9 * * *",
+ "input": {
+ "messages": [{"role": "user", "content": "Check system status and alert if any issues found"}]
+ }
+ }
+}
+
+
+
## Section: Creating and Managing Pipelines
diff --git a/apps/rowboat/src/entities/models/copilot.ts b/apps/rowboat/src/entities/models/copilot.ts
index eeff9742..6317a787 100644
--- a/apps/rowboat/src/entities/models/copilot.ts
+++ b/apps/rowboat/src/entities/models/copilot.ts
@@ -21,7 +21,7 @@ export const CopilotAssistantMessageTextPart = z.object({
export const CopilotAssistantMessageActionPart = z.object({
type: z.literal("action"),
content: z.object({
- config_type: z.enum(['tool', 'agent', 'prompt', 'pipeline', 'start_agent']),
+ config_type: z.enum(['tool', 'agent', 'prompt', 'pipeline', 'start_agent', 'one_time_trigger', 'recurring_trigger']),
action: z.enum(['create_new', 'edit', 'delete']),
name: z.string(),
change_description: z.string(),