Skip to content

Commit 56f019d

Browse files
feat(app): implement automated tasks (#1550)
* feat(app): add automated tasks part 1 * feat(automation): implement task automation system with API integration and UI components * feat(app): improve UI for task page, including attachments and comments * feat(app): add exaSearch and firecrawl tools for web research * chore(deps): update @trycompai/db to version 1.3.6 and bump lucide-react to 0.544.0 * chore: fix parsing md * chore: update imports from @trycompai/ui to @comp/ui * chore(api): remove obsolete Lambda execution route --------- Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
1 parent 3d7ee5f commit 56f019d

File tree

190 files changed

+12009
-402
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+12009
-402
lines changed

apps/api/src/comments/comments.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export class CommentsService {
115115
id: comment.author.user.id,
116116
name: comment.author.user.name,
117117
email: comment.author.user.email,
118+
image: comment.author.user.image,
118119
},
119120
attachments,
120121
createdAt: comment.createdAt,
@@ -209,6 +210,7 @@ export class CommentsService {
209210
id: member.user.id,
210211
name: member.user.name,
211212
email: member.user.email,
213+
image: member.user.image,
212214
},
213215
attachments: result.attachments,
214216
createdAt: result.comment.createdAt,
@@ -281,6 +283,7 @@ export class CommentsService {
281283
id: existingComment.author.user.id,
282284
name: existingComment.author.user.name,
283285
email: existingComment.author.user.email,
286+
image: existingComment.author.user.image,
284287
},
285288
attachments,
286289
createdAt: updatedComment.createdAt,

apps/api/src/comments/dto/comment-responses.dto.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ export class AuthorResponseDto {
8282
example: 'john.doe@company.com',
8383
})
8484
email: string;
85+
86+
@ApiProperty({
87+
description: 'User profile image URL',
88+
example: 'https://example.com/avatar.jpg',
89+
nullable: true,
90+
})
91+
image: string | null;
8592
}
8693

