Skip to content

Commit b7616b3

Browse files
committed
feat(mothership): operate across organization workspaces
1 parent d98b134 commit b7616b3

304 files changed

Lines changed: 8670 additions & 1860 deletions

File tree

Some content is hidden

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

apps/sim/app/api/copilot/chat/resources/route.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ export const POST = defineInternalJsonRoute({
2828
contract: addCopilotChatResourceContract,
2929
mapInput: ({ body }) => ({
3030
chatId: body.chatId,
31-
change: { kind: 'upsert' as const, resources: [{ ...body.resource, ...(body.clearViewId ? { clearViewId: true as const } : {}) }] },
31+
change: {
32+
kind: 'upsert' as const,
33+
resources: [
34+
{ ...body.resource, ...(body.clearViewId ? { clearViewId: true as const } : {}) },
35+
],
36+
},
3237
}),
3338
})
3439

@@ -48,7 +53,7 @@ export const DELETE = defineInternalJsonRoute({
4853
chatId: body.chatId,
4954
change: {
5055
kind: 'remove' as const,
51-
resources: [{ type: body.resourceType, id: body.resourceId }],
56+
resources: [{ type: body.resourceType, id: body.resourceId, workspaceId: body.workspaceId }],
5257
},
5358
}),
5459
})

apps/sim/app/api/copilot/tools/execute/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export const POST = withRouteHandler((request: NextRequest) =>
118118
userPermission,
119119
requestMode,
120120
assistantSearch,
121+
targetWorkspaceId,
121122
} = validation.data
122123
rootSpan.setAttributes({
123124
[TraceAttr.ToolName]: toolName,
@@ -190,6 +191,7 @@ export const POST = withRouteHandler((request: NextRequest) =>
190191
copilotInteractionMode: 'interactive',
191192
requestMode,
192193
assistantSearch,
194+
targetWorkspaceId,
193195
resolvedSecretTraceRegistry: toolRegistry,
194196
})
195197
const projection = inspectToolResultForCopilot(result, toolRegistry, toolName)

apps/sim/app/api/files/serve/[...path]/route.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const {
7070
})
7171

7272
vi.mock('@/lib/uploads/contexts/organization-assistant/application', () => ({
73-
readOrganizationAssistantImage: mockReadOrganizationAssistantImage,
73+
readOrganizationChatAttachment: mockReadOrganizationAssistantImage,
7474
}))
7575

