diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.tsx index 94be2fc3628..db58cdcf461 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor/prompt-editor.tsx @@ -31,8 +31,8 @@ export interface PromptEditorProps extends PromptEditorKeyPolicy { * Renders the editor as a non-editable display surface: the textarea becomes * `readOnly` (so the chip overlay still paints `@`-mention / `/`-skill chips * and the text stays selectable/copyable) and the caret-anchored resource and - * skill menus are not mounted. Use for read-only records — e.g. a finished - * scheduled task — where the prompt should render with chips but not be edited. + * skill menus are not mounted. Use for records where the prompt should render + * with chips but not be edited. */ readOnly?: boolean /** diff --git a/apps/sim/lib/api/contracts/mothership-chats.ts b/apps/sim/lib/api/contracts/mothership-chats.ts index 275f3c80bf5..6350046512d 100644 --- a/apps/sim/lib/api/contracts/mothership-chats.ts +++ b/apps/sim/lib/api/contracts/mothership-chats.ts @@ -116,8 +116,8 @@ export const mothershipExecuteBodySchema = z.object({ fileAttachments: z.array(mothershipExecuteFileAttachmentSchema).optional(), /** * `@`-mentioned resources / `/`-invoked skills to resolve into the agent run, - * mirroring the interactive chat path. Used by scheduled tasks, whose - * captured contexts must reach the run without a live client. + * mirroring the interactive chat path. Headless executions use this to pass + * captured contexts into the run without a live client. */ contexts: z.array(scheduleContextSchema).optional(), mcpTools: z.array(mothershipExecuteMcpToolSchema).optional(), diff --git a/apps/sim/lib/copilot/chat/workspace-context.test.ts b/apps/sim/lib/copilot/chat/workspace-context.test.ts index c6e0da22b0e..f383c841439 100644 --- a/apps/sim/lib/copilot/chat/workspace-context.test.ts +++ b/apps/sim/lib/copilot/chat/workspace-context.test.ts @@ -166,26 +166,6 @@ describe('buildWorkspaceMd - determinism (prompt-cache stability)', () => { { id: 'sk-2', name: 'Writer', description: 'writes' }, { id: 'sk-1', name: 'Editor', description: 'edits' }, ], - jobs: [ - { - id: 'j-2', - title: 'Nightly', - prompt: 'run nightly', - cronExpression: '0 0 * * *', - status: 'active', - lifecycle: 'persistent', - sourceTaskName: null, - }, - { - id: 'j-1', - title: 'Hourly', - prompt: 'run hourly', - cronExpression: '0 * * * *', - status: 'active', - lifecycle: 'persistent', - sourceTaskName: null, - }, - ], }) ) const b = buildWorkspaceMd( @@ -223,26 +203,6 @@ describe('buildWorkspaceMd - determinism (prompt-cache stability)', () => { { id: 'sk-1', name: 'Editor', description: 'edits' }, { id: 'sk-2', name: 'Writer', description: 'writes' }, ], - jobs: [ - { - id: 'j-1', - title: 'Hourly', - prompt: 'run hourly', - cronExpression: '0 * * * *', - status: 'active', - lifecycle: 'persistent', - sourceTaskName: null, - }, - { - id: 'j-2', - title: 'Nightly', - prompt: 'run nightly', - cronExpression: '0 0 * * *', - status: 'active', - lifecycle: 'persistent', - sourceTaskName: null, - }, - ], }) ) expect(a).toBe(b) diff --git a/apps/sim/lib/copilot/request/tools/permission.ts b/apps/sim/lib/copilot/request/tools/permission.ts index a9829c8c155..08e751dc204 100644 --- a/apps/sim/lib/copilot/request/tools/permission.ts +++ b/apps/sim/lib/copilot/request/tools/permission.ts @@ -54,9 +54,8 @@ export const TOOL_AWAITING_APPROVAL_STATUS = MothershipStreamV1ToolStatus.awaiti /** * Whether this call must be held for an explicit user decision. * - * Headless and non-interactive runs (scheduled tasks, one-shot execute) are - * never gated: nobody is there to answer, and blocking them would hang the run - * until the orchestration timeout. + * Headless one-shot executions are never gated: nobody is there to answer, and + * blocking them would hang the run until the orchestration timeout. */ export function toolCallNeedsApproval( toolName: string, diff --git a/apps/sim/lib/copilot/tools/handlers/function-execute.ts b/apps/sim/lib/copilot/tools/handlers/function-execute.ts index 45634e9792d..d0c8d2280cb 100644 --- a/apps/sim/lib/copilot/tools/handlers/function-execute.ts +++ b/apps/sim/lib/copilot/tools/handlers/function-execute.ts @@ -198,9 +198,7 @@ function unmountableNamespaceReason(filePath: string): string | null { if (path.startsWith('tables/')) { return 'tables are not mounted as files. Pass the table in inputs.tables instead and it is mounted as CSV.' } - const namespace = /^(workflows|knowledgebases|components|environment|agent|jobs|tasks)\//.exec( - path - )?.[1] + const namespace = /^(workflows|knowledgebases|components|environment|agent)\//.exec(path)?.[1] if (namespace) { return `${namespace}/ paths are VFS metadata views, not stored file bytes, so the sandbox cannot mount them. This path is correct — read or grep it and inline the values you need in code.` } diff --git a/apps/sim/lib/copilot/tools/tool-display.test.ts b/apps/sim/lib/copilot/tools/tool-display.test.ts index a519f326339..9fd352aff15 100644 --- a/apps/sim/lib/copilot/tools/tool-display.test.ts +++ b/apps/sim/lib/copilot/tools/tool-display.test.ts @@ -116,7 +116,6 @@ describe('getToolDisplayTitle natural-language coverage', () => { 'Managing table', 'Preparing file', 'Processing media', - 'Scheduled task action', 'Skill action', ]) const unresolvedVariants: string[] = [] diff --git a/apps/sim/lib/copilot/vfs/serializers.ts b/apps/sim/lib/copilot/vfs/serializers.ts index abbbded736c..9e06975de38 100644 --- a/apps/sim/lib/copilot/vfs/serializers.ts +++ b/apps/sim/lib/copilot/vfs/serializers.ts @@ -1115,57 +1115,3 @@ export function serializeTriggerOverview( lines.push('') return lines.join('\n') } - -export function serializeTaskSession(task: { - id: string - title: string - messageCount: number - createdAt: Date - updatedAt: Date -}): string { - return [ - `# ${task.title}`, - '', - `- **Chat ID:** ${task.id}`, - `- **Created:** ${task.createdAt.toISOString()}`, - `- **Updated:** ${task.updatedAt.toISOString()}`, - `- **Messages:** ${task.messageCount}`, - '', - ].join('\n') -} - -export function serializeTaskChat(rawMessages: unknown[]): string { - const filtered: { role: string; content: string }[] = [] - - for (const msg of rawMessages) { - if (!msg || typeof msg !== 'object') continue - const m = msg as Record - const role = m.role as string | undefined - if (role !== 'user' && role !== 'assistant') continue - - let content = '' - if (role === 'assistant' && Array.isArray(m.contentBlocks)) { - const textParts: string[] = [] - for (const block of m.contentBlocks) { - if ( - block && - typeof block === 'object' && - (block as any).type === 'text' && - (block as any).content - ) { - textParts.push((block as any).content) - } - } - content = textParts.join('') - } - - if (!content && typeof m.content === 'string') { - content = m.content - } - - if (!content) continue - filtered.push({ role, content }) - } - - return JSON.stringify(filtered, null, 2) -} diff --git a/apps/sim/lib/copilot/vfs/workspace-vfs.ts b/apps/sim/lib/copilot/vfs/workspace-vfs.ts index bdc264e4eaa..6eaf1f3c284 100644 --- a/apps/sim/lib/copilot/vfs/workspace-vfs.ts +++ b/apps/sim/lib/copilot/vfs/workspace-vfs.ts @@ -2,7 +2,6 @@ import { trace } from '@opentelemetry/api' import { db } from '@sim/db' import { chat as chatTable, - copilotChats, customTools as customToolsTable, document, folder as folderTable, @@ -17,7 +16,7 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' -import { and, desc, eq, inArray, isNotNull, isNull, or, sql } from 'drizzle-orm' +import { and, desc, eq, inArray, isNotNull, isNull, or } from 'drizzle-orm' import { listApiKeys } from '@/lib/api-key/service' import { buildWorkspaceContextMd, @@ -79,8 +78,6 @@ import { serializeRecentExecutions, serializeSkill, serializeTableMeta, - serializeTaskChat, - serializeTaskSession, serializeTriggerOverview, serializeTriggerSchema, serializeVersions, @@ -521,8 +518,6 @@ function getStaticComponentFiles(): Map { * files/{name} (workspace file leaf; dynamic content on read) * files/{path}/{name}/style (dynamic — style extraction for .docx/.pptx/.pdf) * files/{path}/{name}/compiled-check (dynamic — compile generated source / validate diagrams, returns {ok,error?}) - * tasks/{title}/session.md - * tasks/{title}/chat.json * custom-tools/{name}.json * environment/credentials.json * environment/api-keys.json @@ -777,10 +772,6 @@ export class WorkspaceVFS { timed('workspace_row', getWorkspaceWithOwner(workspaceId)), timed('members', getUsersWithPermissions(workspaceId)), permissionConfigPromise, - // Writes tasks/ files only — WORKSPACE.md has no Tasks section - // (recent chats reorder every turn and would bust the cached - // prompt prefix), so nothing is destructured from this one. - timed('tasks', this.materializeTasks(workspaceId, userId)), ]) const workspaceMdData = { @@ -2116,89 +2107,6 @@ export class WorkspaceVFS { } } - /** - * Materialize mothership task chats as browsable conversation files under - * `tasks/{title}/`. Nothing is returned: the inventory deliberately has no - * Tasks section, so these files are reached through glob/read only. - */ - private async materializeTasks(workspaceId: string, userId: string): Promise { - try { - const taskRows = await db - .select({ - id: copilotChats.id, - title: copilotChats.title, - messageCount: sql`COALESCE(( - SELECT COUNT(*) FROM copilot_messages cm - WHERE cm.chat_id = ${copilotChats.id} AND cm.deleted_at IS NULL - ), 0)`, - messages: sql`COALESCE(( - SELECT jsonb_agg( - jsonb_build_object( - 'role', cm.content->>'role', - 'content', cm.content->'content', - 'contentBlocks', COALESCE(( - SELECT jsonb_agg(jsonb_build_object('type', 'text', 'content', b.value->'content') ORDER BY b.ord) - FROM jsonb_array_elements( - CASE WHEN jsonb_typeof(cm.content->'contentBlocks') = 'array' - THEN cm.content->'contentBlocks' - ELSE '[]'::jsonb - END - ) WITH ORDINALITY AS b(value, ord) - WHERE b.value->>'type' = 'text' - ), '[]'::jsonb) - ) - ORDER BY cm.seq ASC NULLS LAST, cm.created_at ASC, cm.id ASC - ) - FROM copilot_messages cm - WHERE cm.chat_id = ${copilotChats.id} - AND cm.deleted_at IS NULL - AND cm.content->>'role' IN ('user', 'assistant') - ), '[]'::jsonb)`, - createdAt: copilotChats.createdAt, - updatedAt: copilotChats.updatedAt, - }) - .from(copilotChats) - .where( - and( - eq(copilotChats.workspaceId, workspaceId), - eq(copilotChats.userId, userId), - eq(copilotChats.type, 'mothership'), - isNull(copilotChats.deletedAt) - ) - ) - .orderBy(desc(copilotChats.updatedAt)) - .limit(5) - - for (const task of taskRows) { - const title = task.title || 'Untitled task' - const safeName = sanitizeName(title) - const prefix = `tasks/${safeName}/` - const messages = Array.isArray(task.messages) ? task.messages : [] - const messageCount = Number(task.messageCount) || 0 - - this.files.set( - `${prefix}session.md`, - serializeTaskSession({ - id: task.id, - title, - messageCount, - createdAt: task.createdAt, - updatedAt: task.updatedAt, - }) - ) - - if (messages.length > 0) { - this.files.set(`${prefix}chat.json`, serializeTaskChat(messages)) - } - } - } catch (err) { - logger.warn('Failed to materialize tasks', { - workspaceId, - error: toError(err).message, - }) - } - } - private async materializeRecentlyDeleted(workspaceId: string, userId: string): Promise { try { const [