8794
export class CommentResponseDto {

apps/app/instrumentation-client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// import { initBotId } from 'botid/client/core';
2+
3+
// initBotId({
4+
// protect: [
5+
// {
6+
// path: '/api/chat',
7+
// method: 'POST',
8+
// },
9+
// {
10+
// path: '/api/tasks-automations/chat',
11+
// method: 'POST',
12+
// },
13+
// ],
14+
// });

apps/app/markdown.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module '*.md' {
2+
const content: string
3+
export default content
4+
}

apps/app/next.config.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,31 @@ import './src/env.mjs';
55
const isStandalone = process.env.NEXT_OUTPUT_STANDALONE === 'true';
66

77
const config: NextConfig = {
8+
turbopack: {
9+
rules: {
10+
'*.md': {
11+
loaders: ['raw-loader'],
12+
as: '*.js',
13+
},
14+
},
15+
},
16+
// Ensure .md files can be imported as strings during webpack builds
17+
webpack: (cfg) => {
18+
cfg.module = cfg.module || { rules: [] };
19+
cfg.module.rules = cfg.module.rules || [];
20+
cfg.module.rules.push({
21+
test: /\.md$/,
22+
type: 'asset/source',
23+
});
24+
return cfg;
25+
},
826
// Use S3 bucket for static assets with app-specific path
927
assetPrefix:
1028
process.env.NODE_ENV === 'production' && process.env.STATIC_ASSETS_URL
1129
? `${process.env.STATIC_ASSETS_URL}/app`
1230
: '',
1331
reactStrictMode: true,
14-
transpilePackages: ['@trycompai/db'],
32+
transpilePackages: ['@trycompai/db', '@prisma/client'],
1533
images: {
1634
remotePatterns: [
1735
{

apps/app/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@ai-sdk/provider": "^2.0.0",
99
"@ai-sdk/react": "^2.0.0",
1010
"@ai-sdk/rsc": "^1.0.0",
11+
"@aws-sdk/client-lambda": "^3.891.0",
1112
"@aws-sdk/client-s3": "^3.859.0",
1213
"@aws-sdk/client-sts": "^3.808.0",
1314
"@aws-sdk/s3-request-presigner": "^3.859.0",
@@ -25,6 +26,7 @@
2526
"@dub/embed-react": "^0.0.16",
2627
"@hookform/resolvers": "^5.1.1",
2728
"@mendable/firecrawl-js": "^1.24.0",
29+
"@monaco-editor/react": "^4.7.0",
2830
"@nangohq/frontend": "^0.53.2",
2931
"@next/third-parties": "^15.3.1",
3032
"@number-flow/react": "^0.5.9",
@@ -49,13 +51,16 @@
4951
"@trycompai/db": "^1.3.6",
5052
"@trycompai/email": "workspace:*",
5153
"@types/canvas-confetti": "^1.9.0",
54+
"@types/react-syntax-highlighter": "^15.5.13",
5255
"@types/three": "^0.180.0",
5356
"@uploadthing/react": "^7.3.0",
5457
"@upstash/ratelimit": "^2.0.5",
58+
"@vercel/sandbox": "^0.0.21",
5559
"@vercel/sdk": "^1.7.1",
5660
"ai": "^5.0.0",
5761
"axios": "^1.9.0",
5862
"better-auth": "^1.2.8",
63+
"botid": "^1.5.5",
5964
"canvas-confetti": "^1.9.3",
6065
"d3": "^7.9.0",
6166
"dub": "^0.66.1",
@@ -79,9 +84,12 @@
7984
"react-hotkeys-hook": "^5.1.0",
8085
"react-intersection-observer": "^9.16.0",
8186
"react-markdown": "10.1.0",
87+
"react-spinners": "^0.17.0",
88+
"react-syntax-highlighter": "^15.6.6",
8289
"react-textarea-autosize": "^8.5.9",
8390
"react-use-draggable-scroll": "^0.4.7",
8491
"react-wrap-balancer": "^1.1.1",
92+
"rehype-raw": "^7.0.0",
8593
"remark-gfm": "^4.0.1",
8694
"remark-parse": "^11.0.0",
8795
"resend": "^4.4.1",
@@ -91,6 +99,7 @@
9199
"ts-pattern": "^5.7.0",
92100
"use-debounce": "^10.0.4",
93101
"use-long-press": "^3.3.0",
102+
"use-stick-to-bottom": "^1.1.1",
94103
"xml2js": "^0.6.2",
95104
"zaraz-ts": "^1.2.0",
96105
"zod": "^3.25.76",
@@ -116,6 +125,7 @@
116125
"jsdom": "^26.1.0",
117126
"postcss": "^8.5.4",
118127
"prisma": "^6.13.0",
128+
"raw-loader": "^4.0.2",
119129
"tailwindcss": "^4.1.8",
120130
"typescript": "^5.8.3",
121131
"vite-tsconfig-paths": "^5.1.4",

apps/app/src/ai/constants.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { type GatewayModelId } from '@ai-sdk/gateway';
2+
3+
export enum Models {
4+
AmazonNovaPro = 'amazon/nova-pro',
5+
AnthropicClaude4Sonnet = 'anthropic/claude-4-sonnet',
6+
GoogleGeminiFlash = 'google/gemini-2.5-flash',
7+
MoonshotKimiK2 = 'moonshotai/kimi-k2',
8+
OpenAIGPT5 = 'gpt-5',
9+
XaiGrok3Fast = 'xai/grok-3-fast',
10+
}
11+
12+
export const DEFAULT_MODEL = Models.OpenAIGPT5;
13+
14+
export const SUPPORTED_MODELS: GatewayModelId[] = [
15+
Models.AmazonNovaPro,
16+
Models.AnthropicClaude4Sonnet,
17+
Models.GoogleGeminiFlash,
18+
Models.MoonshotKimiK2,
19+
Models.OpenAIGPT5,
20+
Models.XaiGrok3Fast,
21+
];
22+
23+
export const TEST_PROMPTS = [
24+
'I need an automation that calls github to check if trycompai/comp has dependabot enabled.',
25+
'Create an automation to list all open issues in a GitHub repository.',
26+
];

apps/app/src/ai/gateway.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { createGatewayProvider } from '@ai-sdk/gateway'
2+
import { Models } from './constants'
3+
import type { JSONValue } from 'ai'
4+
import type { OpenAIResponsesProviderOptions } from '@ai-sdk/openai'
5+
import type { LanguageModelV2 } from '@ai-sdk/provider'
6+
7+
export async function getAvailableModels() {
8+
const gateway = gatewayInstance()
9+
const response = await gateway.getAvailableModels()
10+
return response.models
11+
.map((model) => ({ id: model.id, name: model.name }))
12+
.concat([{ id: Models.OpenAIGPT5, name: 'GPT-5' }])
13+
}
14+
15+
export interface ModelOptions {
16+
model: LanguageModelV2
17+
providerOptions?: Record<string, Record<string, JSONValue>>
18+
headers?: Record<string, string>
19+
}
20+
21+
export function getModelOptions(
22+
modelId: string,
23+
options?: { reasoningEffort?: 'minimal' | 'low' | 'medium' }
24+
): ModelOptions {
25+
const gateway = gatewayInstance()
26+
if (modelId === Models.OpenAIGPT5) {
27+
return {
28+
model: gateway(modelId),
29+
providerOptions: {
30+
openai: {
31+
include: ['reasoning.encrypted_content'],
32+
reasoningEffort: options?.reasoningEffort ?? 'low',
33+
reasoningSummary: 'auto',
34+
serviceTier: 'priority',
35+
} satisfies OpenAIResponsesProviderOptions,
36+
},
37+
}
38+
}
39+
40+
if (modelId === Models.AnthropicClaude4Sonnet) {
41+
return {
42+
model: gateway(modelId),
43+
headers: { 'anthropic-beta': 'fine-grained-tool-streaming-2025-05-14' },
44+
providerOptions: {
45+
anthropic: {
46+
cacheControl: { type: 'ephemeral' },
47+
},
48+
},
49+
}
50+
}
51+
52+
return {
53+
model: gateway(modelId),
54+
}
55+
}
56+
57+
function gatewayInstance() {
58+
return createGatewayProvider({
59+
baseURL: process.env.AI_GATEWAY_BASE_URL,
60+
})
61+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import z from 'zod/v3';
2+
3+
export const errorSchema = z.object({
4+
message: z.string(),
5+
});
6+
7+
export const dataPartSchema = z.object({
8+
'create-sandbox': z.object({
9+
sandboxId: z.string().optional(),
10+
status: z.enum(['loading', 'done', 'error']),
11+
error: errorSchema.optional(),
12+
}),
13+
'generating-files': z.object({
14+
paths: z.array(z.string()),
15+
status: z.enum(['generating', 'uploading', 'uploaded', 'done', 'error']),
16+
error: errorSchema.optional(),
17+
}),
18+
'run-command': z.object({
19+
sandboxId: z.string(),
20+
commandId: z.string().optional(),
21+
command: z.string(),
22+
args: z.array(z.string()),
23+
status: z.enum(['executing', 'running', 'waiting', 'done', 'error']),
24+
exitCode: z.number().optional(),
25+
error: errorSchema.optional(),
26+
}),
27+
'get-sandbox-url': z.object({
28+
url: z.string().optional(),
29+
status: z.enum(['loading', 'done']),
30+
}),
31+
'report-errors': z.object({
32+
summary: z.string(),
33+
paths: z.array(z.string()).optional(),
34+
}),
35+
'store-to-s3': z.object({
36+
status: z.enum(['uploading', 'done', 'error']),
37+
bucket: z.string().optional(),
38+
key: z.string().optional(),
39+
region: z.string().optional(),
40+
error: errorSchema.optional(),
41+
}),
42+
});
43+
44+
export type DataPart = z.infer<typeof dataPartSchema>;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import z from 'zod/v3'
2+
3+
export const metadataSchema = z.object({
4+
model: z.string(),
5+
})
6+
7+
export type Metadata = z.infer<typeof metadataSchema>

0 commit comments

Comments
 (0)