7676
vi.mock('fs/promises', () => ({

apps/sim/app/api/files/serve/[...path]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { resolveServableDocBytes } from '@/lib/mothership/tools/server/files/doc
1717
import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error'
1818
import { CopilotFiles, isUsingCloudStorage } from '@/lib/uploads'
1919
import type { StorageContext } from '@/lib/uploads/config'
20-
import { readOrganizationAssistantImage } from '@/lib/uploads/contexts/organization-assistant/application'
20+
import { readOrganizationChatAttachment } from '@/lib/uploads/contexts/organization-assistant/application'
2121
import { parseWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
2222
import { downloadFile } from '@/lib/uploads/core/storage-service'
2323
import { resolveServableImageBytes } from '@/lib/uploads/server/image-derivative'
@@ -225,7 +225,7 @@ export const GET = withRouteHandler(
225225

226226
if (cloudKey.startsWith('assistant/')) {
227227
const principal = await internalSessionAuth.authenticate()
228-
const image = await readOrganizationAssistantImage({
228+
const image = await readOrganizationChatAttachment({
229229
principal,
230230
key: cloudKey,
231231
signal: request.signal,

apps/sim/app/api/files/uploads/purposes.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import {
1414
} from '@/lib/uploads/upload-session/service'
1515
import { isImageFileType } from '@/lib/uploads/utils/file-utils'
1616
import { validateAttachmentFileType } from '@/lib/uploads/utils/validation'
17-
import { authorizeWorkspaceFileAccess } from '@/lib/workspace-files/application/authorization'
17+
import {
18+
authorizeWorkspaceFileAccess,
19+
WORKSPACE_FILES_DELEGATION_AUDIENCE,
20+
} from '@/lib/workspace-files/application/authorization'
1821
import { fileOperations } from '@/lib/workspace-files/application/operations'
1922
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
2023

@@ -266,6 +269,15 @@ async function principalUserId(principal: Principal, workspaceId?: string): Prom
266269
return context.billedAccountUserId
267270
}
268271
case 'delegated':
272+
if (
273+
principal.serviceId === 'copilot' &&
274+
principal.workspaceId === workspaceId &&
275+
principal.audience === WORKSPACE_FILES_DELEGATION_AUDIENCE &&
276+
principal.subjectUserId &&
277+
principal.resourceScope?.chatId &&
278+
principal.expiresAt > new Date()
279+
)
280+
return principal.subjectUserId
269281
throw new UploadSessionError('forbidden', 'Delegated principals cannot create uploads')
270282
case 'system':
271283
throw new UploadSessionError('forbidden', 'System principals cannot create uploads')

apps/sim/app/api/mothership/chats/[chatId]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export const GET = withRouteHandler(
124124
chat: {
125125
id: chat.id,
126126
title: chat.title,
127+
mode: chat.mode,
127128
messages: effectiveMessages,
128129
activeStreamId: liveStreamId,
129130
resources: Array.isArray(chat.resources) ? chat.resources : [],

apps/sim/app/api/mothership/chats/route.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
listMothershipChatsContract,
88
} from '@/lib/api/contracts/mothership-chats'
99
import { parseRequest } from '@/lib/api/server'
10+
import { asOrchestrationError } from '@/lib/core/orchestration/types'
1011
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
1112
import { listMothershipChats } from '@/lib/mothership/chat/list-mothership-chats'
1213
import {
@@ -21,7 +22,6 @@ import {
2122
createInternalServerErrorResponse,
2223
createUnauthorizedResponse,
2324
} from '@/lib/mothership/request/http'
24-
import { asOrchestrationError } from '@/lib/core/orchestration/types'
2525
import { captureServerEvent } from '@/lib/posthog/server'
2626
import {
2727
assertActiveWorkspaceAccess,
@@ -85,11 +85,14 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
8585

8686
const validation = await parseRequest(createMothershipChatContract, request, {})
8787
if (!validation.success) return validation.response
88-
const { workspaceId, organizationId } = validation.data.body
88+
const { workspaceId, organizationId, mode } = validation.data.body
8989

9090
if (organizationId) {
9191
if (!principal) return createUnauthorizedResponse()
92-
const chat = await createOrganizationChat.execute({ principal, input: { organizationId } })
92+
const chat = await createOrganizationChat.execute({
93+
principal,
94+
input: { organizationId, mode },
95+
})
9396
return NextResponse.json({ success: true, id: chat.id })
9497
}
9598

apps/sim/app/api/mothership/tasks/workflow-status/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { readWatchedWorkflowStatus } from '@/lib/mothership/tasks/application/re
1111

1212
export const POST = defineInternalJsonRoute({
1313
contract: workflowWatchStatusContract,
14-
auth: internalCopilotAuth(TASK_DELEGATION_AUDIENCE),
14+
auth: internalCopilotAuth(TASK_DELEGATION_AUDIENCE, { organization: true }),
1515
operation: taskOperations.readWorkflowStatus,
1616
rateLimit: internalRateLimits.none({
1717
reason: 'Authenticated worker reconciles its bounded task inbox.',

apps/sim/app/api/mothership/wake/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { runWakeTurn } from '@/lib/mothership/tasks/wake'
1313

1414
export const POST = defineInternalJsonRoute({
1515
contract: wakeMothershipTaskContract,
16-
auth: internalCopilotAuth(TASK_DELEGATION_AUDIENCE),
16+
auth: internalCopilotAuth(TASK_DELEGATION_AUDIENCE, { organization: true }),
1717
operation: taskOperations.wake,
1818
rateLimit: internalRateLimits.none({
1919
reason:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { readWorkspaceContextContract } from '@/lib/api/contracts/mothership-workspaces'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
} from '@/lib/api/server/routes'
7+
import { internalCopilotAuth } from '@/lib/mothership/auth/internal'
8+
import {
9+
readWorkspaceContext,
10+
readWorkspaceContextOperation,
11+
} from '@/lib/mothership/chat/application/workspace-context'
12+
import { WORKSPACE_TARGET_AUDIENCE } from '@/lib/mothership/chat/application/workspace-target'
13+
14+
export const POST = defineInternalJsonRoute({
15+
contract: readWorkspaceContextContract,
16+
auth: internalCopilotAuth(WORKSPACE_TARGET_AUDIENCE, { organization: true }),
17+
operation: readWorkspaceContextOperation,
18+
rateLimit: internalRateLimits.none({
19+
reason:
20+
'Private worker preflight rechecks the current workspace grant before local memory access.',
21+
}),
22+
errorPolicy: internalOrchestrationErrorPolicy,
23+
mapInput: ({ body }) => body,
24+
useCase: readWorkspaceContext,
25+
})

0 commit comments

Comments
 (0)