feat: add automation reactions event-trigger layer
This commit is contained in:
@@ -1082,6 +1082,7 @@ describe('configSchema automation', () => {
|
||||
const result = configSchema.parse(baseConfig);
|
||||
expect(result.automation).toBeDefined();
|
||||
expect(result.automation.delivery_mode).toBe('shared_session');
|
||||
expect(result.automation.reactions).toEqual([]);
|
||||
expect(result.automation.cron).toEqual([]);
|
||||
expect(result.automation.daily_briefing.enabled).toBe(false);
|
||||
expect(result.automation.daily_briefing.schedule).toBe('0 8 * * *');
|
||||
@@ -1129,6 +1130,36 @@ describe('configSchema automation', () => {
|
||||
expect(result.automation.cron[0].enabled).toBe(true); // default
|
||||
});
|
||||
|
||||
it('accepts reactions config with filters', () => {
|
||||
const result = configSchema.parse({
|
||||
...baseConfig,
|
||||
automation: {
|
||||
reactions: [{
|
||||
name: 'boss-email',
|
||||
on: ['gmail'],
|
||||
filter: {
|
||||
contains: 'boss@company.com',
|
||||
regex: 'urgent|asap',
|
||||
metadata: { from: 'boss@company.com' },
|
||||
},
|
||||
run: 'Summarize and propose next actions:\n\n{{text}}',
|
||||
}],
|
||||
},
|
||||
});
|
||||
expect(result.automation.reactions).toHaveLength(1);
|
||||
expect(result.automation.reactions[0]).toMatchObject({
|
||||
name: 'boss-email',
|
||||
enabled: true,
|
||||
on: ['gmail'],
|
||||
run: 'Summarize and propose next actions:\n\n{{text}}',
|
||||
});
|
||||
expect(result.automation.reactions[0].filter).toMatchObject({
|
||||
contains: 'boss@company.com',
|
||||
regex: 'urgent|asap',
|
||||
metadata: { from: 'boss@company.com' },
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects cron job with empty name', () => {
|
||||
expect(() => configSchema.parse({
|
||||
...baseConfig,
|
||||
|
||||
@@ -257,6 +257,25 @@ const mcpSchema = z.object({
|
||||
|
||||
const modelTierEnum = z.enum(['fast', 'default', 'complex', 'local']);
|
||||
|
||||
const reactionFilterSchema = z.object({
|
||||
/** Case-insensitive substring match against inbound message text. */
|
||||
contains: z.string().optional(),
|
||||
/** Case-insensitive regex match against inbound message text. */
|
||||
regex: z.string().optional(),
|
||||
/** Dot-path metadata constraints (exact string comparison). */
|
||||
metadata: z.record(z.string(), z.string()).optional(),
|
||||
}).optional();
|
||||
|
||||
const automationReactionSchema = z.object({
|
||||
name: z.string().min(1, 'Reaction name is required'),
|
||||
enabled: z.boolean().default(true),
|
||||
/** Source channels/events this rule applies to (e.g. gmail, webhook). */
|
||||
on: z.array(z.string().min(1)).default([]),
|
||||
filter: reactionFilterSchema,
|
||||
/** Prompt template to run when matched. Supports {{text}}, {{channel}}, {{sender_id}}, {{metadata.*}}. */
|
||||
run: z.string().min(1, 'Reaction run template is required'),
|
||||
});
|
||||
|
||||
const cronJobSchema = z.object({
|
||||
name: z.string().min(1, 'Cron job name is required'),
|
||||
schedule: z.string().min(1, 'Cron schedule is required'),
|
||||
@@ -422,6 +441,7 @@ const automationDeliveryModeSchema = z.enum(['shared_session', 'isolated_job', '
|
||||
const automationSchema = z.object({
|
||||
/** Session strategy for automation-triggered runs (cron/webhooks/gmail). */
|
||||
delivery_mode: automationDeliveryModeSchema.default('shared_session'),
|
||||
reactions: z.array(automationReactionSchema).default([]),
|
||||
cron: z.array(cronJobSchema).default([]),
|
||||
webhooks: z.array(webhookSchema).default([]),
|
||||
gmail: gmailSchema,
|
||||
@@ -999,6 +1019,7 @@ export type DailyBriefingConfig = z.infer<typeof dailyBriefingSchema>;
|
||||
export type MinioSyncTaskConfig = z.infer<typeof minioSyncTaskSchema>;
|
||||
export type MinioSyncAutomationConfig = z.infer<typeof minioSyncAutomationSchema>;
|
||||
export type AutomationDeliveryMode = z.infer<typeof automationDeliveryModeSchema>;
|
||||
export type AutomationReactionConfig = z.infer<typeof automationReactionSchema>;
|
||||
export type PairingCodeConfig = z.infer<typeof pairingSchema>;
|
||||
export type LogLevel = z.infer<typeof logLevelSchema>;
|
||||
export type AuditConfig = z.infer<typeof auditSchema>;
|
||||
|
||||
Reference in New Issue
Block a user