- Change workspaceId from v.string() to v.id('workspaces') in 7 tables and cascade the type fix through all validators, helpers, and call sites - Make anonymousSessions.userId and workspaceToolCache.dtsStorageIds required with cleanup/backfill migrations for both dev and prod - Delete pushSubscriptions table - Remove legacy OpenAPI compat (schemaTypes/operationTypes), legacy GraphQL run spec, and credential provider fallback code - Inline @executor/contracts types into lib/types.ts and web types.ts, delete the contracts package entirely - Turn executor/convex/ into @executor/convex workspace package so the web app imports from @executor/convex/_generated/* instead of fragile relative paths
Unified · Split
| @@ -9,7 +9,6 @@ · 8 unmodified lines | ||
| 9 | 9 | "@convex-dev/migrations": "^0.3.1", |
| 10 | 10 | "@convex-dev/stripe": "^0.1.3", |
| 11 | 11 | "@convex-dev/workos-authkit": "^0.1.6", |
| 12 | − "@executor/contracts": "workspace:*", | |
| 13 | 12 | "@workos-inc/node": "^8.2.0", |
| 14 | 13 | "convex": "^1.31.7", |
| 15 | 14 | "convex-helpers": "^0.1.111", |
| @@ -91,7 +90,7 @@ · 75 unmodified lines | ||
| 91 | 90 | "dependencies": { |
| 92 | 91 | "@convex-dev/workos": "^0.0.1", |
| 93 | 92 | "@elysiajs/eden": "^1.4.6", |
| 94 | − "@executor/contracts": "workspace:*", | |
| 93 | + "@executor/convex": "workspace:*", | |
| 95 | 94 | "@monaco-editor/react": "^4.7.0", |
| 96 | 95 | "@streamdown/code": "^1.0.1", |
| 97 | 96 | "@tanstack/react-query": "^5.80.7", |
| @@ -127,8 +126,8 @@ · 29 unmodified lines | ||
| 127 | 126 | "typescript": "^5", |
| 128 | 127 | }, |
| 129 | 128 | }, |
| 130 | − "executor/packages/contracts": { | |
| 131 | − "name": "@executor/contracts", | |
| 129 | + "executor/convex": { | |
| 130 | + "name": "@executor/convex", | |
| 132 | 131 | "version": "0.0.1", |
| 133 | 132 | }, |
| 134 | 133 | }, |
| @@ -399,7 +398,7 @@ · 264 unmodified lines | ||
| 399 | 398 | |
| 400 | 399 | "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], |
| 401 | 400 | |
| 402 | − "@executor/contracts": ["@executor/contracts@workspace:executor/packages/contracts"], | |
| 401 | + "@executor/convex": ["@executor/convex@workspace:executor/convex"], | |
| 403 | 402 | |
| 404 | 403 | "@executor/web": ["@executor/web@workspace:executor/apps/web"], |
| 405 | 404 | |
| @@ -13,7 +13,7 @@ const appShellRewriteExclusions = [ · 12 unmodified lines | ||
| 13 | 13 | ].join("|"); |
| 14 | 14 | |
| 15 | 15 | const nextConfig: NextConfig = { |
| 16 | − transpilePackages: ["@executor/contracts"], | |
| 16 | + transpilePackages: ["@executor/convex"], | |
| 17 | 17 | env: { |
| 18 | 18 | // Map canonical env vars to NEXT_PUBLIC_ so they're available client-side. |
| 19 | 19 | // This lets us keep a single root .env without NEXT_PUBLIC_ prefixes. |
| @@ -11,8 +11,8 @@ · 10 unmodified lines | ||
| 11 | 11 | }, |
| 12 | 12 | "dependencies": { |
| 13 | 13 | "@convex-dev/workos": "^0.0.1", |
| 14 | + "@executor/convex": "workspace:*", | |
| 14 | 15 | "@elysiajs/eden": "^1.4.6", |
| 15 | − "@executor/contracts": "workspace:*", | |
| 16 | 16 | "@monaco-editor/react": "^4.7.0", |
| 17 | 17 | "@streamdown/code": "^1.0.1", |
| 18 | 18 | "@tanstack/react-query": "^5.80.7", |
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | import type { NextRequest } from "next/server"; |
| 2 | 2 | import { redirect } from "next/navigation"; |
| 3 | 3 | import { ConvexHttpClient } from "convex/browser"; |
| 4 | −import { api } from "../../../../../convex/_generated/api"; | |
| 5 | −import type { Id } from "../../../../../convex/_generated/dataModel"; | |
| 4 | +import { api } from "@executor/convex/_generated/api"; | |
| 5 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 6 | 6 | |
| 7 | 7 | function getExternalOrigin(request: NextRequest) { |
| 8 | 8 | const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host"); |
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | import type { NextRequest } from "next/server"; |
| 2 | 2 | import { redirect } from "next/navigation"; |
| 3 | 3 | import { ConvexHttpClient } from "convex/browser"; |
| 4 | −import { api } from "../../../../../convex/_generated/api"; | |
| 5 | −import type { Id } from "../../../../../convex/_generated/dataModel"; | |
| 4 | +import { api } from "@executor/convex/_generated/api"; | |
| 5 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 6 | 6 | |
| 7 | 7 | function getExternalOrigin(request: NextRequest) { |
| 8 | 8 | const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host"); |
| @@ -131,17 +131,6 @@ type ToolOutput<Op> = · 130 unmodified lines | ||
| 131 | 131 | function generateToolsDts(tools: ToolDescriptor[], dtsSources: Set<string>): string { |
| 132 | 132 | const root = buildTree(tools); |
| 133 | 133 | |
| 134 | − // Legacy compat: collect schemaTypes from tools that use the old format | |
| 135 | − const schemaTypeAliases = new Map<string, string>(); | |
| 136 | − for (const tool of tools) { | |
| 137 | − if (!tool.schemaTypes) continue; | |
| 138 | − for (const [name, type] of Object.entries(tool.schemaTypes)) { | |
| 139 | − if (!schemaTypeAliases.has(name)) { | |
| 140 | − schemaTypeAliases.set(name, type); | |
| 141 | − } | |
| 142 | − } | |
| 143 | − } | |
| 144 | − | |
| 145 | 134 | const interfaces: string[] = []; |
| 146 | 135 | |
| 147 | 136 | // Emit all namespace interfaces recursively |
| @@ -170,11 +159,6 @@ function generateToolsDts(tools: ToolDescriptor[], dtsSources: Set<string>): str · 22 unmodified lines | ||
| 170 | 159 | */ |
| 171 | 160 | `; |
| 172 | 161 | |
| 173 | − // Legacy schema type aliases | |
| 174 | − if (schemaTypeAliases.size > 0) { | |
| 175 | − dts += Array.from(schemaTypeAliases, ([name, type]) => `type ${name} = ${type};`).join("\n") + "\n\n"; | |
| 176 | − } | |
| 177 | − | |
| 178 | 162 | // Note: .d.ts blocks from OpenAPI sources are fetched separately and registered |
| 179 | 163 | // as a distinct Monaco extra lib (see dtsUrls effect). This keeps generateToolsDts |
| 180 | 164 | // fast and avoids bundling multi-MB .d.ts content into the tool declarations string. |
| @@ -9,7 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; · 8 unmodified lines | ||
| 9 | 9 | import { Input } from "@/components/ui/input"; |
| 10 | 10 | import { useSession } from "@/lib/session-context"; |
| 11 | 11 | import { convexApi } from "@/lib/convex-api"; |
| 12 | −import type { Id } from "../../../../convex/_generated/dataModel"; | |
| 12 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 13 | 13 | |
| 14 | 14 | type Role = "owner" | "admin" | "member" | "billing_admin"; |
| 15 | 15 | type MemberStatus = "active" | "pending" | "removed"; |
| @@ -64,7 +64,7 @@ export function MembersView({ showHeader = true }: MembersViewProps) { · 48 unmodified lines | ||
| 64 | 64 | |
| 65 | 65 | const memberItems: OrganizationMemberListItem[] = members?.items ?? []; |
| 66 | 66 | const actorMembership = memberItems.find((member: { accountId?: string; role?: string }) => |
| 67 | − context?.accountId ? member.accountId === context.accountId : false, | |
| 67 | + context ? member.accountId === context.accountId : false, | |
| 68 | 68 | ); |
| 69 | 69 | const actorRole = actorMembership?.role ?? null; |
| 70 | 70 | const canManageMembers = actorRole === "owner" || actorRole === "admin"; |
| @@ -34,6 +34,7 @@ import type { · 33 unmodified lines | ||
| 34 | 34 | TaskEventRecord, |
| 35 | 35 | TaskRecord, |
| 36 | 36 | } from "@/lib/types"; |
| 37 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 37 | 38 | import { toast } from "sonner"; |
| 38 | 39 | import { cn } from "@/lib/utils"; |
| 39 | 40 | |
| @@ -210,7 +211,7 @@ function TaskDetail({ · 170 unmodified lines | ||
| 210 | 211 | onClose, |
| 211 | 212 | }: { |
| 212 | 213 | task: TaskRecord; |
| 213 | − workspaceId: string; | |
| 214 | + workspaceId: Id<"workspaces">; | |
| 214 | 215 | sessionId?: string; |
| 215 | 216 | onClose: () => void; |
| 216 | 217 | }) { |
| @@ -4,9 +4,10 @@ import { useQuery as useTanstackQuery } from "@tanstack/react-query"; · 3 unmodified lines | ||
| 4 | 4 | import { useAction, useQuery as useConvexQuery } from "convex/react"; |
| 5 | 5 | import { convexApi } from "@/lib/convex-api"; |
| 6 | 6 | import type { OpenApiSourceQuality, ToolDescriptor } from "@/lib/types"; |
| 7 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 7 | 8 | |
| 8 | 9 | interface WorkspaceContext { |
| 9 | − workspaceId: string; | |
| 10 | + workspaceId: Id<"workspaces">; | |
| 10 | 11 | actorId?: string; |
| 11 | 12 | clientId?: string; |
| 12 | 13 | sessionId?: string; |
| @@ -1,3 +1,3 @@ | ||
| 1 | −import { api } from "../../../../convex/_generated/api"; | |
| 1 | +import { api } from "@executor/convex/_generated/api"; | |
| 2 | 2 | |
| 3 | 3 | export const convexApi = api; |
| @@ -14,7 +14,7 @@ import { anonymousDemoEnabled, workosEnabled } from "@/lib/auth-capabilities"; · 13 unmodified lines | ||
| 14 | 14 | import { useWorkosAuthLoading } from "@/lib/convex-provider"; |
| 15 | 15 | import { convexApi } from "@/lib/convex-api"; |
| 16 | 16 | import type { AnonymousContext } from "./types"; |
| 17 | −import type { Id } from "../../../../convex/_generated/dataModel"; | |
| 17 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 18 | 18 | |
| 19 | 19 | interface SessionState { |
| 20 | 20 | context: AnonymousContext | null; |
| @@ -1,27 +1,129 @@ | ||
| 1 | −// Re-export shared types from contracts | |
| 2 | −export type { | |
| 3 | − TaskStatus, | |
| 4 | − ApprovalStatus, | |
| 5 | − PolicyDecision, | |
| 6 | − CredentialScope, | |
| 7 | − CredentialProvider, | |
| 8 | − ToolApprovalMode, | |
| 9 | − ToolSourceType, | |
| 10 | − TaskRecord, | |
| 11 | − ApprovalRecord, | |
| 12 | − PendingApprovalRecord, | |
| 13 | − TaskEventRecord, | |
| 14 | − AccessPolicyRecord, | |
| 15 | − CredentialRecord, | |
| 16 | − ToolSourceRecord, | |
| 17 | − ToolDescriptor, | |
| 18 | − OpenApiSourceQuality, | |
| 19 | − AnonymousContext, | |
| 20 | −} from "@executor/contracts"; | |
| 1 | +import type { Id } from "@executor/convex/_generated/dataModel"; | |
| 21 | 2 | |
| 22 | −// ── Web-only types ──────────────────────────────────────────────────────────── | |
| 3 | +// ── Shared types (inlined from @executor/contracts) ────────────────────────── | |
| 4 | + | |
| 5 | +export type TaskStatus = "queued" | "running" | "completed" | "failed" | "timed_out" | "denied"; | |
| 6 | +export type ApprovalStatus = "pending" | "approved" | "denied"; | |
| 7 | +export type PolicyDecision = "allow" | "require_approval" | "deny"; | |
| 8 | +export type CredentialScope = "workspace" | "actor"; | |
| 9 | +export type CredentialProvider = "managed" | "workos-vault"; | |
| 10 | +export type ToolApprovalMode = "auto" | "required"; | |
| 11 | +export type ToolSourceType = "mcp" | "openapi" | "graphql"; | |
| 12 | + | |
| 13 | +export interface TaskRecord { | |
| 14 | + id: string; | |
| 15 | + code: string; | |
| 16 | + runtimeId: string; | |
| 17 | + status: TaskStatus; | |
| 18 | + timeoutMs: number; | |
| 19 | + metadata: Record<string, unknown>; | |
| 20 | + workspaceId: string; | |
| 21 | + actorId?: string; | |
| 22 | + clientId?: string; | |
| 23 | + createdAt: number; | |
| 24 | + updatedAt: number; | |
| 25 | + startedAt?: number; | |
| 26 | + completedAt?: number; | |
| 27 | + error?: string; | |
| 28 | + stdout?: string; | |
| 29 | + stderr?: string; | |
| 30 | + exitCode?: number; | |
| 31 | +} | |
| 32 | + | |
| 33 | +export interface ApprovalRecord { | |
| 34 | + id: string; | |
| 35 | + taskId: string; | |
| 36 | + toolPath: string; | |
| 37 | + input: unknown; | |
| 38 | + status: ApprovalStatus; | |
| 39 | + reason?: string; | |
| 40 | + reviewerId?: string; | |
| 41 | + createdAt: number; | |
| 42 | + resolvedAt?: number; | |
| 43 | +} | |
| 44 | + | |
| 45 | +export interface PendingApprovalRecord extends ApprovalRecord { | |
| 46 | + task: Pick<TaskRecord, "id" | "status" | "runtimeId" | "timeoutMs" | "createdAt">; | |
| 47 | +} | |
| 48 | + | |
| 49 | +export interface TaskEventRecord { | |
| 50 | + id: number; | |
| 51 | + taskId: string; | |
| 52 | + eventName: string; | |
| 53 | + type: string; | |
| 54 | + payload: unknown; | |
| 55 | + createdAt: number; | |
| 56 | +} | |
| 57 | + | |
| 58 | +export interface AccessPolicyRecord { | |
| 59 | + id: string; | |
| 60 | + workspaceId: string; | |
| 61 | + actorId?: string; | |
| 62 | + clientId?: string; | |
| 63 | + toolPathPattern: string; | |
| 64 | + decision: PolicyDecision; | |
| 65 | + priority: number; | |
| 66 | + createdAt: number; | |
| 67 | + updatedAt: number; | |
| 68 | +} | |
| 69 | + | |
| 70 | +export interface CredentialRecord { | |
| 71 | + id: string; | |
| 72 | + workspaceId: string; | |
| 73 | + sourceKey: string; | |
| 74 | + scope: CredentialScope; | |
| 75 | + actorId?: string; | |
| 76 | + provider: CredentialProvider; | |
| 77 | + secretJson: Record<string, unknown>; | |
| 78 | + createdAt: number; | |
| 79 | + updatedAt: number; | |
| 80 | +} | |
| 81 | + | |
| 82 | +export interface ToolSourceRecord { | |
| 83 | + id: string; | |
| 84 | + workspaceId: string; | |
| 85 | + name: string; | |
| 86 | + type: ToolSourceType; | |
| 87 | + config: Record<string, unknown>; | |
| 88 | + enabled: boolean; | |
| 89 | + createdAt: number; | |
| 90 | + updatedAt: number; | |
| 91 | +} | |
| 92 | + | |
| 93 | +export interface ToolDescriptor { | |
| 94 | + path: string; | |
| 95 | + description: string; | |
| 96 | + approval: ToolApprovalMode; | |
| 97 | + source?: string; | |
| 98 | + argsType?: string; | |
| 99 | + returnsType?: string; | |
| 100 | + operationId?: string; | |
| 101 | +} | |
| 102 | + | |
| 103 | +export interface OpenApiSourceQuality { | |
| 104 | + sourceKey: string; | |
| 105 | + toolCount: number; | |
| 106 | + unknownArgsCount: number; | |
| 107 | + unknownReturnsCount: number; | |
| 108 | + partialUnknownArgsCount: number; | |
| 109 | + partialUnknownReturnsCount: number; | |
| 110 | + argsQuality: number; | |
| 111 | + returnsQuality: number; | |
| 112 | + overallQuality: number; | |
| 113 | +} | |
| 114 | + | |
| 115 | +export interface AnonymousContext { | |
| 116 | + sessionId: string; | |
| 117 | + workspaceId: Id<"workspaces">; | |
| 118 | + actorId: string; | |
| 119 | + clientId: string; | |
| 120 | + accountId: string; | |
| 121 | + userId: string; | |
| 122 | + createdAt: number; | |
| 123 | + lastSeenAt: number; | |
| 124 | +} | |
| 23 | 125 | |
| 24 | −import type { TaskStatus, ApprovalStatus, CredentialScope } from "@executor/contracts"; | |
| 126 | +// ── Web-only types ──────────────────────────────────────────────────────────── | |
| 25 | 127 | |
| 26 | 128 | export type ApprovalDecision = "approved" | "denied"; |
| 27 | 129 | |
| @@ -93,7 +93,7 @@ export declare const api: { · 92 unmodified lines | ||
| 93 | 93 | secretJson: any; |
| 94 | 94 | sessionId?: string; |
| 95 | 95 | sourceKey: string; |
| 96 | − workspaceId: string; | |
| 96 | + workspaceId: Id<"workspaces">; | |
| 97 | 97 | }, |
| 98 | 98 | any |
| 99 | 99 | >; |
| @@ -107,7 +107,7 @@ export declare const api: { · 7 unmodified lines | ||
| 107 | 107 | id: string; |
| 108 | 108 | prompt: string; |
| 109 | 109 | requesterId: string; |
| 110 | − workspaceId: string; | |
| 110 | + workspaceId: Id<"workspaces">; | |
| 111 | 111 | }, |
| 112 | 112 | any |
| 113 | 113 | >; |
| @@ -142,7 +142,7 @@ export declare const api: { · 28 unmodified lines | ||
| 142 | 142 | runtimeId?: string; |
| 143 | 143 | sessionId?: string; |
| 144 | 144 | timeoutMs?: number; |
| 145 | − workspaceId: string; | |
| 145 | + workspaceId: Id<"workspaces">; | |
| 146 | 146 | }, |
| 147 | 147 | any |
| 148 | 148 | >; |
| @@ -155,7 +155,7 @@ export declare const api: { · 6 unmodified lines | ||
| 155 | 155 | reason?: string; |
| 156 | 156 | reviewerId?: string; |
| 157 | 157 | sessionId?: string; |
| 158 | − workspaceId: string; | |
| 158 | + workspaceId: Id<"workspaces">; | |
| 159 | 159 | }, |
| 160 | 160 | any |
| 161 | 161 | >; |
| @@ -168,7 +168,7 @@ export declare const api: { · 6 unmodified lines | ||
| 168 | 168 | actorId?: string; |
| 169 | 169 | clientId?: string; |
| 170 | 170 | sessionId?: string; |
| 171 | − workspaceId: string; | |
| 171 | + workspaceId: Id<"workspaces">; | |
| 172 | 172 | }, |
| 173 | 173 | any |
| 174 | 174 | >; |
| @@ -179,7 +179,7 @@ export declare const api: { · 4 unmodified lines | ||
| 179 | 179 | actorId?: string; |
| 180 | 180 | clientId?: string; |
| 181 | 181 | sessionId?: string; |
| 182 | − workspaceId: string; | |
| 182 | + workspaceId: Id<"workspaces">; | |
| 183 | 183 | }, |
| 184 | 184 | any |
| 185 | 185 | >; |
| @@ -292,31 +292,31 @@ export declare const api: { · 106 unmodified lines | ||
| 292 | 292 | deleteToolSource: FunctionReference< |
| 293 | 293 | "mutation", |
| 294 | 294 | "public", |
| 295 | − { sessionId?: string; sourceId: string; workspaceId: string }, | |
| 295 | + { sessionId?: string; sourceId: string; workspaceId: Id<"workspaces"> }, | |
| 296 | 296 | any |
| 297 | 297 | >; |
| 298 | 298 | getRequestContext: FunctionReference< |
| 299 | 299 | "query", |
| 300 | 300 | "public", |
| 301 | − { sessionId?: string; workspaceId: string }, | |
| 301 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 302 | 302 | any |
| 303 | 303 | >; |
| 304 | 304 | getTask: FunctionReference< |
| 305 | 305 | "query", |
| 306 | 306 | "public", |
| 307 | − { sessionId?: string; taskId: string; workspaceId: string }, | |
| 307 | + { sessionId?: string; taskId: string; workspaceId: Id<"workspaces"> }, | |
| 308 | 308 | any |
| 309 | 309 | >; |
| 310 | 310 | getTaskInWorkspace: FunctionReference< |
| 311 | 311 | "query", |
| 312 | 312 | "public", |
| 313 | − { sessionId?: string; taskId: string; workspaceId: string }, | |
| 313 | + { sessionId?: string; taskId: string; workspaceId: Id<"workspaces"> }, | |
| 314 | 314 | any |
| 315 | 315 | >; |
| 316 | 316 | listAccessPolicies: FunctionReference< |
| 317 | 317 | "query", |
| 318 | 318 | "public", |
| 319 | − { sessionId?: string; workspaceId: string }, | |
| 319 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 320 | 320 | any |
| 321 | 321 | >; |
| 322 | 322 | listApprovals: FunctionReference< |
| @@ -325,7 +325,7 @@ export declare const api: { · 2 unmodified lines | ||
| 325 | 325 | { |
| 326 | 326 | sessionId?: string; |
| 327 | 327 | status?: "pending" | "approved" | "denied"; |
| 328 | − workspaceId: string; | |
| 328 | + workspaceId: Id<"workspaces">; | |
| 329 | 329 | }, |
| 330 | 330 | any |
| 331 | 331 | >; |
| @@ -333,32 +333,32 @@ export declare const api: { · 1 unmodified line | ||
| 333 | 333 | listCredentials: FunctionReference< |
| 334 | 334 | "query", |
| 335 | 335 | "public", |
| 336 | − { sessionId?: string; workspaceId: string }, | |
| 336 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 337 | 337 | any |
| 338 | 338 | >; |
| 339 | 339 | listPendingApprovals: FunctionReference< |
| 340 | 340 | "query", |
| 341 | 341 | "public", |
| 342 | − { sessionId?: string; workspaceId: string }, | |
| 342 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 343 | 343 | any |
| 344 | 344 | >; |
| 345 | 345 | listRuntimeTargets: FunctionReference<"query", "public", {}, any>; |
| 346 | 346 | listTaskEvents: FunctionReference< |
| 347 | 347 | "query", |
| 348 | 348 | "public", |
| 349 | − { sessionId?: string; taskId: string; workspaceId: string }, | |
| 349 | + { sessionId?: string; taskId: string; workspaceId: Id<"workspaces"> }, | |
| 350 | 350 | any |
| 351 | 351 | >; |
| 352 | 352 | listTasks: FunctionReference< |
| 353 | 353 | "query", |
| 354 | 354 | "public", |
| 355 | − { sessionId?: string; workspaceId: string }, | |
| 355 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 356 | 356 | any |
| 357 | 357 | >; |
| 358 | 358 | listToolSources: FunctionReference< |
| 359 | 359 | "query", |
| 360 | 360 | "public", |
| 361 | − { sessionId?: string; workspaceId: string }, | |
| 361 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 362 | 362 | any |
| 363 | 363 | >; |
| 364 | 364 | resolveCredential: FunctionReference< |
| @@ -369,7 +369,7 @@ export declare const api: { · 4 unmodified lines | ||
| 369 | 369 | scope: "workspace" | "actor"; |
| 370 | 370 | sessionId?: string; |
| 371 | 371 | sourceKey: string; |
| 372 | − workspaceId: string; | |
| 372 | + workspaceId: Id<"workspaces">; | |
| 373 | 373 | }, |
| 374 | 374 | any |
| 375 | 375 | >; |
| @@ -384,7 +384,7 @@ export declare const api: { · 8 unmodified lines | ||
| 384 | 384 | priority?: number; |
| 385 | 385 | sessionId?: string; |
| 386 | 386 | toolPathPattern: string; |
| 387 | − workspaceId: string; | |
| 387 | + workspaceId: Id<"workspaces">; | |
| 388 | 388 | }, |
| 389 | 389 | any |
| 390 | 390 | >; |
| @@ -399,7 +399,7 @@ export declare const api: { · 8 unmodified lines | ||
| 399 | 399 | secretJson: any; |
| 400 | 400 | sessionId?: string; |
| 401 | 401 | sourceKey: string; |
| 402 | − workspaceId: string; | |
| 402 | + workspaceId: Id<"workspaces">; | |
| 403 | 403 | }, |
| 404 | 404 | any |
| 405 | 405 | >; |
| @@ -413,7 +413,7 @@ export declare const api: { · 7 unmodified lines | ||
| 413 | 413 | name: string; |
| 414 | 414 | sessionId?: string; |
| 415 | 415 | type: "mcp" | "openapi" | "graphql"; |
| 416 | − workspaceId: string; | |
| 416 | + workspaceId: Id<"workspaces">; | |
| 417 | 417 | }, |
| 418 | 418 | any |
| 419 | 419 | >; |
| @@ -532,7 +532,7 @@ export declare const internal: { · 112 unmodified lines | ||
| 532 | 532 | metadata?: any; |
| 533 | 533 | runtimeId: string; |
| 534 | 534 | timeoutMs?: number; |
| 535 | − workspaceId: string; | |
| 535 | + workspaceId: Id<"workspaces">; | |
| 536 | 536 | }, |
| 537 | 537 | any |
| 538 | 538 | >; |
| @@ -545,7 +545,7 @@ export declare const internal: { · 6 unmodified lines | ||
| 545 | 545 | deleteToolSource: FunctionReference< |
| 546 | 546 | "mutation", |
| 547 | 547 | "internal", |
| 548 | − { sourceId: string; workspaceId: string }, | |
| 548 | + { sourceId: string; workspaceId: Id<"workspaces"> }, | |
| 549 | 549 | any |
| 550 | 550 | >; |
| 551 | 551 | getApproval: FunctionReference< |
| @@ -557,39 +557,42 @@ export declare const internal: { · 5 unmodified lines | ||
| 557 | 557 | getApprovalInWorkspace: FunctionReference< |
| 558 | 558 | "query", |
| 559 | 559 | "internal", |
| 560 | − { approvalId: string; workspaceId: string }, | |
| 560 | + { approvalId: string; workspaceId: Id<"workspaces"> }, | |
| 561 | 561 | any |
| 562 | 562 | >; |
| 563 | 563 | getTask: FunctionReference<"query", "internal", { taskId: string }, any>; |
| 564 | 564 | getTaskInWorkspace: FunctionReference< |
| 565 | 565 | "query", |
| 566 | 566 | "internal", |
| 567 | − { taskId: string; workspaceId: string }, | |
| 567 | + { taskId: string; workspaceId: Id<"workspaces"> }, | |
| 568 | 568 | any |
| 569 | 569 | >; |
| 570 | 570 | listAccessPolicies: FunctionReference< |
| 571 | 571 | "query", |
| 572 | 572 | "internal", |
| 573 | − { workspaceId: string }, | |
| 573 | + { workspaceId: Id<"workspaces"> }, | |
| 574 | 574 | any |
| 575 | 575 | >; |
| 576 | 576 | listApprovals: FunctionReference< |
| 577 | 577 | "query", |
| 578 | 578 | "internal", |
| 579 | − { status?: "pending" | "approved" | "denied"; workspaceId: string }, | |
| 579 | + { | |
| 580 | + status?: "pending" | "approved" | "denied"; | |
| 581 | + workspaceId: Id<"workspaces">; | |
| 582 | + }, | |
| 580 | 583 | any |
| 581 | 584 | >; |
| 582 | 585 | listCredentialProviders: FunctionReference<"query", "internal", {}, any>; |
| 583 | 586 | listCredentials: FunctionReference< |
| 584 | 587 | "query", |
| 585 | 588 | "internal", |
| 586 | − { workspaceId: string }, | |
| 589 | + { workspaceId: Id<"workspaces"> }, | |
| 587 | 590 | any |
| 588 | 591 | >; |
| 589 | 592 | listPendingApprovals: FunctionReference< |
| 590 | 593 | "query", |
| 591 | 594 | "internal", |
| 592 | − { workspaceId: string }, | |
| 595 | + { workspaceId: Id<"workspaces"> }, | |
| 593 | 596 | any |
| 594 | 597 | >; |
| 595 | 598 | listQueuedTaskIds: FunctionReference< |
| @@ -608,13 +611,13 @@ export declare const internal: { · 12 unmodified lines | ||
| 608 | 611 | listTasks: FunctionReference< |
| 609 | 612 | "query", |
| 610 | 613 | "internal", |
| 611 | − { workspaceId: string }, | |
| 614 | + { workspaceId: Id<"workspaces"> }, | |
| 612 | 615 | any |
| 613 | 616 | >; |
| 614 | 617 | listToolSources: FunctionReference< |
| 615 | 618 | "query", |
| 616 | 619 | "internal", |
| 617 | − { workspaceId: string }, | |
| 620 | + { workspaceId: Id<"workspaces"> }, | |
| 618 | 621 | any |
| 619 | 622 | >; |
| 620 | 623 | markTaskFinished: FunctionReference< |
| @@ -654,7 +657,7 @@ export declare const internal: { · 33 unmodified lines | ||
| 654 | 657 | actorId?: string; |
| 655 | 658 | scope: "workspace" | "actor"; |
| 656 | 659 | sourceKey: string; |
| 657 | − workspaceId: string; | |
| 660 | + workspaceId: Id<"workspaces">; | |
| 658 | 661 | }, |
| 659 | 662 | any |
| 660 | 663 | >; |
| @@ -668,7 +671,7 @@ export declare const internal: { · 7 unmodified lines | ||
| 668 | 671 | id?: string; |
| 669 | 672 | priority?: number; |
| 670 | 673 | toolPathPattern: string; |
| 671 | − workspaceId: string; | |
| 674 | + workspaceId: Id<"workspaces">; | |
| 672 | 675 | }, |
| 673 | 676 | any |
| 674 | 677 | >; |
| @@ -682,7 +685,7 @@ export declare const internal: { · 7 unmodified lines | ||
| 682 | 685 | scope: "workspace" | "actor"; |
| 683 | 686 | secretJson: any; |
| 684 | 687 | sourceKey: string; |
| 685 | − workspaceId: string; | |
| 688 | + workspaceId: Id<"workspaces">; | |
| 686 | 689 | }, |
| 687 | 690 | any |
| 688 | 691 | >; |
| @@ -695,7 +698,7 @@ export declare const internal: { · 6 unmodified lines | ||
| 695 | 698 | id?: string; |
| 696 | 699 | name: string; |
| 697 | 700 | type: "mcp" | "openapi" | "graphql"; |
| 698 | − workspaceId: string; | |
| 701 | + workspaceId: Id<"workspaces">; | |
| 699 | 702 | }, |
| 700 | 703 | any |
| 701 | 704 | >; |
| @@ -722,7 +725,7 @@ export declare const internal: { · 20 unmodified lines | ||
| 722 | 725 | metadata?: any; |
| 723 | 726 | runtimeId?: string; |
| 724 | 727 | timeoutMs?: number; |
| 725 | − workspaceId: string; | |
| 728 | + workspaceId: Id<"workspaces">; | |
| 726 | 729 | }, |
| 727 | 730 | any |
| 728 | 731 | >; |
| @@ -734,7 +737,7 @@ export declare const internal: { · 5 unmodified lines | ||
| 734 | 737 | decision: "approved" | "denied"; |
| 735 | 738 | reason?: string; |
| 736 | 739 | reviewerId?: string; |
| 737 | − workspaceId: string; | |
| 740 | + workspaceId: Id<"workspaces">; | |
| 738 | 741 | }, |
| 739 | 742 | any |
| 740 | 743 | >; |
| @@ -749,13 +752,13 @@ export declare const internal: { · 8 unmodified lines | ||
| 749 | 752 | listToolsInternal: FunctionReference< |
| 750 | 753 | "action", |
| 751 | 754 | "internal", |
| 752 | − { actorId?: string; clientId?: string; workspaceId: string }, | |
| 755 | + { actorId?: string; clientId?: string; workspaceId: Id<"workspaces"> }, | |
| 753 | 756 | any |
| 754 | 757 | >; |
| 755 | 758 | listToolsWithWarningsInternal: FunctionReference< |
| 756 | 759 | "action", |
| 757 | 760 | "internal", |
| 758 | − { actorId?: string; clientId?: string; workspaceId: string }, | |
| 761 | + { actorId?: string; clientId?: string; workspaceId: Id<"workspaces"> }, | |
| 759 | 762 | any |
| 760 | 763 | >; |
| 761 | 764 | runTask: FunctionReference<"action", "internal", { taskId: string }, any>; |
| @@ -814,6 +817,18 @@ export declare const internal: { · 52 unmodified lines | ||
| 814 | 817 | >; |
| 815 | 818 | }; |
| 816 | 819 | migrations: { |
| 820 | + backfillDtsStorageIds: FunctionReference< | |
| 821 | + "mutation", | |
| 822 | + "internal", | |
| 823 | + { | |
| 824 | + batchSize?: number; | |
| 825 | + cursor?: string | null; | |
| 826 | + dryRun?: boolean; | |
| 827 | + fn?: string; | |
| 828 | + next?: Array<string>; | |
| 829 | + }, | |
| 830 | + any | |
| 831 | + >; | |
| 817 | 832 | deleteAnonymousSessionsMissingAccountId: FunctionReference< |
| 818 | 833 | "mutation", |
| 819 | 834 | "internal", |
| @@ -826,6 +841,18 @@ export declare const internal: { · 6 unmodified lines | ||
| 826 | 841 | }, |
| 827 | 842 | any |
| 828 | 843 | >; |
| 844 | + deleteAnonymousSessionsMissingUserId: FunctionReference< | |
| 845 | + "mutation", | |
| 846 | + "internal", | |
| 847 | + { | |
| 848 | + batchSize?: number; | |
| 849 | + cursor?: string | null; | |
| 850 | + dryRun?: boolean; | |
| 851 | + fn?: string; | |
| 852 | + next?: Array<string>; | |
| 853 | + }, | |
| 854 | + any | |
| 855 | + >; | |
| 829 | 856 | deleteInvitesMissingProvider: FunctionReference< |
| 830 | 857 | "mutation", |
| 831 | 858 | "internal", |
| @@ -916,13 +943,13 @@ export declare const internal: { · 84 unmodified lines | ||
| 916 | 943 | getWorkspaceAccessForRequest: FunctionReference< |
| 917 | 944 | "query", |
| 918 | 945 | "internal", |
| 919 | − { sessionId?: string; workspaceId: string }, | |
| 946 | + { sessionId?: string; workspaceId: Id<"workspaces"> }, | |
| 920 | 947 | any |
| 921 | 948 | >; |
| 922 | 949 | getWorkspaceAccessForWorkosSubject: FunctionReference< |
| 923 | 950 | "query", |
| 924 | 951 | "internal", |
| 925 | − { subject: string; workspaceId: string }, | |
| 952 | + { subject: string; workspaceId: Id<"workspaces"> }, | |
| 926 | 953 | any |
| 927 | 954 | >; |
| 928 | 955 | }; |
| @@ -930,25 +957,25 @@ export declare const internal: { · 1 unmodified line | ||
| 930 | 957 | getDtsStorageIds: FunctionReference< |
| 931 | 958 | "query", |
| 932 | 959 | "internal", |
| 933 | − { workspaceId: string }, | |
| 960 | + { workspaceId: Id<"workspaces"> }, | |
| 934 | 961 | any |
| 935 | 962 | >; |
| 936 | 963 | getEntry: FunctionReference< |
| 937 | 964 | "query", |
| 938 | 965 | "internal", |
| 939 | − { signature: string; workspaceId: string }, | |
| 966 | + { signature: string; workspaceId: Id<"workspaces"> }, | |
| 940 | 967 | any |
| 941 | 968 | >; |
| 942 | 969 | putEntry: FunctionReference< |
| 943 | 970 | "mutation", |
| 944 | 971 | "internal", |
| 945 | 972 | { |
| 946 | − dtsStorageIds?: Array<{ sourceKey: string; storageId: Id<"_storage"> }>; | |
| 973 | + dtsStorageIds: Array<{ sourceKey: string; storageId: Id<"_storage"> }>; | |
| 947 | 974 | signature: string; |
| 948 | 975 | sizeBytes: number; |
| 949 | 976 | storageId: Id<"_storage">; |
| 950 | 977 | toolCount: number; |
| 951 | − workspaceId: string; | |
| 978 | + workspaceId: Id<"workspaces">; | |
| 952 | 979 | }, |
| 953 | 980 | any |
| 954 | 981 | >; |
| @@ -153,10 +153,6 @@ async function addWorkspaceMember( · 152 unmodified lines | ||
| 153 | 153 | }); |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | −function asWorkspaceId(id: Id<"workspaces">): string { | |
| 157 | − return id as unknown as string; | |
| 158 | −} | |
| 159 | − | |
| 160 | 156 | // --------------------------------------------------------------------------- |
| 161 | 157 | // Tests |
| 162 | 158 | // --------------------------------------------------------------------------- |
| @@ -168,7 +164,7 @@ describe("authentication", () => { · 5 unmodified lines | ||
| 168 | 164 | |
| 169 | 165 | await expect( |
| 170 | 166 | t.query(api.workspace.listTasks, { |
| 171 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 167 | + workspaceId: owner.workspaceId, | |
| 172 | 168 | }), |
| 173 | 169 | ).rejects.toThrow("Must be signed in"); |
| 174 | 170 | }); |
| @@ -179,7 +175,7 @@ describe("authentication", () => { · 4 unmodified lines | ||
| 179 | 175 | |
| 180 | 176 | await expect( |
| 181 | 177 | t.mutation(api.workspace.upsertAccessPolicy, { |
| 182 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 178 | + workspaceId: owner.workspaceId, | |
| 183 | 179 | toolPathPattern: "*", |
| 184 | 180 | decision: "allow", |
| 185 | 181 | }), |
| @@ -203,7 +199,7 @@ describe("authentication", () => { · 17 unmodified lines | ||
| 203 | 199 | |
| 204 | 200 | await expect( |
| 205 | 201 | t.mutation(api.executor.createTask, { |
| 206 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 202 | + workspaceId: owner.workspaceId, | |
| 207 | 203 | code: "console.log('hello')", |
| 208 | 204 | }), |
| 209 | 205 | ).rejects.toThrow("Must be signed in"); |
| @@ -215,7 +211,7 @@ describe("authentication", () => { · 5 unmodified lines | ||
| 215 | 211 | const authed = t.withIdentity({ subject: "user-ok" }); |
| 216 | 212 | |
| 217 | 213 | const tasks = await authed.query(api.workspace.listTasks, { |
| 218 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 214 | + workspaceId: owner.workspaceId, | |
| 219 | 215 | }); |
| 220 | 216 | |
| 221 | 217 | expect(tasks).toEqual([]); |
| @@ -259,7 +255,7 @@ describe("workspace access controls", () => { · 37 unmodified lines | ||
| 259 | 255 | |
| 260 | 256 | const authedMember = t.withIdentity({ subject: "ws-member" }); |
| 261 | 257 | const tasks = await authedMember.query(api.workspace.listTasks, { |
| 262 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 258 | + workspaceId: owner.workspaceId, | |
| 263 | 259 | }); |
| 264 | 260 | |
| 265 | 261 | expect(tasks).toEqual([]); |
| @@ -278,7 +274,7 @@ describe("workspace access controls", () => { · 12 unmodified lines | ||
| 278 | 274 | |
| 279 | 275 | const authedMember = t.withIdentity({ subject: "ws-member-org-fallback" }); |
| 280 | 276 | const tasks = await authedMember.query(api.workspace.listTasks, { |
| 281 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 277 | + workspaceId: owner.workspaceId, | |
| 282 | 278 | }); |
| 283 | 279 | |
| 284 | 280 | expect(tasks).toEqual([]); |
| @@ -293,7 +289,7 @@ describe("workspace access controls", () => { · 8 unmodified lines | ||
| 293 | 289 | |
| 294 | 290 | await expect( |
| 295 | 291 | authedOutsider.query(api.workspace.listTasks, { |
| 296 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 292 | + workspaceId: owner.workspaceId, | |
| 297 | 293 | }), |
| 298 | 294 | ).rejects.toThrow("You are not a member of this workspace"); |
| 299 | 295 | }); |
| @@ -319,7 +315,7 @@ describe("workspace access controls", () => { · 19 unmodified lines | ||
| 319 | 315 | |
| 320 | 316 | await expect( |
| 321 | 317 | authedRemoved.query(api.workspace.listTasks, { |
| 322 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 318 | + workspaceId: owner.workspaceId, | |
| 323 | 319 | }), |
| 324 | 320 | ).rejects.toThrow("You are not a member of this workspace"); |
| 325 | 321 | }); |
| @@ -344,7 +340,7 @@ describe("workspace access controls", () => { · 18 unmodified lines | ||
| 344 | 340 | |
| 345 | 341 | await expect( |
| 346 | 342 | authedMember.mutation(api.workspace.upsertAccessPolicy, { |
| 347 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 343 | + workspaceId: owner.workspaceId, | |
| 348 | 344 | toolPathPattern: "*", |
| 349 | 345 | decision: "allow", |
| 350 | 346 | }), |
| @@ -370,7 +366,7 @@ describe("workspace access controls", () => { · 19 unmodified lines | ||
| 370 | 366 | const authedAdmin = t.withIdentity({ subject: "ws-policy-admin" }); |
| 371 | 367 | |
| 372 | 368 | const policy = await authedAdmin.mutation(api.workspace.upsertAccessPolicy, { |
| 373 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 369 | + workspaceId: owner.workspaceId, | |
| 374 | 370 | toolPathPattern: "*.delete", |
| 375 | 371 | decision: "require_approval", |
| 376 | 372 | }); |
| @@ -385,7 +381,7 @@ describe("workspace access controls", () => { · 8 unmodified lines | ||
| 385 | 381 | const authedOwner = t.withIdentity({ subject: "ws-owner-policy" }); |
| 386 | 382 | |
| 387 | 383 | const policy = await authedOwner.mutation(api.workspace.upsertAccessPolicy, { |
| 388 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 384 | + workspaceId: owner.workspaceId, | |
| 389 | 385 | toolPathPattern: "*", |
| 390 | 386 | decision: "allow", |
| 391 | 387 | }); |
| @@ -413,7 +409,7 @@ describe("workspace access controls", () => { · 21 unmodified lines | ||
| 413 | 409 | |
| 414 | 410 | await expect( |
| 415 | 411 | authedMember.mutation(api.workspace.upsertCredential, { |
| 416 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 412 | + workspaceId: owner.workspaceId, | |
| 417 | 413 | sourceKey: "openapi:github", |
| 418 | 414 | scope: "workspace", |
| 419 | 415 | secretJson: { token: "ghp_test" }, |
| @@ -441,7 +437,7 @@ describe("workspace access controls", () => { · 21 unmodified lines | ||
| 441 | 437 | |
| 442 | 438 | await expect( |
| 443 | 439 | authedMember.mutation(api.workspace.upsertToolSource, { |
| 444 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 440 | + workspaceId: owner.workspaceId, | |
| 445 | 441 | name: "my-tool", |
| 446 | 442 | type: "mcp", |
| 447 | 443 | config: { url: "https://example.com" }, |
| @@ -469,7 +465,7 @@ describe("workspace access controls", () => { · 21 unmodified lines | ||
| 469 | 465 | |
| 470 | 466 | await expect( |
| 471 | 467 | authedMember.mutation(api.workspace.deleteToolSource, { |
| 472 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 468 | + workspaceId: owner.workspaceId, | |
| 473 | 469 | sourceId: "src_nonexistent", |
| 474 | 470 | }), |
| 475 | 471 | ).rejects.toThrow("Only workspace admins can perform this action"); |
| @@ -494,7 +490,7 @@ describe("workspace access controls", () => { · 18 unmodified lines | ||
| 494 | 490 | const authedMember = t.withIdentity({ subject: "read-policy-member" }); |
| 495 | 491 | |
| 496 | 492 | const policies = await authedMember.query(api.workspace.listAccessPolicies, { |
| 497 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 493 | + workspaceId: owner.workspaceId, | |
| 498 | 494 | }); |
| 499 | 495 | |
| 500 | 496 | expect(policies).toEqual([]); |
| @@ -519,7 +515,7 @@ describe("workspace access controls", () => { · 18 unmodified lines | ||
| 519 | 515 | const authedMember = t.withIdentity({ subject: "read-ts-member" }); |
| 520 | 516 | |
| 521 | 517 | const sources = await authedMember.query(api.workspace.listToolSources, { |
| 522 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 518 | + workspaceId: owner.workspaceId, | |
| 523 | 519 | }); |
| 524 | 520 | |
| 525 | 521 | expect(sources).toEqual([]); |
| @@ -695,7 +691,7 @@ describe("cross-workspace isolation", () => { · 169 unmodified lines | ||
| 695 | 691 | // User A tries to read User B's workspace |
| 696 | 692 | await expect( |
| 697 | 693 | authedA.query(api.workspace.listTasks, { |
| 698 | − workspaceId: asWorkspaceId(userB.workspaceId), | |
| 694 | + workspaceId: userB.workspaceId, | |
| 699 | 695 | }), |
| 700 | 696 | ).rejects.toThrow("You are not a member of this workspace"); |
| 701 | 697 | }); |
| @@ -709,7 +705,7 @@ describe("cross-workspace isolation", () => { · 7 unmodified lines | ||
| 709 | 705 | |
| 710 | 706 | await expect( |
| 711 | 707 | authedA.mutation(api.executor.createTask, { |
| 712 | − workspaceId: asWorkspaceId(userB.workspaceId), | |
| 708 | + workspaceId: userB.workspaceId, | |
| 713 | 709 | code: "console.log('pwned')", |
| 714 | 710 | }), |
| 715 | 711 | ).rejects.toThrow("You are not a member of this workspace"); |
| @@ -724,7 +720,7 @@ describe("cross-workspace isolation", () => { · 8 unmodified lines | ||
| 724 | 720 | |
| 725 | 721 | await expect( |
| 726 | 722 | authedA.mutation(api.workspace.upsertAccessPolicy, { |
| 727 | − workspaceId: asWorkspaceId(userB.workspaceId), | |
| 723 | + workspaceId: userB.workspaceId, | |
| 728 | 724 | toolPathPattern: "*", |
| 729 | 725 | decision: "allow", |
| 730 | 726 | }), |
| @@ -738,7 +734,7 @@ describe("cross-workspace isolation", () => { · 7 unmodified lines | ||
| 738 | 734 | |
| 739 | 735 | await expect( |
| 740 | 736 | authed.query(api.workspace.listTasks, { |
| 741 | − workspaceId: "invalid_workspace_id", | |
| 737 | + workspaceId: "invalid_workspace_id" as Id<"workspaces">, | |
| 742 | 738 | }), |
| 743 | 739 | ).rejects.toThrow(); |
| 744 | 740 | }); |
| @@ -1018,12 +1014,12 @@ describe("task creation with access controls", () => { · 273 unmodified lines | ||
| 1018 | 1014 | const authed = t.withIdentity({ subject: "task-creator" }); |
| 1019 | 1015 | |
| 1020 | 1016 | const result = await authed.mutation(api.executor.createTask, { |
| 1021 | − workspaceId: asWorkspaceId(user.workspaceId), | |
| 1017 | + workspaceId: user.workspaceId, | |
| 1022 | 1018 | code: "console.log('hello')", |
| 1023 | 1019 | }); |
| 1024 | 1020 | |
| 1025 | 1021 | expect(result.task.status).toBe("queued"); |
| 1026 | − expect(result.task.workspaceId).toBe(asWorkspaceId(user.workspaceId)); | |
| 1022 | + expect(result.task.workspaceId).toBe(user.workspaceId); | |
| 1027 | 1023 | }); |
| 1028 | 1024 | |
| 1029 | 1025 | test("task is scoped to workspace and visible to members", async () => { |
| @@ -1045,14 +1041,14 @@ describe("task creation with access controls", () => { · 15 unmodified lines | ||
| 1045 | 1041 | // Owner creates a task |
| 1046 | 1042 | const authedOwner = t.withIdentity({ subject: "task-scope-owner" }); |
| 1047 | 1043 | await authedOwner.mutation(api.executor.createTask, { |
| 1048 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1044 | + workspaceId: owner.workspaceId, | |
| 1049 | 1045 | code: "1 + 1", |
| 1050 | 1046 | }); |
| 1051 | 1047 | |
| 1052 | 1048 | // Member can see it |
| 1053 | 1049 | const authedMember = t.withIdentity({ subject: "task-scope-member" }); |
| 1054 | 1050 | const tasks = await authedMember.query(api.workspace.listTasks, { |
| 1055 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1051 | + workspaceId: owner.workspaceId, | |
| 1056 | 1052 | }); |
| 1057 | 1053 | |
| 1058 | 1054 | expect(tasks.length).toBe(1); |
| @@ -1067,7 +1063,7 @@ describe("task creation with access controls", () => { · 8 unmodified lines | ||
| 1067 | 1063 | // Owner creates a task |
| 1068 | 1064 | const authedOwner = t.withIdentity({ subject: "task-vis-owner" }); |
| 1069 | 1065 | await authedOwner.mutation(api.executor.createTask, { |
| 1070 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1066 | + workspaceId: owner.workspaceId, | |
| 1071 | 1067 | code: "secret code", |
| 1072 | 1068 | }); |
| 1073 | 1069 | |
| @@ -1076,7 +1072,7 @@ describe("task creation with access controls", () => { · 2 unmodified lines | ||
| 1076 | 1072 | |
| 1077 | 1073 | await expect( |
| 1078 | 1074 | authedOutsider.query(api.workspace.listTasks, { |
| 1079 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1075 | + workspaceId: owner.workspaceId, | |
| 1080 | 1076 | }), |
| 1081 | 1077 | ).rejects.toThrow("You are not a member of this workspace"); |
| 1082 | 1078 | }); |
| @@ -1090,7 +1086,7 @@ describe("credential security", () => { · 7 unmodified lines | ||
| 1090 | 1086 | |
| 1091 | 1087 | // Create a credential via admin |
| 1092 | 1088 | await authed.mutation(api.workspace.upsertCredential, { |
| 1093 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1089 | + workspaceId: owner.workspaceId, | |
| 1094 | 1090 | sourceKey: "openapi:stripe", |
| 1095 | 1091 | scope: "workspace", |
| 1096 | 1092 | secretJson: { token: "sk_live_super_secret" }, |
| @@ -1098,7 +1094,7 @@ describe("credential security", () => { · 1 unmodified line | ||
| 1098 | 1094 | |
| 1099 | 1095 | // List should redact |
| 1100 | 1096 | const credentials = await authed.query(api.workspace.listCredentials, { |
| 1101 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1097 | + workspaceId: owner.workspaceId, | |
| 1102 | 1098 | }); |
| 1103 | 1099 | |
| 1104 | 1100 | expect(credentials.length).toBe(1); |
| @@ -1126,7 +1122,7 @@ describe("credential security", () => { · 21 unmodified lines | ||
| 1126 | 1122 | |
| 1127 | 1123 | await expect( |
| 1128 | 1124 | authedMember.query(api.workspace.resolveCredential, { |
| 1129 | − workspaceId: asWorkspaceId(owner.workspaceId), | |
| 1125 | + workspaceId: owner.workspaceId, | |
| 1130 | 1126 | sourceKey: "openapi:github", |
| 1131 | 1127 | scope: "workspace", |
| 1132 | 1128 | }), |
| @@ -1155,7 +1151,7 @@ describe("role hierarchy validation", () => { · 22 unmodified lines | ||
| 1155 | 1151 | |
| 1156 | 1152 | // Admin can upsert access policies |
| 1157 | 1153 | const policy = await authed.mutation(api.workspace.upsertAccessPolicy, { |
| 1158 | − workspaceId: asWorkspaceId(org.workspaceId), | |
| 1154 | + workspaceId: org.workspaceId, | |
| 1159 | 1155 | toolPathPattern: "admin.*", |
| 1160 | 1156 | decision: "deny", |
| 1161 | 1157 | }); |
| @@ -1163,7 +1159,7 @@ describe("role hierarchy validation", () => { · 1 unmodified line | ||
| 1163 | 1159 | |
| 1164 | 1160 | // Admin can upsert tool sources |
| 1165 | 1161 | const source = await authed.mutation(api.workspace.upsertToolSource, { |
| 1166 | − workspaceId: asWorkspaceId(org.workspaceId), | |
| 1162 | + workspaceId: org.workspaceId, | |
| 1167 | 1163 | name: "admin-tool", |
| 1168 | 1164 | type: "mcp", |
| 1169 | 1165 | config: { url: "https://example.com" }, |
| @@ -11,11 +11,9 @@ async function deleteWorkspaceData( · 10 unmodified lines | ||
| 11 | 11 | return false; |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | − const workspaceKey = String(workspaceId); | |
| 15 | − | |
| 16 | 14 | const taskDocs = await ctx.db |
| 17 | 15 | .query("tasks") |
| 18 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceKey)) | |
| 16 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 19 | 17 | .collect(); |
| 20 | 18 | for (const task of taskDocs) { |
| 21 | 19 | const taskEvents = await ctx.db |
| @@ -30,7 +28,7 @@ async function deleteWorkspaceData( · 8 unmodified lines | ||
| 30 | 28 | |
| 31 | 29 | const approvalDocs = await ctx.db |
| 32 | 30 | .query("approvals") |
| 33 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceKey)) | |
| 31 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 34 | 32 | .collect(); |
| 35 | 33 | for (const approval of approvalDocs) { |
| 36 | 34 | await ctx.db.delete(approval._id); |
| @@ -38,7 +36,7 @@ async function deleteWorkspaceData( · 1 unmodified line | ||
| 38 | 36 | |
| 39 | 37 | const policyDocs = await ctx.db |
| 40 | 38 | .query("accessPolicies") |
| 41 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceKey)) | |
| 39 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 42 | 40 | .collect(); |
| 43 | 41 | for (const policy of policyDocs) { |
| 44 | 42 | await ctx.db.delete(policy._id); |
| @@ -46,7 +44,7 @@ async function deleteWorkspaceData( · 1 unmodified line | ||
| 46 | 44 | |
| 47 | 45 | const credentialDocs = await ctx.db |
| 48 | 46 | .query("sourceCredentials") |
| 49 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceKey)) | |
| 47 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 50 | 48 | .collect(); |
| 51 | 49 | for (const credential of credentialDocs) { |
| 52 | 50 | await ctx.db.delete(credential._id); |
| @@ -54,7 +52,7 @@ async function deleteWorkspaceData( · 1 unmodified line | ||
| 54 | 52 | |
| 55 | 53 | const toolSources = await ctx.db |
| 56 | 54 | .query("toolSources") |
| 57 | − .withIndex("by_workspace_updated", (q) => q.eq("workspaceId", workspaceKey)) | |
| 55 | + .withIndex("by_workspace_updated", (q) => q.eq("workspaceId", workspaceId)) | |
| 58 | 56 | .collect(); |
| 59 | 57 | for (const source of toolSources) { |
| 60 | 58 | await ctx.db.delete(source._id); |
| @@ -62,7 +60,7 @@ async function deleteWorkspaceData( · 1 unmodified line | ||
| 62 | 60 | |
| 63 | 61 | const agentTasks = await ctx.db |
| 64 | 62 | .query("agentTasks") |
| 65 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceKey)) | |
| 63 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 66 | 64 | .collect(); |
| 67 | 65 | for (const agentTask of agentTasks) { |
| 68 | 66 | await ctx.db.delete(agentTask._id); |
| @@ -70,7 +68,7 @@ async function deleteWorkspaceData( · 1 unmodified line | ||
| 70 | 68 | |
| 71 | 69 | const cachedToolsets = await ctx.db |
| 72 | 70 | .query("workspaceToolCache") |
| 73 | − .withIndex("by_workspace", (q) => q.eq("workspaceId", workspaceKey)) | |
| 71 | + .withIndex("by_workspace", (q) => q.eq("workspaceId", workspaceId)) | |
| 74 | 72 | .collect(); |
| 75 | 73 | for (const cacheEntry of cachedToolsets) { |
| 76 | 74 | await ctx.storage.delete(cacheEntry.storageId).catch(() => {}); |
| @@ -141,7 +141,7 @@ async function upsertVaultObject(args: { · 140 unmodified lines | ||
| 141 | 141 | export const upsertCredential = action({ |
| 142 | 142 | args: { |
| 143 | 143 | id: v.optional(v.string()), |
| 144 | − workspaceId: v.string(), | |
| 144 | + workspaceId: v.id("workspaces"), | |
| 145 | 145 | sessionId: v.optional(v.string()), |
| 146 | 146 | sourceKey: v.string(), |
| 147 | 147 | scope: credentialScopeValidator, |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import { expect, test } from "bun:test"; |
| 2 | 2 | import { convexTest } from "convex-test"; |
| 3 | 3 | import { internal } from "./_generated/api"; |
| 4 | +import type { Id } from "./_generated/dataModel"; | |
| 4 | 5 | import schema from "./schema"; |
| 5 | 6 | |
| 6 | 7 | function setup() { |
| @@ -10,14 +11,36 @@ function setup() { · 3 unmodified lines | ||
| 10 | 11 | }); |
| 11 | 12 | } |
| 12 | 13 | |
| 14 | +/** Insert a minimal workspace row and return its Id. */ | |
| 15 | +async function seedWorkspace(t: ReturnType<typeof setup>, name = "test-ws"): Promise<Id<"workspaces">> { | |
| 16 | + return await t.run(async (ctx) => { | |
| 17 | + const orgId = await ctx.db.insert("organizations", { | |
| 18 | + name: `${name}-org`, | |
| 19 | + slug: `${name}-org`, | |
| 20 | + status: "active", | |
| 21 | + createdAt: Date.now(), | |
| 22 | + updatedAt: Date.now(), | |
| 23 | + }); | |
| 24 | + return await ctx.db.insert("workspaces", { | |
| 25 | + name, | |
| 26 | + slug: name, | |
| 27 | + organizationId: orgId, | |
| 28 | + plan: "free", | |
| 29 | + createdAt: Date.now(), | |
| 30 | + updatedAt: Date.now(), | |
| 31 | + }); | |
| 32 | + }); | |
| 33 | +} | |
| 34 | + | |
| 13 | 35 | test("task lifecycle supports queue, run, and complete", async () => { |
| 14 | 36 | const t = setup(); |
| 37 | + const wsId = await seedWorkspace(t, "ws_1"); | |
| 15 | 38 | |
| 16 | 39 | const created = await t.mutation(internal.database.createTask, { |
| 17 | 40 | id: "task_1", |
| 18 | 41 | code: "console.log('hello')", |
| 19 | 42 | runtimeId: "local-bun", |
| 20 | − workspaceId: "ws_1", | |
| 43 | + workspaceId: wsId, | |
| 21 | 44 | actorId: "actor_1", |
| 22 | 45 | clientId: "web", |
| 23 | 46 | }); |
| @@ -49,12 +72,13 @@ test("task lifecycle supports queue, run, and complete", async () => { · 25 unmodified lines | ||
| 49 | 72 | |
| 50 | 73 | test("approval lifecycle tracks pending and resolution", async () => { |
| 51 | 74 | const t = setup(); |
| 75 | + const wsId = await seedWorkspace(t, "ws_2"); | |
| 52 | 76 | |
| 53 | 77 | await t.mutation(internal.database.createTask, { |
| 54 | 78 | id: "task_2", |
| 55 | 79 | code: "await tools.admin.delete_data({ id: 'x' })", |
| 56 | 80 | runtimeId: "local-bun", |
| 57 | − workspaceId: "ws_2", | |
| 81 | + workspaceId: wsId, | |
| 58 | 82 | actorId: "actor_2", |
| 59 | 83 | clientId: "web", |
| 60 | 84 | }); |
| @@ -67,7 +91,7 @@ test("approval lifecycle tracks pending and resolution", async () => { · 6 unmodified lines | ||
| 67 | 91 | }); |
| 68 | 92 | expect(createdApproval.status).toBe("pending"); |
| 69 | 93 | |
| 70 | − const pending = await t.query(internal.database.listPendingApprovals, { workspaceId: "ws_2" }); | |
| 94 | + const pending = await t.query(internal.database.listPendingApprovals, { workspaceId: wsId }); | |
| 71 | 95 | expect(pending.length).toBe(1); |
| 72 | 96 | expect(pending[0]?.task.id).toBe("task_2"); |
| 73 | 97 | |
| @@ -78,7 +102,7 @@ test("approval lifecycle tracks pending and resolution", async () => { · 4 unmodified lines | ||
| 78 | 102 | }); |
| 79 | 103 | expect(resolved?.status).toBe("approved"); |
| 80 | 104 | |
| 81 | − const pendingAfter = await t.query(internal.database.listPendingApprovals, { workspaceId: "ws_2" }); | |
| 105 | + const pendingAfter = await t.query(internal.database.listPendingApprovals, { workspaceId: wsId }); | |
| 82 | 106 | expect(pendingAfter).toEqual([]); |
| 83 | 107 | }); |
| 84 | 108 | |
| @@ -140,9 +164,10 @@ test("bootstrap honors MCP caller-provided session id", async () => { · 55 unmodified lines | ||
| 140 | 164 | |
| 141 | 165 | test("credentials persist provider and resolve by scope", async () => { |
| 142 | 166 | const t = setup(); |
| 167 | + const wsId = await seedWorkspace(t, "ws_cred"); | |
| 143 | 168 | |
| 144 | 169 | const workspaceCredential = await t.mutation(internal.database.upsertCredential, { |
| 145 | − workspaceId: "ws_cred", | |
| 170 | + workspaceId: wsId, | |
| 146 | 171 | sourceKey: "openapi:github", |
| 147 | 172 | scope: "workspace", |
| 148 | 173 | provider: "managed", |
| @@ -152,7 +177,7 @@ test("credentials persist provider and resolve by scope", async () => { · 3 unmodified lines | ||
| 152 | 177 | expect(workspaceCredential.provider).toBe("managed"); |
| 153 | 178 | |
| 154 | 179 | const actorCredential = await t.mutation(internal.database.upsertCredential, { |
| 155 | − workspaceId: "ws_cred", | |
| 180 | + workspaceId: wsId, | |
| 156 | 181 | sourceKey: "openapi:github", |
| 157 | 182 | scope: "actor", |
| 158 | 183 | actorId: "actor_cred", |
| @@ -164,14 +189,14 @@ test("credentials persist provider and resolve by scope", async () => { · 5 unmodified lines | ||
| 164 | 189 | expect(actorCredential.actorId).toBe("actor_cred"); |
| 165 | 190 | |
| 166 | 191 | const resolvedWorkspace = await t.query(internal.database.resolveCredential, { |
| 167 | − workspaceId: "ws_cred", | |
| 192 | + workspaceId: wsId, | |
| 168 | 193 | sourceKey: "openapi:github", |
| 169 | 194 | scope: "workspace", |
| 170 | 195 | }); |
| 171 | 196 | expect(resolvedWorkspace?.provider).toBe("managed"); |
| 172 | 197 | |
| 173 | 198 | const resolvedActor = await t.query(internal.database.resolveCredential, { |
| 174 | − workspaceId: "ws_cred", | |
| 199 | + workspaceId: wsId, | |
| 175 | 200 | sourceKey: "openapi:github", |
| 176 | 201 | scope: "actor", |
| 177 | 202 | actorId: "actor_cred", |
| @@ -181,9 +206,10 @@ test("credentials persist provider and resolve by scope", async () => { · 3 unmodified lines | ||
| 181 | 206 | |
| 182 | 207 | test("upsertCredential defaults provider to managed", async () => { |
| 183 | 208 | const t = setup(); |
| 209 | + const wsId = await seedWorkspace(t, "ws_default_provider"); | |
| 184 | 210 | |
| 185 | 211 | const credential = await t.mutation(internal.database.upsertCredential, { |
| 186 | − workspaceId: "ws_default_provider", | |
| 212 | + workspaceId: wsId, | |
| 187 | 213 | sourceKey: "openapi:stripe", |
| 188 | 214 | scope: "workspace", |
| 189 | 215 | secretJson: { token: "sk_test_123" }, |
| @@ -5,7 +5,7 @@ query, · 4 unmodified lines | ||
| 5 | 5 | type MutationCtx, type QueryCtx } from "./_generated/server"; |
| 6 | 6 | import { internalMutation, internalQuery } from "./_generated/server"; |
| 7 | 7 | import { ensureUniqueSlug } from "../lib/slug"; |
| 8 | −import type { TaskStatus } from "@executor/contracts"; | |
| 8 | +import type { TaskStatus } from "../lib/types"; | |
| 9 | 9 | |
| 10 | 10 | const DEFAULT_TIMEOUT_MS = 300_000; |
| 11 | 11 | type OrganizationRole = "owner" | "admin" | "member" | "billing_admin"; |
| @@ -335,7 +335,7 @@ export const createTask = internalMutation({ · 323 unmodified lines | ||
| 335 | 335 | runtimeId: v.string(), |
| 336 | 336 | timeoutMs: v.optional(v.number()), |
| 337 | 337 | metadata: v.optional(v.any()), |
| 338 | − workspaceId: v.string(), | |
| 338 | + workspaceId: v.id("workspaces"), | |
| 339 | 339 | actorId: v.string(), |
| 340 | 340 | clientId: v.optional(v.string()), |
| 341 | 341 | }, |
| @@ -377,7 +377,7 @@ export const getTask = internalQuery({ · 35 unmodified lines | ||
| 377 | 377 | }); |
| 378 | 378 | |
| 379 | 379 | export const listTasks = internalQuery({ |
| 380 | − args: { workspaceId: v.string() }, | |
| 380 | + args: { workspaceId: v.id("workspaces") }, | |
| 381 | 381 | handler: async (ctx, args) => { |
| 382 | 382 | const docs = await ctx.db |
| 383 | 383 | .query("tasks") |
| @@ -415,7 +415,7 @@ export const listRuntimeTargets = internalQuery({ · 31 unmodified lines | ||
| 415 | 415 | }); |
| 416 | 416 | |
| 417 | 417 | export const getTaskInWorkspace = internalQuery({ |
| 418 | − args: { taskId: v.string(), workspaceId: v.string() }, | |
| 418 | + args: { taskId: v.string(), workspaceId: v.id("workspaces") }, | |
| 419 | 419 | handler: async (ctx, args) => { |
| 420 | 420 | const doc = await getTaskDoc(ctx, args.taskId); |
| 421 | 421 | if (!doc || doc.workspaceId !== args.workspaceId) { |
| @@ -523,7 +523,7 @@ export const getApproval = internalQuery({ · 101 unmodified lines | ||
| 523 | 523 | |
| 524 | 524 | export const listApprovals = internalQuery({ |
| 525 | 525 | args: { |
| 526 | − workspaceId: v.string(), | |
| 526 | + workspaceId: v.id("workspaces"), | |
| 527 | 527 | status: v.optional(approvalStatusValidator), |
| 528 | 528 | }, |
| 529 | 529 | handler: async (ctx, args) => { |
| @@ -549,7 +549,7 @@ export const listApprovals = internalQuery({ · 19 unmodified lines | ||
| 549 | 549 | }); |
| 550 | 550 | |
| 551 | 551 | export const listPendingApprovals = internalQuery({ |
| 552 | − args: { workspaceId: v.string() }, | |
| 552 | + args: { workspaceId: v.id("workspaces") }, | |
| 553 | 553 | handler: async (ctx, args) => { |
| 554 | 554 | const docs = await ctx.db |
| 555 | 555 | .query("approvals") |
| @@ -616,7 +616,7 @@ export const resolveApproval = internalMutation({ · 60 unmodified lines | ||
| 616 | 616 | }); |
| 617 | 617 | |
| 618 | 618 | export const getApprovalInWorkspace = internalQuery({ |
| 619 | − args: { approvalId: v.string(), workspaceId: v.string() }, | |
| 619 | + args: { approvalId: v.string(), workspaceId: v.id("workspaces") }, | |
| 620 | 620 | handler: async (ctx, args) => { |
| 621 | 621 | const doc = await getApprovalDoc(ctx, args.approvalId); |
| 622 | 622 | if (!doc || doc.workspaceId !== args.workspaceId) { |
| @@ -656,7 +656,7 @@ export const createAgentTask = mutation({ · 33 unmodified lines | ||
| 656 | 656 | id: v.string(), |
| 657 | 657 | prompt: v.string(), |
| 658 | 658 | requesterId: v.string(), |
| 659 | − workspaceId: v.string(), | |
| 659 | + workspaceId: v.id("workspaces"), | |
| 660 | 660 | actorId: v.string(), |
| 661 | 661 | }, |
| 662 | 662 | handler: async (ctx, args) => { |
| @@ -796,7 +796,7 @@ export const bootstrapAnonymousSession = internalMutation({ · 133 unmodified lines | ||
| 796 | 796 | export const upsertAccessPolicy = internalMutation({ |
| 797 | 797 | args: { |
| 798 | 798 | id: v.optional(v.string()), |
| 799 | − workspaceId: v.string(), | |
| 799 | + workspaceId: v.id("workspaces"), | |
| 800 | 800 | actorId: v.optional(v.string()), |
| 801 | 801 | clientId: v.optional(v.string()), |
| 802 | 802 | toolPathPattern: v.string(), |
| @@ -847,7 +847,7 @@ export const upsertAccessPolicy = internalMutation({ · 44 unmodified lines | ||
| 847 | 847 | }); |
| 848 | 848 | |
| 849 | 849 | export const listAccessPolicies = internalQuery({ |
| 850 | − args: { workspaceId: v.string() }, | |
| 850 | + args: { workspaceId: v.id("workspaces") }, | |
| 851 | 851 | handler: async (ctx, args) => { |
| 852 | 852 | const docs = await ctx.db |
| 853 | 853 | .query("accessPolicies") |
| @@ -868,7 +868,7 @@ export const listAccessPolicies = internalQuery({ · 14 unmodified lines | ||
| 868 | 868 | export const upsertCredential = internalMutation({ |
| 869 | 869 | args: { |
| 870 | 870 | id: v.optional(v.string()), |
| 871 | − workspaceId: v.string(), | |
| 871 | + workspaceId: v.id("workspaces"), | |
| 872 | 872 | sourceKey: v.string(), |
| 873 | 873 | scope: credentialScopeValidator, |
| 874 | 874 | actorId: v.optional(v.string()), |
| @@ -932,7 +932,7 @@ export const upsertCredential = internalMutation({ · 57 unmodified lines | ||
| 932 | 932 | }); |
| 933 | 933 | |
| 934 | 934 | export const listCredentials = internalQuery({ |
| 935 | − args: { workspaceId: v.string() }, | |
| 935 | + args: { workspaceId: v.id("workspaces") }, | |
| 936 | 936 | handler: async (ctx, args) => { |
| 937 | 937 | const docs = await ctx.db |
| 938 | 938 | .query("sourceCredentials") |
| @@ -963,7 +963,7 @@ export const listCredentialProviders = internalQuery({ · 24 unmodified lines | ||
| 963 | 963 | |
| 964 | 964 | export const resolveCredential = internalQuery({ |
| 965 | 965 | args: { |
| 966 | − workspaceId: v.string(), | |
| 966 | + workspaceId: v.id("workspaces"), | |
| 967 | 967 | sourceKey: v.string(), |
| 968 | 968 | scope: credentialScopeValidator, |
| 969 | 969 | actorId: v.optional(v.string()), |
| @@ -1007,7 +1007,7 @@ export const resolveCredential = internalQuery({ · 37 unmodified lines | ||
| 1007 | 1007 | export const upsertToolSource = internalMutation({ |
| 1008 | 1008 | args: { |
| 1009 | 1009 | id: v.optional(v.string()), |
| 1010 | − workspaceId: v.string(), | |
| 1010 | + workspaceId: v.id("workspaces"), | |
| 1011 | 1011 | name: v.string(), |
| 1012 | 1012 | type: toolSourceTypeValidator, |
| 1013 | 1013 | config: v.any(), |
| @@ -1065,7 +1065,7 @@ export const upsertToolSource = internalMutation({ · 51 unmodified lines | ||
| 1065 | 1065 | }); |
| 1066 | 1066 | |
| 1067 | 1067 | export const listToolSources = internalQuery({ |
| 1068 | − args: { workspaceId: v.string() }, | |
| 1068 | + args: { workspaceId: v.id("workspaces") }, | |
| 1069 | 1069 | handler: async (ctx, args) => { |
| 1070 | 1070 | const docs = await ctx.db |
| 1071 | 1071 | .query("toolSources") |
| @@ -1077,7 +1077,7 @@ export const listToolSources = internalQuery({ · 5 unmodified lines | ||
| 1077 | 1077 | }); |
| 1078 | 1078 | |
| 1079 | 1079 | export const deleteToolSource = internalMutation({ |
| 1080 | − args: { workspaceId: v.string(), sourceId: v.string() }, | |
| 1080 | + args: { workspaceId: v.id("workspaces"), sourceId: v.string() }, | |
| 1081 | 1081 | handler: async (ctx, args) => { |
| 1082 | 1082 | const doc = await ctx.db |
| 1083 | 1083 | .query("toolSources") |
| @@ -4,6 +4,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js"; · 3 unmodified lines | ||
| 4 | 4 | import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; |
| 5 | 5 | import { ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js"; |
| 6 | 6 | import { internal } from "./_generated/api"; |
| 7 | +import type { Id } from "./_generated/dataModel"; | |
| 7 | 8 | import schema from "./schema"; |
| 8 | 9 | |
| 9 | 10 | function setup() { |
| @@ -46,7 +47,7 @@ function createMcpTransport( · 36 unmodified lines | ||
| 46 | 47 | async function waitForTaskId(t: ReturnType<typeof setup>, workspaceId: string, timeoutMs = 10_000): Promise<string> { |
| 47 | 48 | const start = Date.now(); |
| 48 | 49 | while (Date.now() - start < timeoutMs) { |
| 49 | − const tasks = await t.query(internal.database.listTasks, { workspaceId }); | |
| 50 | + const tasks = await t.query(internal.database.listTasks, { workspaceId: workspaceId as Id<"workspaces"> }); | |
| 50 | 51 | if (tasks.length > 0) { |
| 51 | 52 | return tasks[0]!.id; |
| 52 | 53 | } |
| @@ -64,7 +65,7 @@ async function waitForPendingApproval( · 11 unmodified lines | ||
| 64 | 65 | ): Promise<string> { |
| 65 | 66 | const start = Date.now(); |
| 66 | 67 | while (Date.now() - start < timeoutMs) { |
| 67 | − const approvals = await t.query(internal.database.listPendingApprovals, { workspaceId }); | |
| 68 | + const approvals = await t.query(internal.database.listPendingApprovals, { workspaceId: workspaceId as Id<"workspaces"> }); | |
| 68 | 69 | const approval = approvals.find((item: { toolPath: string; id: string }) => item.toolPath === toolPath); |
| 69 | 70 | if (approval) { |
| 70 | 71 | return approval.id; |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | import { v } from "convex/values"; |
| 2 | 2 | import { internal } from "./_generated/api"; |
| 3 | +import type { Id } from "./_generated/dataModel"; | |
| 3 | 4 | import type { MutationCtx } from "./_generated/server"; |
| 4 | 5 | import { internalMutation } from "./_generated/server"; |
| 5 | 6 | import { workspaceMutation } from "../lib/functionBuilders"; |
| @@ -30,7 +31,7 @@ async function createTaskRecord( · 24 unmodified lines | ||
| 30 | 31 | timeoutMs?: number; |
| 31 | 32 | runtimeId?: string; |
| 32 | 33 | metadata?: unknown; |
| 33 | − workspaceId: string; | |
| 34 | + workspaceId: Id<"workspaces">; | |
| 34 | 35 | actorId: string; |
| 35 | 36 | clientId?: string; |
| 36 | 37 | }, |
| @@ -92,7 +93,7 @@ async function createTaskRecord( · 55 unmodified lines | ||
| 92 | 93 | async function resolveApprovalRecord( |
| 93 | 94 | ctx: MutationCtx, |
| 94 | 95 | args: { |
| 95 | − workspaceId: string; | |
| 96 | + workspaceId: Id<"workspaces">; | |
| 96 | 97 | approvalId: string; |
| 97 | 98 | decision: "approved" | "denied"; |
| 98 | 99 | reviewerId?: string; |
| @@ -172,7 +173,7 @@ export const createTaskInternal = internalMutation({ · 73 unmodified lines | ||
| 172 | 173 | timeoutMs: v.optional(v.number()), |
| 173 | 174 | runtimeId: v.optional(v.string()), |
| 174 | 175 | metadata: v.optional(v.any()), |
| 175 | − workspaceId: v.string(), | |
| 176 | + workspaceId: v.id("workspaces"), | |
| 176 | 177 | actorId: v.string(), |
| 177 | 178 | clientId: v.optional(v.string()), |
| 178 | 179 | }, |
| @@ -204,7 +205,7 @@ export const resolveApproval = workspaceMutation({ · 25 unmodified lines | ||
| 204 | 205 | |
| 205 | 206 | export const resolveApprovalInternal = internalMutation({ |
| 206 | 207 | args: { |
| 207 | − workspaceId: v.string(), | |
| 208 | + workspaceId: v.id("workspaces"), | |
| 208 | 209 | approvalId: v.string(), |
| 209 | 210 | decision: v.union(v.literal("approved"), v.literal("denied")), |
| 210 | 211 | reviewerId: v.optional(v.string()), |
| @@ -525,8 +525,6 @@ function toToolDescriptor(tool: ToolDefinition, approval: "auto" | "required"): · 524 unmodified lines | ||
| 525 | 525 | operationId: tool.metadata?.operationId, |
| 526 | 526 | // Note: sourceDts is NOT included — it's too large to send over the wire. |
| 527 | 527 | // Monaco fetches .d.ts blobs separately via Convex storage URLs. |
| 528 | − // Legacy compat | |
| 529 | − schemaTypes: tool.metadata?.schemaTypes, | |
| 530 | 528 | }; |
| 531 | 529 | } |
| 532 | 530 | |
| @@ -910,7 +908,7 @@ async function invokeTool(ctx: any, task: TaskRecord, call: ToolCallRequest): Pr · 377 unmodified lines | ||
| 910 | 908 | |
| 911 | 909 | export const listTools = action({ |
| 912 | 910 | args: { |
| 913 | − workspaceId: v.string(), | |
| 911 | + workspaceId: v.id("workspaces"), | |
| 914 | 912 | actorId: v.optional(v.string()), |
| 915 | 913 | clientId: v.optional(v.string()), |
| 916 | 914 | sessionId: v.optional(v.string()), |
| @@ -939,7 +937,7 @@ export const listTools = action({ · 22 unmodified lines | ||
| 939 | 937 | |
| 940 | 938 | export const listToolsWithWarnings = action({ |
| 941 | 939 | args: { |
| 942 | − workspaceId: v.string(), | |
| 940 | + workspaceId: v.id("workspaces"), | |
| 943 | 941 | actorId: v.optional(v.string()), |
| 944 | 942 | clientId: v.optional(v.string()), |
| 945 | 943 | sessionId: v.optional(v.string()), |
| @@ -976,7 +974,7 @@ export const listToolsWithWarnings = action({ · 30 unmodified lines | ||
| 976 | 974 | |
| 977 | 975 | export const listToolsInternal = internalAction({ |
| 978 | 976 | args: { |
| 979 | − workspaceId: v.string(), | |
| 977 | + workspaceId: v.id("workspaces"), | |
| 980 | 978 | actorId: v.optional(v.string()), |
| 981 | 979 | clientId: v.optional(v.string()), |
| 982 | 980 | }, |
| @@ -987,7 +985,7 @@ export const listToolsInternal = internalAction({ · 4 unmodified lines | ||
| 987 | 985 | |
| 988 | 986 | export const listToolsWithWarningsInternal = internalAction({ |
| 989 | 987 | args: { |
| 990 | − workspaceId: v.string(), | |
| 988 | + workspaceId: v.id("workspaces"), | |
| 991 | 989 | actorId: v.optional(v.string()), |
| 992 | 990 | clientId: v.optional(v.string()), |
| 993 | 991 | }, |
| @@ -6,6 +6,7 @@ import { httpAction } from "./_generated/server"; · 5 unmodified lines | ||
| 6 | 6 | import { authKit } from "./auth"; |
| 7 | 7 | import { handleMcpRequest, type McpWorkspaceContext } from "../lib/mcp_server"; |
| 8 | 8 | import type { AnonymousContext, PendingApprovalRecord, TaskRecord, ToolDescriptor } from "../lib/types"; |
| 9 | +import type { Id } from "./_generated/dataModel"; | |
| 9 | 10 | |
| 10 | 11 | const http = httpRouter(); |
| 11 | 12 | const internalToken = process.env.EXECUTOR_INTERNAL_TOKEN ?? null; |
| @@ -161,7 +162,7 @@ const mcpHandler = httpAction(async (ctx, request) => { · 149 unmodified lines | ||
| 161 | 162 | try { |
| 162 | 163 | if (mcpAuthConfig.enabled && auth?.subject) { |
| 163 | 164 | const access = await ctx.runQuery(internal.workspaceAuthInternal.getWorkspaceAccessForWorkosSubject, { |
| 164 | − workspaceId: requestedContext.workspaceId, | |
| 165 | + workspaceId: requestedContext.workspaceId as Id<"workspaces">, | |
| 165 | 166 | subject: auth.subject, |
| 166 | 167 | }); |
| 167 | 168 | |
| @@ -176,7 +177,7 @@ const mcpHandler = httpAction(async (ctx, request) => { · 8 unmodified lines | ||
| 176 | 177 | } |
| 177 | 178 | |
| 178 | 179 | const access = await ctx.runQuery(internal.workspaceAuthInternal.getWorkspaceAccessForRequest, { |
| 179 | − workspaceId: requestedContext.workspaceId, | |
| 180 | + workspaceId: requestedContext.workspaceId as Id<"workspaces">, | |
| 180 | 181 | sessionId: requestedContext.sessionId, |
| 181 | 182 | }); |
| 182 | 183 | |
| @@ -212,11 +213,14 @@ const mcpHandler = httpAction(async (ctx, request) => { · 29 unmodified lines | ||
| 212 | 213 | actorId: string; |
| 213 | 214 | clientId?: string; |
| 214 | 215 | }) => { |
| 215 | − return (await ctx.runMutation(internal.executor.createTaskInternal, input)) as { task: TaskRecord }; | |
| 216 | + return (await ctx.runMutation(internal.executor.createTaskInternal, { | |
| 217 | + ...input, | |
| 218 | + workspaceId: input.workspaceId as Id<"workspaces">, | |
| 219 | + })) as { task: TaskRecord }; | |
| 216 | 220 | }, |
| 217 | 221 | getTask: async (taskId: string, workspaceId?: string) => { |
| 218 | 222 | if (workspaceId) { |
| 219 | − return (await ctx.runQuery(internal.database.getTaskInWorkspace, { taskId, workspaceId })) as TaskRecord | null; | |
| 223 | + return (await ctx.runQuery(internal.database.getTaskInWorkspace, { taskId, workspaceId: workspaceId as Id<"workspaces"> })) as TaskRecord | null; | |
| 220 | 224 | } |
| 221 | 225 | return null; |
| 222 | 226 | }, |
| @@ -231,10 +235,10 @@ const mcpHandler = httpAction(async (ctx, request) => { · 8 unmodified lines | ||
| 231 | 235 | return []; |
| 232 | 236 | } |
| 233 | 237 | |
| 234 | − return (await ctx.runAction(internal.executorNode.listToolsInternal, toolContext)) as ToolDescriptor[]; | |
| 238 | + return (await ctx.runAction(internal.executorNode.listToolsInternal, { ...toolContext, workspaceId: toolContext.workspaceId as Id<"workspaces"> })) as ToolDescriptor[]; | |
| 235 | 239 | }, |
| 236 | 240 | listToolsForTypecheck: async (toolContext: { workspaceId: string; actorId?: string; clientId?: string }) => { |
| 237 | − const result = await ctx.runAction(internal.executorNode.listToolsWithWarningsInternal, toolContext) as { | |
| 241 | + const result = await ctx.runAction(internal.executorNode.listToolsWithWarningsInternal, { ...toolContext, workspaceId: toolContext.workspaceId as Id<"workspaces"> }) as { | |
| 238 | 242 | tools: ToolDescriptor[]; |
| 239 | 243 | dtsUrls?: Record<string, string>; |
| 240 | 244 | }; |
| @@ -245,7 +249,7 @@ const mcpHandler = httpAction(async (ctx, request) => { · 4 unmodified lines | ||
| 245 | 249 | }; |
| 246 | 250 | }, |
| 247 | 251 | listPendingApprovals: async (workspaceId: string) => { |
| 248 | − return (await ctx.runQuery(internal.database.listPendingApprovals, { workspaceId })) as PendingApprovalRecord[]; | |
| 252 | + return (await ctx.runQuery(internal.database.listPendingApprovals, { workspaceId: workspaceId as Id<"workspaces"> })) as PendingApprovalRecord[]; | |
| 249 | 253 | }, |
| 250 | 254 | resolveApproval: async (input: { |
| 251 | 255 | workspaceId: string; |
| @@ -254,7 +258,10 @@ const mcpHandler = httpAction(async (ctx, request) => { · 2 unmodified lines | ||
| 254 | 258 | reviewerId?: string; |
| 255 | 259 | reason?: string; |
| 256 | 260 | }) => { |
| 257 | − return await ctx.runMutation(internal.executor.resolveApprovalInternal, input); | |
| 261 | + return await ctx.runMutation(internal.executor.resolveApprovalInternal, { | |
| 262 | + ...input, | |
| 263 | + workspaceId: input.workspaceId as Id<"workspaces">, | |
| 264 | + }); | |
| 258 | 265 | }, |
| 259 | 266 | }; |
| 260 | 267 | |
| @@ -92,4 +92,24 @@ export const deleteInvitesMissingProvider = migrations.define({ · 91 unmodified lines | ||
| 92 | 92 | }, |
| 93 | 93 | }); |
| 94 | 94 | |
| 95 | +export const deleteAnonymousSessionsMissingUserId = migrations.define({ | |
| 96 | + table: "anonymousSessions", | |
| 97 | + migrateOne: async (ctx, session) => { | |
| 98 | + if (session.userId) { | |
| 99 | + return; | |
| 100 | + } | |
| 101 | + | |
| 102 | + await ctx.db.delete(session._id); | |
| 103 | + }, | |
| 104 | +}); | |
| 105 | + | |
| 106 | +export const backfillDtsStorageIds = migrations.define({ | |
| 107 | + table: "workspaceToolCache", | |
| 108 | + migrateOne: async (_ctx, entry) => { | |
| 109 | + if (entry.dtsStorageIds === undefined) { | |
| 110 | + return { dtsStorageIds: [] }; | |
| 111 | + } | |
| 112 | + }, | |
| 113 | +}); | |
| 114 | + | |
| 95 | 115 | export const run = migrations.runner(); |
| @@ -0,0 +1,7 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@executor/convex", | |
| 3 | + "version": "0.0.1", | |
| 4 | + "private": true, | |
| 5 | + "main": "./_generated/api.js", | |
| 6 | + "types": "./_generated/api.d.ts" | |
| 7 | +} | |
| @@ -181,7 +181,7 @@ export default defineSchema({ · 180 unmodified lines | ||
| 181 | 181 | taskId: v.string(), |
| 182 | 182 | code: v.string(), |
| 183 | 183 | runtimeId: v.string(), |
| 184 | − workspaceId: v.string(), | |
| 184 | + workspaceId: v.id("workspaces"), | |
| 185 | 185 | actorId: v.string(), |
| 186 | 186 | clientId: v.string(), |
| 187 | 187 | status: taskStatus, |
| @@ -203,7 +203,7 @@ export default defineSchema({ · 15 unmodified lines | ||
| 203 | 203 | approvals: defineTable({ |
| 204 | 204 | approvalId: v.string(), |
| 205 | 205 | taskId: v.string(), |
| 206 | − workspaceId: v.string(), | |
| 206 | + workspaceId: v.id("workspaces"), | |
| 207 | 207 | toolPath: v.string(), |
| 208 | 208 | input: v.any(), |
| 209 | 209 | status: approvalStatus, |
| @@ -217,21 +217,6 @@ export default defineSchema({ · 7 unmodified lines | ||
| 217 | 217 | .index("by_workspace_created", ["workspaceId", "createdAt"]) |
| 218 | 218 | .index("by_workspace_status_created", ["workspaceId", "status", "createdAt"]), |
| 219 | 219 | |
| 220 | − pushSubscriptions: defineTable({ | |
| 221 | − workspaceId: v.string(), | |
| 222 | − accountId: v.string(), | |
| 223 | − endpoint: v.string(), | |
| 224 | − p256dh: v.string(), | |
| 225 | − auth: v.string(), | |
| 226 | − expirationTime: v.optional(v.number()), | |
| 227 | − userAgent: v.optional(v.string()), | |
| 228 | − createdAt: v.number(), | |
| 229 | − updatedAt: v.number(), | |
| 230 | − }) | |
| 231 | − .index("by_workspace_created", ["workspaceId", "createdAt"]) | |
| 232 | − .index("by_workspace_account", ["workspaceId", "accountId"]) | |
| 233 | − .index("by_endpoint", ["endpoint"]), | |
| 234 | − | |
| 235 | 220 | taskEvents: defineTable({ |
| 236 | 221 | sequence: v.number(), |
| 237 | 222 | taskId: v.string(), |
| @@ -245,7 +230,7 @@ export default defineSchema({ · 7 unmodified lines | ||
| 245 | 230 | |
| 246 | 231 | accessPolicies: defineTable({ |
| 247 | 232 | policyId: v.string(), |
| 248 | − workspaceId: v.string(), | |
| 233 | + workspaceId: v.id("workspaces"), | |
| 249 | 234 | actorId: v.string(), |
| 250 | 235 | clientId: v.string(), |
| 251 | 236 | toolPathPattern: v.string(), |
| @@ -259,7 +244,7 @@ export default defineSchema({ · 7 unmodified lines | ||
| 259 | 244 | |
| 260 | 245 | sourceCredentials: defineTable({ |
| 261 | 246 | credentialId: v.string(), |
| 262 | − workspaceId: v.string(), | |
| 247 | + workspaceId: v.id("workspaces"), | |
| 263 | 248 | sourceKey: v.string(), |
| 264 | 249 | scope: credentialScope, |
| 265 | 250 | actorId: v.string(), |
| @@ -274,7 +259,7 @@ export default defineSchema({ · 8 unmodified lines | ||
| 274 | 259 | |
| 275 | 260 | toolSources: defineTable({ |
| 276 | 261 | sourceId: v.string(), |
| 277 | − workspaceId: v.string(), | |
| 262 | + workspaceId: v.id("workspaces"), | |
| 278 | 263 | name: v.string(), |
| 279 | 264 | type: toolSourceType, |
| 280 | 265 | config: v.any(), |
| @@ -291,7 +276,7 @@ export default defineSchema({ · 10 unmodified lines | ||
| 291 | 276 | agentTaskId: v.string(), |
| 292 | 277 | prompt: v.string(), |
| 293 | 278 | requesterId: v.string(), |
| 294 | − workspaceId: v.string(), | |
| 279 | + workspaceId: v.id("workspaces"), | |
| 295 | 280 | actorId: v.string(), |
| 296 | 281 | status: agentTaskStatus, |
| 297 | 282 | resultText: v.optional(v.string()), |
| @@ -314,14 +299,14 @@ export default defineSchema({ · 16 unmodified lines | ||
| 314 | 299 | .index("by_spec_url_version", ["specUrl", "version"]), |
| 315 | 300 | |
| 316 | 301 | workspaceToolCache: defineTable({ |
| 317 | − workspaceId: v.string(), | |
| 302 | + workspaceId: v.id("workspaces"), | |
| 318 | 303 | signature: v.string(), |
| 319 | 304 | storageId: v.id("_storage"), |
| 320 | 305 | /** Per-source .d.ts blobs stored separately (too large for action responses). */ |
| 321 | − dtsStorageIds: v.optional(v.array(v.object({ | |
| 306 | + dtsStorageIds: v.array(v.object({ | |
| 322 | 307 | sourceKey: v.string(), |
| 323 | 308 | storageId: v.id("_storage"), |
| 324 | − }))), | |
| 309 | + })), | |
| 325 | 310 | toolCount: v.number(), |
| 326 | 311 | sizeBytes: v.number(), |
| 327 | 312 | createdAt: v.number(), |
| @@ -334,7 +319,7 @@ export default defineSchema({ · 6 unmodified lines | ||
| 334 | 319 | actorId: v.string(), |
| 335 | 320 | clientId: v.string(), |
| 336 | 321 | accountId: v.id("accounts"), |
| 337 | − userId: v.optional(v.id("workspaceMembers")), | |
| 322 | + userId: v.id("workspaceMembers"), | |
| 338 | 323 | createdAt: v.number(), |
| 339 | 324 | lastSeenAt: v.number(), |
| 340 | 325 | }) |
| @@ -12,7 +12,7 @@ function actorIdForAccount(account: { _id: string; provider: string; providerAcc · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | export const getWorkspaceAccessForRequest = internalQuery({ |
| 14 | 14 | args: { |
| 15 | − workspaceId: v.string(), | |
| 15 | + workspaceId: v.id("workspaces"), | |
| 16 | 16 | sessionId: v.optional(v.string()), |
| 17 | 17 | }, |
| 18 | 18 | handler: async (ctx, args) => { |
| @@ -36,7 +36,7 @@ export const getWorkspaceAccessForRequest = internalQuery({ · 17 unmodified lines | ||
| 36 | 36 | |
| 37 | 37 | export const getWorkspaceAccessForWorkosSubject = internalQuery({ |
| 38 | 38 | args: { |
| 39 | − workspaceId: v.string(), | |
| 39 | + workspaceId: v.id("workspaces"), | |
| 40 | 40 | subject: v.string(), |
| 41 | 41 | }, |
| 42 | 42 | handler: async (ctx, args) => { |
| @@ -7,7 +7,7 @@ import { internalMutation, internalQuery } from "./_generated/server"; · 6 unmodified lines | ||
| 7 | 7 | */ |
| 8 | 8 | export const getEntry = internalQuery({ |
| 9 | 9 | args: { |
| 10 | − workspaceId: v.string(), | |
| 10 | + workspaceId: v.id("workspaces"), | |
| 11 | 11 | signature: v.string(), |
| 12 | 12 | }, |
| 13 | 13 | handler: async (ctx, args) => { |
| @@ -21,7 +21,7 @@ export const getEntry = internalQuery({ · 7 unmodified lines | ||
| 21 | 21 | |
| 22 | 22 | return { |
| 23 | 23 | storageId: entry.storageId, |
| 24 | − dtsStorageIds: entry.dtsStorageIds ?? [], | |
| 24 | + dtsStorageIds: entry.dtsStorageIds, | |
| 25 | 25 | toolCount: entry.toolCount, |
| 26 | 26 | sizeBytes: entry.sizeBytes, |
| 27 | 27 | createdAt: entry.createdAt, |
| @@ -35,13 +35,13 @@ export const getEntry = internalQuery({ · 7 unmodified lines | ||
| 35 | 35 | */ |
| 36 | 36 | export const putEntry = internalMutation({ |
| 37 | 37 | args: { |
| 38 | − workspaceId: v.string(), | |
| 38 | + workspaceId: v.id("workspaces"), | |
| 39 | 39 | signature: v.string(), |
| 40 | 40 | storageId: v.id("_storage"), |
| 41 | − dtsStorageIds: v.optional(v.array(v.object({ | |
| 41 | + dtsStorageIds: v.array(v.object({ | |
| 42 | 42 | sourceKey: v.string(), |
| 43 | 43 | storageId: v.id("_storage"), |
| 44 | − }))), | |
| 44 | + })), | |
| 45 | 45 | toolCount: v.number(), |
| 46 | 46 | sizeBytes: v.number(), |
| 47 | 47 | }, |
| @@ -55,10 +55,8 @@ export const putEntry = internalMutation({ · 7 unmodified lines | ||
| 55 | 55 | // Delete old main snapshot blob |
| 56 | 56 | await ctx.storage.delete(existing.storageId).catch(() => {}); |
| 57 | 57 | // Delete old .d.ts blobs |
| 58 | − if (existing.dtsStorageIds) { | |
| 59 | − for (const entry of existing.dtsStorageIds) { | |
| 60 | − await ctx.storage.delete(entry.storageId).catch(() => {}); | |
| 61 | − } | |
| 58 | + for (const entry of existing.dtsStorageIds) { | |
| 59 | + await ctx.storage.delete(entry.storageId).catch(() => {}); | |
| 62 | 60 | } |
| 63 | 61 | await ctx.db.delete(existing._id); |
| 64 | 62 | } |
| @@ -80,7 +78,7 @@ export const putEntry = internalMutation({ · 15 unmodified lines | ||
| 80 | 78 | */ |
| 81 | 79 | export const getDtsStorageIds = internalQuery({ |
| 82 | 80 | args: { |
| 83 | − workspaceId: v.string(), | |
| 81 | + workspaceId: v.id("workspaces"), | |
| 84 | 82 | }, |
| 85 | 83 | handler: async (ctx, args) => { |
| 86 | 84 | const entry = await ctx.db |
| @@ -89,6 +87,6 @@ export const getDtsStorageIds = internalQuery({ · 2 unmodified lines | ||
| 89 | 87 | .unique(); |
| 90 | 88 | |
| 91 | 89 | if (!entry) return []; |
| 92 | − return entry.dtsStorageIds ?? []; | |
| 90 | + return entry.dtsStorageIds; | |
| 93 | 91 | }, |
| 94 | 92 | }); |
| @@ -20,8 +20,6 @@ async function workspaceHasActivity( · 19 unmodified lines | ||
| 20 | 20 | ctx: Pick<QueryCtx, "db"> | Pick<MutationCtx, "db">, |
| 21 | 21 | workspaceId: Id<"workspaces">, |
| 22 | 22 | ): Promise<boolean> { |
| 23 | − const workspaceIdString = String(workspaceId); | |
| 24 | − | |
| 25 | 23 | const [ |
| 26 | 24 | task, |
| 27 | 25 | approval, |
| @@ -31,15 +29,15 @@ async function workspaceHasActivity( · 3 unmodified lines | ||
| 31 | 29 | agentTask, |
| 32 | 30 | member, |
| 33 | 31 | ] = await Promise.all([ |
| 34 | − ctx.db.query("tasks").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceIdString)).first(), | |
| 35 | − ctx.db.query("approvals").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceIdString)).first(), | |
| 36 | − ctx.db.query("accessPolicies").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceIdString)).first(), | |
| 32 | + ctx.db.query("tasks").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)).first(), | |
| 33 | + ctx.db.query("approvals").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)).first(), | |
| 34 | + ctx.db.query("accessPolicies").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)).first(), | |
| 37 | 35 | ctx.db |
| 38 | 36 | .query("sourceCredentials") |
| 39 | − .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceIdString)) | |
| 37 | + .withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)) | |
| 40 | 38 | .first(), |
| 41 | − ctx.db.query("toolSources").withIndex("by_workspace_updated", (q) => q.eq("workspaceId", workspaceIdString)).first(), | |
| 42 | − ctx.db.query("agentTasks").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceIdString)).first(), | |
| 39 | + ctx.db.query("toolSources").withIndex("by_workspace_updated", (q) => q.eq("workspaceId", workspaceId)).first(), | |
| 40 | + ctx.db.query("agentTasks").withIndex("by_workspace_created", (q) => q.eq("workspaceId", workspaceId)).first(), | |
| 43 | 41 | ctx.db.query("workspaceMembers").withIndex("by_workspace", (q) => q.eq("workspaceId", workspaceId)).first(), |
| 44 | 42 | ]); |
| 45 | 43 | |
| @@ -46,15 +46,6 @@ test("WorkOS Vault provider falls back to id key and raw token", async () => { · 45 unmodified lines | ||
| 46 | 46 | expect(payload).toEqual({ token: "ghp_raw_token" }); |
| 47 | 47 | }); |
| 48 | 48 | |
| 49 | −test("WorkOS Vault provider supports legacy inline payload", async () => { | |
| 50 | − const payload = await resolveCredentialPayload({ | |
| 51 | − provider: "workos-vault", | |
| 52 | − secretJson: { token: "legacy-token" }, | |
| 53 | − }); | |
| 54 | − | |
| 55 | − expect(payload).toEqual({ token: "legacy-token" }); | |
| 56 | −}); | |
| 57 | − | |
| 58 | 49 | test("WorkOS Vault provider gives actionable error on missing reference", async () => { |
| 59 | 50 | await expect( |
| 60 | 51 | resolveCredentialPayload({ |
| @@ -124,14 +124,6 @@ async function resolveWorkosVault( · 123 unmodified lines | ||
| 124 | 124 | : ""; |
| 125 | 125 | |
| 126 | 126 | if (!objectId) { |
| 127 | − // Backward compatibility: older rows may have stored credential payload directly | |
| 128 | − // under the encrypted provider before object references were persisted. | |
| 129 | − const { objectId: _objectId, id: _id, apiKey: _apiKey, ...legacyPayload } = config; | |
| 130 | − const normalized = asRecord(legacyPayload); | |
| 131 | − if (Object.keys(normalized).length > 0) { | |
| 132 | − return normalized; | |
| 133 | − } | |
| 134 | − | |
| 135 | 127 | throw new Error( |
| 136 | 128 | "Encrypted credential is missing its secure reference. Re-save this credential in the dashboard.", |
| 137 | 129 | ); |
| @@ -151,7 +143,7 @@ export async function resolveCredentialPayload( · 13 unmodified lines | ||
| 151 | 143 | record: Pick<CredentialRecord, "provider" | "secretJson">, |
| 152 | 144 | options?: { readVaultObject?: VaultObjectReader }, |
| 153 | 145 | ): Promise<CredentialPayload | null> { |
| 154 | − const provider = (record.provider ?? "managed") as CredentialProvider; | |
| 146 | + const provider = record.provider; | |
| 155 | 147 | const resolver = providers[provider]; |
| 156 | 148 | if (!resolver) { |
| 157 | 149 | throw new Error(`Unsupported credential provider: ${provider}`); |
| @@ -138,7 +138,7 @@ export const organizationMutation = customMutation(mutation, { · 137 unmodified lines | ||
| 138 | 138 | |
| 139 | 139 | export const workspaceQuery = customQuery(query, { |
| 140 | 140 | args: { |
| 141 | − workspaceId: v.string(), | |
| 141 | + workspaceId: v.id("workspaces"), | |
| 142 | 142 | sessionId: v.optional(v.string()), |
| 143 | 143 | }, |
| 144 | 144 | input: async (ctx, args, options: WorkspaceAccessOptions = {}) => { |
| @@ -158,7 +158,7 @@ export const workspaceQuery = customQuery(query, { · 13 unmodified lines | ||
| 158 | 158 | |
| 159 | 159 | export const workspaceMutation = customMutation(mutation, { |
| 160 | 160 | args: { |
| 161 | − workspaceId: v.string(), | |
| 161 | + workspaceId: v.id("workspaces"), | |
| 162 | 162 | sessionId: v.optional(v.string()), |
| 163 | 163 | }, |
| 164 | 164 | input: async (ctx, args, options: WorkspaceAccessOptions = {}) => { |
| @@ -100,10 +100,10 @@ export async function getWorkspaceMembership( · 99 unmodified lines | ||
| 100 | 100 | |
| 101 | 101 | export async function requireWorkspaceAccessForAccount( |
| 102 | 102 | ctx: MembershipCtx, |
| 103 | − workspaceId: string, | |
| 103 | + workspaceId: Id<"workspaces">, | |
| 104 | 104 | account: Doc<"accounts">, |
| 105 | 105 | ): Promise<WorkspaceAccess> { |
| 106 | − const workspace = await ctx.db.get(workspaceId as Id<"workspaces">); | |
| 106 | + const workspace = await ctx.db.get(workspaceId); | |
| 107 | 107 | if (!workspace) { |
| 108 | 108 | throw new Error("Workspace not found"); |
| 109 | 109 | } |
| @@ -140,7 +140,7 @@ export async function requireWorkspaceAccessForAccount( · 30 unmodified lines | ||
| 140 | 140 | |
| 141 | 141 | export async function requireWorkspaceAccessForRequest( |
| 142 | 142 | ctx: IdentityCtx, |
| 143 | − workspaceId: string, | |
| 143 | + workspaceId: Id<"workspaces">, | |
| 144 | 144 | sessionId?: string, |
| 145 | 145 | ): Promise<WorkspaceAccess> { |
| 146 | 146 | const account = await resolveAccountForRequest(ctx, sessionId); |
| @@ -78,6 +78,8 @@ class FakeMcpService { · 77 unmodified lines | ||
| 78 | 78 | workspaceId: `ws_${crypto.randomUUID()}`, |
| 79 | 79 | actorId: `anon_${crypto.randomUUID()}`, |
| 80 | 80 | clientId: "mcp", |
| 81 | + accountId: `account_${crypto.randomUUID()}`, | |
| 82 | + userId: `user_${crypto.randomUUID()}`, | |
| 81 | 83 | createdAt: now, |
| 82 | 84 | lastSeenAt: now, |
| 83 | 85 | }; |
| @@ -168,110 +168,7 @@ describe("prepareOpenApiSpec with large specs", () => { · 167 unmodified lines | ||
| 168 | 168 | expect(json.length).toBeGreaterThan(100_000); |
| 169 | 169 | }); |
| 170 | 170 | |
| 171 | − test("preserves operationTypes when spec is large", async () => { | |
| 172 | − const spec = makeLargeSpec(50); | |
| 173 | − const prepared = await prepareOpenApiSpec(spec, "type-test"); | |
| 174 | 171 | |
| 175 | − // openapiTS should generate types for all operations | |
| 176 | − if (prepared.operationTypes) { | |
| 177 | − const opCount = Object.keys(prepared.operationTypes).length; | |
| 178 | − // Should have types for most operations (openapiTS generates from valid specs) | |
| 179 | − expect(opCount).toBeGreaterThan(0); | |
| 180 | − | |
| 181 | − // Spot-check a known operation | |
| 182 | − const getType = prepared.operationTypes["get_resource_0"]; | |
| 183 | − if (getType) { | |
| 184 | − expect(getType.argsType).toBeDefined(); | |
| 185 | − expect(getType.returnsType).toBeDefined(); | |
| 186 | − } | |
| 187 | − } | |
| 188 | − }); | |
| 189 | − | |
| 190 | − test("preserves schemaTypes when spec has shared schemas", async () => { | |
| 191 | − const specWithSchemas: Record<string, unknown> = { | |
| 192 | − openapi: "3.0.3", | |
| 193 | − info: { title: "Schema Test", version: "1.0.0" }, | |
| 194 | − servers: [{ url: "https://api.example.com" }], | |
| 195 | − components: { | |
| 196 | − schemas: { | |
| 197 | − User: { | |
| 198 | − type: "object", | |
| 199 | − properties: { | |
| 200 | − id: { type: "string" }, | |
| 201 | − email: { type: "string" }, | |
| 202 | − name: { type: "string" }, | |
| 203 | − role: { type: "string", enum: ["admin", "user", "viewer"] }, | |
| 204 | − }, | |
| 205 | − required: ["id", "email"], | |
| 206 | − }, | |
| 207 | − UserList: { | |
| 208 | − type: "object", | |
| 209 | − properties: { | |
| 210 | − data: { | |
| 211 | − type: "array", | |
| 212 | − items: { $ref: "#/components/schemas/User" }, | |
| 213 | − }, | |
| 214 | − total: { type: "number" }, | |
| 215 | − }, | |
| 216 | − }, | |
| 217 | − }, | |
| 218 | − }, | |
| 219 | − paths: { | |
| 220 | − "/users": { | |
| 221 | − get: { | |
| 222 | − operationId: "listUsers", | |
| 223 | − tags: ["users"], | |
| 224 | − summary: "List users", | |
| 225 | − responses: { | |
| 226 | − "200": { | |
| 227 | − description: "ok", | |
| 228 | − content: { | |
| 229 | − "application/json": { | |
| 230 | − schema: { $ref: "#/components/schemas/UserList" }, | |
| 231 | − }, | |
| 232 | − }, | |
| 233 | − }, | |
| 234 | − }, | |
| 235 | − }, | |
| 236 | − }, | |
| 237 | − "/users/{id}": { | |
| 238 | − get: { | |
| 239 | − operationId: "getUser", | |
| 240 | − tags: ["users"], | |
| 241 | − parameters: [ | |
| 242 | − { | |
| 243 | − name: "id", | |
| 244 | − in: "path", | |
| 245 | − required: true, | |
| 246 | − schema: { type: "string" }, | |
| 247 | − }, | |
| 248 | − ], | |
| 249 | − summary: "Get user", | |
| 250 | − responses: { | |
| 251 | − "200": { | |
| 252 | − description: "ok", | |
| 253 | − content: { | |
| 254 | − "application/json": { | |
| 255 | − schema: { $ref: "#/components/schemas/User" }, | |
| 256 | − }, | |
| 257 | − }, | |
| 258 | − }, | |
| 259 | − }, | |
| 260 | − }, | |
| 261 | − }, | |
| 262 | − }, | |
| 263 | − }; | |
| 264 | − | |
| 265 | − const prepared = await prepareOpenApiSpec(specWithSchemas, "schema-test"); | |
| 266 | − | |
| 267 | − // Should have generated schema types | |
| 268 | − if (prepared.schemaTypes) { | |
| 269 | − expect(Object.keys(prepared.schemaTypes).length).toBeGreaterThan(0); | |
| 270 | − } | |
| 271 | − | |
| 272 | − // No warnings | |
| 273 | − expect(prepared.warnings).toHaveLength(0); | |
| 274 | − }); | |
| 275 | 172 | }); |
| 276 | 173 | |
| 277 | 174 | describe("buildOpenApiToolsFromPrepared", () => { |
| @@ -946,78 +843,7 @@ describe("buildOpenApiToolsFromPrepared", () => { · 668 unmodified lines | ||
| 946 | 843 | expect(tool!.metadata?.returnsType).not.toBe("unknown"); |
| 947 | 844 | }); |
| 948 | 845 | |
| 949 | − test("schemaTypes only attached to first tool from a source", async () => { | |
| 950 | − const specWithSchemas: Record<string, unknown> = { | |
| 951 | − openapi: "3.0.3", | |
| 952 | − info: { title: "Schema placement test", version: "1.0.0" }, | |
| 953 | − servers: [{ url: "https://api.example.com" }], | |
| 954 | − components: { | |
| 955 | − schemas: { | |
| 956 | − Widget: { | |
| 957 | − type: "object", | |
| 958 | − properties: { | |
| 959 | − id: { type: "string" }, | |
| 960 | − name: { type: "string" }, | |
| 961 | − }, | |
| 962 | − }, | |
| 963 | − }, | |
| 964 | − }, | |
| 965 | − paths: { | |
| 966 | − "/widgets": { | |
| 967 | − get: { | |
| 968 | − operationId: "listWidgets", | |
| 969 | − tags: ["widgets"], | |
| 970 | − responses: { | |
| 971 | − "200": { | |
| 972 | − description: "ok", | |
| 973 | − content: { | |
| 974 | − "application/json": { | |
| 975 | − schema: { | |
| 976 | − type: "array", | |
| 977 | − items: { $ref: "#/components/schemas/Widget" }, | |
| 978 | − }, | |
| 979 | − }, | |
| 980 | − }, | |
| 981 | − }, | |
| 982 | − }, | |
| 983 | − }, | |
| 984 | − post: { | |
| 985 | − operationId: "createWidget", | |
| 986 | − tags: ["widgets"], | |
| 987 | − requestBody: { | |
| 988 | − content: { | |
| 989 | − "application/json": { | |
| 990 | − schema: { $ref: "#/components/schemas/Widget" }, | |
| 991 | − }, | |
| 992 | − }, | |
| 993 | − }, | |
| 994 | − responses: { | |
| 995 | − "201": { description: "created" }, | |
| 996 | − }, | |
| 997 | − }, | |
| 998 | − }, | |
| 999 | − }, | |
| 1000 | − }; | |
| 1001 | 846 | |
| 1002 | − const prepared = await prepareOpenApiSpec(specWithSchemas, "schema-placement"); | |
| 1003 | − const tools = buildOpenApiToolsFromPrepared( | |
| 1004 | − { | |
| 1005 | − type: "openapi", | |
| 1006 | − name: "widgets", | |
| 1007 | − spec: specWithSchemas, | |
| 1008 | − baseUrl: "https://api.example.com", | |
| 1009 | − }, | |
| 1010 | − prepared, | |
| 1011 | − ); | |
| 1012 | − | |
| 1013 | − expect(tools.length).toBe(2); | |
| 1014 | − | |
| 1015 | − // Only the first tool should have schemaTypes | |
| 1016 | − const withSchemas = tools.filter( | |
| 1017 | − (t) => t.metadata?.schemaTypes && Object.keys(t.metadata.schemaTypes).length > 0, | |
| 1018 | − ); | |
| 1019 | − expect(withSchemas.length).toBeLessThanOrEqual(1); | |
| 1020 | − }); | |
| 1021 | 847 | }); |
| 1022 | 848 | |
| 1023 | 849 | describe("prepared spec serialization round-trip", () => { |
| @@ -1035,20 +861,6 @@ describe("prepared spec serialization round-trip", () => { · 11 unmodified lines | ||
| 1035 | 861 | ); |
| 1036 | 862 | expect(restored.warnings).toEqual(prepared.warnings); |
| 1037 | 863 | |
| 1038 | − if (prepared.operationTypes) { | |
| 1039 | − expect(restored.operationTypes).toBeDefined(); | |
| 1040 | − expect(Object.keys(restored.operationTypes!).length).toBe( | |
| 1041 | − Object.keys(prepared.operationTypes).length, | |
| 1042 | − ); | |
| 1043 | − } | |
| 1044 | − | |
| 1045 | − if (prepared.schemaTypes) { | |
| 1046 | − expect(restored.schemaTypes).toBeDefined(); | |
| 1047 | − expect(Object.keys(restored.schemaTypes!).length).toBe( | |
| 1048 | − Object.keys(prepared.schemaTypes).length, | |
| 1049 | − ); | |
| 1050 | − } | |
| 1051 | − | |
| 1052 | 864 | // Verify tools built from the restored spec match the original |
| 1053 | 865 | const config = { |
| 1054 | 866 | type: "openapi" as const, |
| @@ -852,10 +852,6 @@ export interface PreparedOpenApiSpec { · 851 unmodified lines | ||
| 852 | 852 | paths: Record<string, unknown>; |
| 853 | 853 | /** Raw .d.ts from openapiTS — cached as-is, extracted lazily on read */ |
| 854 | 854 | dts?: string; |
| 855 | − /** @deprecated Kept for cache compat — new entries use `dts` instead */ | |
| 856 | − operationTypes?: Record<string, { argsType: string; returnsType: string }>; | |
| 857 | − /** @deprecated Kept for cache compat — new entries use `dts` instead */ | |
| 858 | − schemaTypes?: Record<string, string>; | |
| 859 | 855 | warnings: string[]; |
| 860 | 856 | } |
| 861 | 857 | |
| @@ -941,7 +937,7 @@ export function buildOpenApiToolsFromPrepared( · 79 unmodified lines | ||
| 941 | 937 | const paths = asRecord(prepared.paths); |
| 942 | 938 | const tools: ToolDefinition[] = []; |
| 943 | 939 | |
| 944 | − // The raw .d.ts is attached to the first tool only (same pattern as old schemaTypes). | |
| 940 | + // The raw .d.ts is attached to the first tool only (one per source to avoid duplication). | |
| 945 | 941 | // The typechecker/Monaco use this directly via indexed access types. |
| 946 | 942 | const sourceDts = prepared.dts |
| 947 | 943 | ? prepared.dts.replace(/^export /gm, "") // strip 'export' so types are ambient |
| @@ -1739,7 +1735,6 @@ export interface SerializedTool { · 791 unmodified lines | ||
| 1739 | 1735 | * MCP: { kind: "mcp", url, transport?, queryParams?, toolName } |
| 1740 | 1736 | * GraphQL raw: { kind: "graphql_raw", endpoint, authHeaders } |
| 1741 | 1737 | * GraphQL field: { kind: "graphql_field", endpoint, operationName, operationType, queryTemplate, argNames?, authHeaders } |
| 1742 | − * GraphQL (legacy compat): { kind: "graphql", endpoint, operationName, operationType, authHeaders } | |
| 1743 | 1738 | * Builtin: { kind: "builtin" } — run comes from DEFAULT_TOOLS |
| 1744 | 1739 | */ |
| 1745 | 1740 | runSpec: |
| @@ -1772,13 +1767,6 @@ export interface SerializedTool { · 26 unmodified lines | ||
| 1772 | 1767 | argNames?: string[]; |
| 1773 | 1768 | authHeaders: Record<string, string>; |
| 1774 | 1769 | } |
| 1775 | − | { | |
| 1776 | − kind: "graphql"; | |
| 1777 | − endpoint: string; | |
| 1778 | − operationName: string; | |
| 1779 | − operationType: "query" | "mutation"; | |
| 1780 | − authHeaders: Record<string, string>; | |
| 1781 | − } | |
| 1782 | 1770 | | { kind: "builtin" }; |
| 1783 | 1771 | } |
| 1784 | 1772 | |
| @@ -1976,21 +1964,6 @@ export function rehydrateTools( · 191 unmodified lines | ||
| 1976 | 1964 | }; |
| 1977 | 1965 | } |
| 1978 | 1966 | |
| 1979 | − if (st.runSpec.kind === "graphql") { | |
| 1980 | − // Legacy compat for older cached entries. | |
| 1981 | − const { endpoint, operationName, operationType, authHeaders } = st.runSpec; | |
| 1982 | − const queryTemplate = `${operationType} ${operationName}($input: JSON) { ${operationName}(input: $input) }`; | |
| 1983 | − return { | |
| 1984 | − ...base, | |
| 1985 | − run: async (input: unknown, context) => { | |
| 1986 | − const payload = asRecord(input); | |
| 1987 | − const variables = payload.variables ?? { input: payload }; | |
| 1988 | − const envelope = await executeGraphql(endpoint, authHeaders, queryTemplate, variables, context); | |
| 1989 | − return selectGraphqlFieldEnvelope(envelope, operationName); | |
| 1990 | − }, | |
| 1991 | − }; | |
| 1992 | − } | |
| 1993 | − | |
| 1994 | 1967 | return { ...base, run: async () => { throw new Error(`Unknown run spec kind for '${st.path}'`); } }; |
| 1995 | 1968 | }); |
| 1996 | 1969 | } |
| @@ -424,97 +424,4 @@ export interface operations { · 423 unmodified lines | ||
| 424 | 424 | }); |
| 425 | 425 | }); |
| 426 | 426 | |
| 427 | −// --------------------------------------------------------------------------- | |
| 428 | −// schemaTypes integration — verifies that schema type aliases are emitted and | |
| 429 | −// correctly used by the typechecker | |
| 430 | −// --------------------------------------------------------------------------- | |
| 431 | − | |
| 432 | −describe("typecheckCode with schemaTypes", () => { | |
| 433 | − const CUSTOMER_SCHEMA_TOOL: ToolDescriptor = { | |
| 434 | − path: "stripe.customers.create", | |
| 435 | − description: "Create a customer", | |
| 436 | − approval: "auto", | |
| 437 | − argsType: "{ name: string; email: string }", | |
| 438 | − returnsType: "Customer", | |
| 439 | − // Only the first tool from a source carries schemaTypes | |
| 440 | − schemaTypes: { | |
| 441 | − Customer: "{ id: string; name: string; email: string; subscriptions: Subscription[] }", | |
| 442 | − Subscription: "{ id: string; status: string; plan: Plan }", | |
| 443 | − Plan: "{ id: string; amount: number; currency: string }", | |
| 444 | − }, | |
| 445 | − }; | |
| 446 | − | |
| 447 | − const CUSTOMER_GET_TOOL: ToolDescriptor = { | |
| 448 | − path: "stripe.customers.get", | |
| 449 | − description: "Get a customer", | |
| 450 | − approval: "auto", | |
| 451 | − argsType: "{ id: string }", | |
| 452 | − returnsType: "Customer", | |
| 453 | − // Subsequent tools do NOT carry schemaTypes (deduplication) | |
| 454 | − }; | |
| 455 | − | |
| 456 | − const STRIPE_TOOLS = [CUSTOMER_SCHEMA_TOOL, CUSTOMER_GET_TOOL]; | |
| 457 | − | |
| 458 | − test("schema types are emitted in declarations", () => { | |
| 459 | − const result = generateToolDeclarations(STRIPE_TOOLS); | |
| 460 | − expect(result).toContain("type Customer ="); | |
| 461 | − expect(result).toContain("type Subscription ="); | |
| 462 | − expect(result).toContain("type Plan ="); | |
| 463 | − // Declarations should come before the tools declaration | |
| 464 | − const schemaIdx = result.indexOf("type Customer ="); | |
| 465 | − const toolsIdx = result.indexOf("declare const tools:"); | |
| 466 | − expect(schemaIdx).toBeLessThan(toolsIdx); | |
| 467 | − }); | |
| 468 | − | |
| 469 | − test("code using schema return types typechecks correctly", () => { | |
| 470 | − const decl = generateToolDeclarations(STRIPE_TOOLS); | |
| 471 | − const result = typecheckCode( | |
| 472 | − `const customer = await tools.stripe.customers.create({ name: "John", email: "john@test.com" }); | |
| 473 | − const id: string = customer.id; | |
| 474 | − const subs: Subscription[] = customer.subscriptions; | |
| 475 | − const plan: Plan = subs[0].plan; | |
| 476 | − return plan.amount;`, | |
| 477 | − decl, | |
| 478 | − ); | |
| 479 | − expect(result.ok).toBe(true); | |
| 480 | − expect(result.errors).toHaveLength(0); | |
| 481 | − }); | |
| 482 | − | |
| 483 | − test("wrong property access on schema type produces errors", () => { | |
| 484 | − const decl = generateToolDeclarations(STRIPE_TOOLS); | |
| 485 | − const result = typecheckCode( | |
| 486 | − `const customer = await tools.stripe.customers.get({ id: "cus_123" }); | |
| 487 | − return customer.nonexistent_field;`, | |
| 488 | − decl, | |
| 489 | − ); | |
| 490 | − expect(result.ok).toBe(false); | |
| 491 | − expect(result.errors.some((e) => e.includes("nonexistent_field"))).toBe(true); | |
| 492 | − }); | |
| 493 | 427 | |
| 494 | − test("transitive schema types are available (Subscription, Plan)", () => { | |
| 495 | − const decl = generateToolDeclarations(STRIPE_TOOLS); | |
| 496 | − const result = typecheckCode( | |
| 497 | − `const customer = await tools.stripe.customers.get({ id: "cus_123" }); | |
| 498 | − const sub = customer.subscriptions[0]; | |
| 499 | − const status: string = sub.status; | |
| 500 | − const amount: number = sub.plan.amount; | |
| 501 | − const currency: string = sub.plan.currency; | |
| 502 | − return { status, amount, currency };`, | |
| 503 | − decl, | |
| 504 | − ); | |
| 505 | − expect(result.ok).toBe(true); | |
| 506 | − expect(result.errors).toHaveLength(0); | |
| 507 | − }); | |
| 508 | − | |
| 509 | − test("wrong type on schema property produces errors", () => { | |
| 510 | − const decl = generateToolDeclarations(STRIPE_TOOLS); | |
| 511 | − const result = typecheckCode( | |
| 512 | − `const customer = await tools.stripe.customers.get({ id: "cus_123" }); | |
| 513 | − const amount: string = customer.subscriptions[0].plan.amount;`, | |
| 514 | − decl, | |
| 515 | − ); | |
| 516 | − expect(result.ok).toBe(false); | |
| 517 | − // amount is number, assigning to string should fail | |
| 518 | − expect(result.errors.some((e) => e.includes("number") || e.includes("string"))).toBe(true); | |
| 519 | − }); | |
| 520 | −}); | |
| @@ -104,18 +104,6 @@ export function generateToolDeclarations( · 103 unmodified lines | ||
| 104 | 104 | options?: GenerateToolDeclarationOptions, |
| 105 | 105 | ): string { |
| 106 | 106 | |
| 107 | − // Legacy compat: collect schemaTypes from tools that use the old format | |
| 108 | − const allSchemas = new Map<string, string>(); | |
| 109 | − for (const tool of tools) { | |
| 110 | − if (tool.schemaTypes) { | |
| 111 | − for (const [name, type] of Object.entries(tool.schemaTypes)) { | |
| 112 | − if (!allSchemas.has(name)) { | |
| 113 | − allSchemas.set(name, type); | |
| 114 | − } | |
| 115 | − } | |
| 116 | − } | |
| 117 | − } | |
| 118 | − | |
| 119 | 107 | // Build a nested tree from flat tool paths |
| 120 | 108 | interface TreeNode { |
| 121 | 109 | children: Map<string, TreeNode>; |
| @@ -172,13 +160,6 @@ export function generateToolDeclarations( · 50 unmodified lines | ||
| 172 | 160 | // Assemble the full declarations block |
| 173 | 161 | const parts: string[] = []; |
| 174 | 162 | |
| 175 | − // Legacy schema type aliases (from old cache format) | |
| 176 | − if (allSchemas.size > 0) { | |
| 177 | − for (const [name, type] of allSchemas) { | |
| 178 | − parts.push(`type ${name} = ${type};`); | |
| 179 | − } | |
| 180 | − } | |
| 181 | − | |
| 182 | 163 | const sourceDtsBySource = options?.sourceDtsBySource ?? {}; |
| 183 | 164 | const dtsEntries = Object.entries(sourceDtsBySource) |
| 184 | 165 | .filter(([, dts]) => typeof dts === "string" && dts.trim().length > 0) |
| @@ -1,25 +1,125 @@ | ||
| 1 | −// Re-export shared types from contracts | |
| 2 | −export type { | |
| 3 | − TaskStatus, | |
| 4 | − ApprovalStatus, | |
| 5 | − PolicyDecision, | |
| 6 | − CredentialScope, | |
| 7 | − CredentialProvider, | |
| 8 | − ToolApprovalMode, | |
| 9 | − ToolSourceType, | |
| 10 | − TaskRecord, | |
| 11 | − ApprovalRecord, | |
| 12 | − PendingApprovalRecord, | |
| 13 | − TaskEventRecord, | |
| 14 | − AccessPolicyRecord, | |
| 15 | − CredentialRecord, | |
| 16 | − ToolSourceRecord, | |
| 17 | − ToolDescriptor, | |
| 18 | − OpenApiSourceQuality, | |
| 19 | − AnonymousContext, | |
| 20 | −} from "@executor/contracts"; | |
| 21 | − | |
| 22 | −import type { TaskStatus, CredentialScope, ToolApprovalMode } from "@executor/contracts"; | |
| 1 | +// ── Shared types (inlined from @executor/contracts) ────────────────────────── | |
| 2 | + | |
| 3 | +export type TaskStatus = "queued" | "running" | "completed" | "failed" | "timed_out" | "denied"; | |
| 4 | +export type ApprovalStatus = "pending" | "approved" | "denied"; | |
| 5 | +export type PolicyDecision = "allow" | "require_approval" | "deny"; | |
| 6 | +export type CredentialScope = "workspace" | "actor"; | |
| 7 | +export type CredentialProvider = "managed" | "workos-vault"; | |
| 8 | +export type ToolApprovalMode = "auto" | "required"; | |
| 9 | +export type ToolSourceType = "mcp" | "openapi" | "graphql"; | |
| 10 | + | |
| 11 | +export interface TaskRecord { | |
| 12 | + id: string; | |
| 13 | + code: string; | |
| 14 | + runtimeId: string; | |
| 15 | + status: TaskStatus; | |
| 16 | + timeoutMs: number; | |
| 17 | + metadata: Record<string, unknown>; | |
| 18 | + workspaceId: string; | |
| 19 | + actorId?: string; | |
| 20 | + clientId?: string; | |
| 21 | + createdAt: number; | |
| 22 | + updatedAt: number; | |
| 23 | + startedAt?: number; | |
| 24 | + completedAt?: number; | |
| 25 | + error?: string; | |
| 26 | + stdout?: string; | |
| 27 | + stderr?: string; | |
| 28 | + exitCode?: number; | |
| 29 | +} | |
| 30 | + | |
| 31 | +export interface ApprovalRecord { | |
| 32 | + id: string; | |
| 33 | + taskId: string; | |
| 34 | + toolPath: string; | |
| 35 | + input: unknown; | |
| 36 | + status: ApprovalStatus; | |
| 37 | + reason?: string; | |
| 38 | + reviewerId?: string; | |
| 39 | + createdAt: number; | |
| 40 | + resolvedAt?: number; | |
| 41 | +} | |
| 42 | + | |
| 43 | +export interface PendingApprovalRecord extends ApprovalRecord { | |
| 44 | + task: Pick<TaskRecord, "id" | "status" | "runtimeId" | "timeoutMs" | "createdAt">; | |
| 45 | +} | |
| 46 | + | |
| 47 | +export interface TaskEventRecord { | |
| 48 | + id: number; | |
| 49 | + taskId: string; | |
| 50 | + eventName: string; | |
| 51 | + type: string; | |
| 52 | + payload: unknown; | |
| 53 | + createdAt: number; | |
| 54 | +} | |
| 55 | + | |
| 56 | +export interface AccessPolicyRecord { | |
| 57 | + id: string; | |
| 58 | + workspaceId: string; | |
| 59 | + actorId?: string; | |
| 60 | + clientId?: string; | |
| 61 | + toolPathPattern: string; | |
| 62 | + decision: PolicyDecision; | |
| 63 | + priority: number; | |
| 64 | + createdAt: number; | |
| 65 | + updatedAt: number; | |
| 66 | +} | |
| 67 | + | |
| 68 | +export interface CredentialRecord { | |
| 69 | + id: string; | |
| 70 | + workspaceId: string; | |
| 71 | + sourceKey: string; | |
| 72 | + scope: CredentialScope; | |
| 73 | + actorId?: string; | |
| 74 | + provider: CredentialProvider; | |
| 75 | + secretJson: Record<string, unknown>; | |
| 76 | + createdAt: number; | |
| 77 | + updatedAt: number; | |
| 78 | +} | |
| 79 | + | |
| 80 | +export interface ToolSourceRecord { | |
| 81 | + id: string; | |
| 82 | + workspaceId: string; | |
| 83 | + name: string; | |
| 84 | + type: ToolSourceType; | |
| 85 | + config: Record<string, unknown>; | |
| 86 | + enabled: boolean; | |
| 87 | + createdAt: number; | |
| 88 | + updatedAt: number; | |
| 89 | +} | |
| 90 | + | |
| 91 | +export interface ToolDescriptor { | |
| 92 | + path: string; | |
| 93 | + description: string; | |
| 94 | + approval: ToolApprovalMode; | |
| 95 | + source?: string; | |
| 96 | + argsType?: string; | |
| 97 | + returnsType?: string; | |
| 98 | + operationId?: string; | |
| 99 | +} | |
| 100 | + | |
| 101 | +export interface OpenApiSourceQuality { | |
| 102 | + sourceKey: string; | |
| 103 | + toolCount: number; | |
| 104 | + unknownArgsCount: number; | |
| 105 | + unknownReturnsCount: number; | |
| 106 | + partialUnknownArgsCount: number; | |
| 107 | + partialUnknownReturnsCount: number; | |
| 108 | + argsQuality: number; | |
| 109 | + returnsQuality: number; | |
| 110 | + overallQuality: number; | |
| 111 | +} | |
| 112 | + | |
| 113 | +export interface AnonymousContext { | |
| 114 | + sessionId: string; | |
| 115 | + workspaceId: string; | |
| 116 | + actorId: string; | |
| 117 | + clientId: string; | |
| 118 | + accountId: string; | |
| 119 | + userId: string; | |
| 120 | + createdAt: number; | |
| 121 | + lastSeenAt: number; | |
| 122 | +} | |
| 23 | 123 | |
| 24 | 124 | // ── Server-only types ───────────────────────────────────────────────────────── |
| 25 | 125 | |
| @@ -120,8 +220,6 @@ export interface ToolTypeMetadata { · 94 unmodified lines | ||
| 120 | 220 | * Only set on the FIRST tool per source to avoid duplication. |
| 121 | 221 | */ |
| 122 | 222 | sourceDts?: string; |
| 123 | − /** @deprecated Use sourceDts instead. Kept for cache compat. */ | |
| 124 | − schemaTypes?: Record<string, string>; | |
| 125 | 223 | } |
| 126 | 224 | |
| 127 | 225 | export interface ToolDefinition { |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | * round-trip with functional `run` methods. |
| 6 | 6 | */ |
| 7 | 7 | import { test, expect, describe } from "bun:test"; |
| 8 | +import type { Id } from "../convex/_generated/dataModel"; | |
| 8 | 9 | import { |
| 9 | 10 | prepareOpenApiSpec, |
| 10 | 11 | buildOpenApiToolsFromPrepared, |
| @@ -464,8 +465,7 @@ describe("serializeTools + rehydrateTools round-trip", () => { · 453 unmodified lines | ||
| 464 | 465 | }); |
| 465 | 466 | |
| 466 | 467 | describe("workspace tool cache table", () => { |
| 467 | − // convex-test based tests for the table operations | |
| 468 | − test("getEntry + putEntry round-trip", async () => { | |
| 468 | + async function setupCacheTest() { | |
| 469 | 469 | const { convexTest } = await import("convex-test"); |
| 470 | 470 | const { internal } = await import("../convex/_generated/api"); |
| 471 | 471 | const schema = (await import("../convex/schema")).default; |
| @@ -475,9 +475,33 @@ describe("workspace tool cache table", () => { · 3 unmodified lines | ||
| 475 | 475 | "./_generated/api.js": () => import("../convex/_generated/api.js"), |
| 476 | 476 | }); |
| 477 | 477 | |
| 478 | + const wsId = await t.run(async (ctx) => { | |
| 479 | + const orgId = await ctx.db.insert("organizations", { | |
| 480 | + name: "test-org", | |
| 481 | + slug: "test-org", | |
| 482 | + status: "active" as const, | |
| 483 | + createdAt: Date.now(), | |
| 484 | + updatedAt: Date.now(), | |
| 485 | + }); | |
| 486 | + return await ctx.db.insert("workspaces", { | |
| 487 | + name: "test-ws", | |
| 488 | + slug: "test-ws", | |
| 489 | + organizationId: orgId, | |
| 490 | + plan: "free", | |
| 491 | + createdAt: Date.now(), | |
| 492 | + updatedAt: Date.now(), | |
| 493 | + }); | |
| 494 | + }); | |
| 495 | + | |
| 496 | + return { t, internal, wsId }; | |
| 497 | + } | |
| 498 | + | |
| 499 | + test("getEntry + putEntry round-trip", async () => { | |
| 500 | + const { t, internal, wsId } = await setupCacheTest(); | |
| 501 | + | |
| 478 | 502 | // Empty cache |
| 479 | 503 | const miss = await t.query(internal.workspaceToolCache.getEntry, { |
| 480 | − workspaceId: "ws_1", | |
| 504 | + workspaceId: wsId, | |
| 481 | 505 | signature: "sig_1", |
| 482 | 506 | }); |
| 483 | 507 | expect(miss).toBeNull(); |
| @@ -489,16 +513,17 @@ describe("workspace tool cache table", () => { · 5 unmodified lines | ||
| 489 | 513 | }); |
| 490 | 514 | |
| 491 | 515 | await t.mutation(internal.workspaceToolCache.putEntry, { |
| 492 | − workspaceId: "ws_1", | |
| 516 | + workspaceId: wsId, | |
| 493 | 517 | signature: "sig_1", |
| 494 | 518 | storageId, |
| 495 | 519 | toolCount: 0, |
| 496 | 520 | sizeBytes: 27, |
| 521 | + dtsStorageIds: [], | |
| 497 | 522 | }); |
| 498 | 523 | |
| 499 | 524 | // Hit |
| 500 | 525 | const hit = await t.query(internal.workspaceToolCache.getEntry, { |
| 501 | − workspaceId: "ws_1", | |
| 526 | + workspaceId: wsId, | |
| 502 | 527 | signature: "sig_1", |
| 503 | 528 | }); |
| 504 | 529 | expect(hit).not.toBeNull(); |
| @@ -506,32 +531,26 @@ describe("workspace tool cache table", () => { · 1 unmodified line | ||
| 506 | 531 | |
| 507 | 532 | // Wrong signature = miss |
| 508 | 533 | const wrongSig = await t.query(internal.workspaceToolCache.getEntry, { |
| 509 | − workspaceId: "ws_1", | |
| 534 | + workspaceId: wsId, | |
| 510 | 535 | signature: "sig_2", |
| 511 | 536 | }); |
| 512 | 537 | expect(wrongSig).toBeNull(); |
| 513 | 538 | }); |
| 514 | 539 | |
| 515 | 540 | test("putEntry replaces old entry and deletes old blob", async () => { |
| 516 | − const { convexTest } = await import("convex-test"); | |
| 517 | − const { internal } = await import("../convex/_generated/api"); | |
| 518 | − const schema = (await import("../convex/schema")).default; | |
| 519 | − | |
| 520 | − const t = convexTest(schema, { | |
| 521 | − "./workspaceToolCache.ts": () => import("../convex/workspaceToolCache"), | |
| 522 | − "./_generated/api.js": () => import("../convex/_generated/api.js"), | |
| 523 | − }); | |
| 541 | + const { t, internal, wsId } = await setupCacheTest(); | |
| 524 | 542 | |
| 525 | 543 | const storageId1 = await t.run(async (ctx) => { |
| 526 | 544 | return await ctx.storage.store(new Blob(["old"])); |
| 527 | 545 | }); |
| 528 | 546 | |
| 529 | 547 | await t.mutation(internal.workspaceToolCache.putEntry, { |
| 530 | − workspaceId: "ws_1", | |
| 548 | + workspaceId: wsId, | |
| 531 | 549 | signature: "sig_1", |
| 532 | 550 | storageId: storageId1, |
| 533 | 551 | toolCount: 5, |
| 534 | 552 | sizeBytes: 3, |
| 553 | + dtsStorageIds: [], | |
| 535 | 554 | }); |
| 536 | 555 | |
| 537 | 556 | const storageId2 = await t.run(async (ctx) => { |
| @@ -539,16 +558,17 @@ describe("workspace tool cache table", () => { · 1 unmodified line | ||
| 539 | 558 | }); |
| 540 | 559 | |
| 541 | 560 | await t.mutation(internal.workspaceToolCache.putEntry, { |
| 542 | − workspaceId: "ws_1", | |
| 561 | + workspaceId: wsId, | |
| 543 | 562 | signature: "sig_2", |
| 544 | 563 | storageId: storageId2, |
| 545 | 564 | toolCount: 10, |
| 546 | 565 | sizeBytes: 3, |
| 566 | + dtsStorageIds: [], | |
| 547 | 567 | }); |
| 548 | 568 | |
| 549 | 569 | // New entry |
| 550 | 570 | const entry = await t.query(internal.workspaceToolCache.getEntry, { |
| 551 | − workspaceId: "ws_1", | |
| 571 | + workspaceId: wsId, | |
| 552 | 572 | signature: "sig_2", |
| 553 | 573 | }); |
| 554 | 574 | expect(entry!.storageId).toBe(storageId2); |
| @@ -3,6 +3,7 @@ import { createRemoteJWKSet, jwtVerify } from "jose"; · 2 unmodified lines | ||
| 3 | 3 | import { api } from "./convex/_generated/api"; |
| 4 | 4 | import { handleMcpRequest, type McpWorkspaceContext } from "./lib/mcp_server"; |
| 5 | 5 | import type { AnonymousContext, PendingApprovalRecord, TaskRecord, ToolDescriptor } from "./lib/types"; |
| 6 | +import type { Id } from "./convex/_generated/dataModel"; | |
| 6 | 7 | |
| 7 | 8 | const convexUrlFromEnv = Bun.env.CONVEX_URL; |
| 8 | 9 | if (!convexUrlFromEnv) { |
| @@ -126,7 +127,7 @@ function createService(context?: McpWorkspaceContext, bearerToken?: string) { · 117 unmodified lines | ||
| 126 | 127 | clientId?: string; |
| 127 | 128 | }) => { |
| 128 | 129 | const created = await convex.mutation(api.executor.createTask, { |
| 129 | − workspaceId: input.workspaceId, | |
| 130 | + workspaceId: input.workspaceId as Id<"workspaces">, | |
| 130 | 131 | sessionId, |
| 131 | 132 | code: input.code, |
| 132 | 133 | timeoutMs: input.timeoutMs, |
| @@ -144,7 +145,7 @@ function createService(context?: McpWorkspaceContext, bearerToken?: string) { · 11 unmodified lines | ||
| 144 | 145 | return null; |
| 145 | 146 | } |
| 146 | 147 | const task = await convex.query(api.workspace.getTaskInWorkspace, { |
| 147 | − workspaceId: effectiveWorkspaceId, | |
| 148 | + workspaceId: effectiveWorkspaceId as Id<"workspaces">, | |
| 148 | 149 | sessionId, |
| 149 | 150 | taskId, |
| 150 | 151 | }); |
| @@ -165,7 +166,7 @@ function createService(context?: McpWorkspaceContext, bearerToken?: string) { · 14 unmodified lines | ||
| 165 | 166 | return []; |
| 166 | 167 | } |
| 167 | 168 | const tools = await convex.action(api.executorNode.listTools, { |
| 168 | − workspaceId: toolContext.workspaceId, | |
| 169 | + workspaceId: toolContext.workspaceId as Id<"workspaces">, | |
| 169 | 170 | sessionId, |
| 170 | 171 | actorId: toolContext.actorId, |
| 171 | 172 | clientId: toolContext.clientId, |
| @@ -175,7 +176,7 @@ function createService(context?: McpWorkspaceContext, bearerToken?: string) { · 3 unmodified lines | ||
| 175 | 176 | |
| 176 | 177 | listPendingApprovals: async (approvalWorkspaceId: string) => { |
| 177 | 178 | const approvals = await convex.query(api.workspace.listPendingApprovals, { |
| 178 | − workspaceId: approvalWorkspaceId, | |
| 179 | + workspaceId: approvalWorkspaceId as Id<"workspaces">, | |
| 179 | 180 | sessionId, |
| 180 | 181 | }); |
| 181 | 182 | return approvals as PendingApprovalRecord[]; |
| @@ -189,7 +190,7 @@ function createService(context?: McpWorkspaceContext, bearerToken?: string) { · 7 unmodified lines | ||
| 189 | 190 | reason?: string; |
| 190 | 191 | }) => { |
| 191 | 192 | return await convex.mutation(api.executor.resolveApproval, { |
| 192 | − workspaceId: input.workspaceId, | |
| 193 | + workspaceId: input.workspaceId as Id<"workspaces">, | |
| 193 | 194 | sessionId, |
| 194 | 195 | approvalId: input.approvalId, |
| 195 | 196 | decision: input.decision, |
| @@ -232,7 +233,7 @@ async function resolveContext( · 36 unmodified lines | ||
| 232 | 233 | } |
| 233 | 234 | |
| 234 | 235 | const requestContext = await convex.query(api.workspace.getRequestContext, { |
| 235 | − workspaceId: requested.workspaceId, | |
| 236 | + workspaceId: requested.workspaceId as Id<"workspaces">, | |
| 236 | 237 | sessionId: requested.sessionId, |
| 237 | 238 | }); |
| 238 | 239 | |
| @@ -1,7 +0,0 @@ | ||
| 1 | −{ | |
| 2 | − "name": "@executor/contracts", | |
| 3 | − "version": "0.0.1", | |
| 4 | − "private": true, | |
| 5 | − "main": "src/index.ts", | |
| 6 | − "types": "src/index.ts" | |
| 7 | −} | |
| @@ -1,143 +0,0 @@ | ||
| 1 | −// ── Status enums ────────────────────────────────────────────────────────────── | |
| 2 | − | |
| 3 | −export type TaskStatus = | |
| 4 | − | "queued" | |
| 5 | − | "running" | |
| 6 | − | "completed" | |
| 7 | − | "failed" | |
| 8 | − | "timed_out" | |
| 9 | − | "denied"; | |
| 10 | − | |
| 11 | −export type ApprovalStatus = "pending" | "approved" | "denied"; | |
| 12 | − | |
| 13 | −export type PolicyDecision = "allow" | "require_approval" | "deny"; | |
| 14 | − | |
| 15 | −export type CredentialScope = "workspace" | "actor"; | |
| 16 | − | |
| 17 | −export type CredentialProvider = "managed" | "workos-vault"; | |
| 18 | − | |
| 19 | −export type ToolApprovalMode = "auto" | "required"; | |
| 20 | − | |
| 21 | −export type ToolSourceType = "mcp" | "openapi" | "graphql"; | |
| 22 | − | |
| 23 | −// ── Records ─────────────────────────────────────────────────────────────────── | |
| 24 | − | |
| 25 | −export interface TaskRecord { | |
| 26 | − id: string; | |
| 27 | − code: string; | |
| 28 | − runtimeId: string; | |
| 29 | − status: TaskStatus; | |
| 30 | − timeoutMs: number; | |
| 31 | − metadata: Record<string, unknown>; | |
| 32 | − workspaceId: string; | |
| 33 | − actorId?: string; | |
| 34 | − clientId?: string; | |
| 35 | − createdAt: number; | |
| 36 | − updatedAt: number; | |
| 37 | − startedAt?: number; | |
| 38 | − completedAt?: number; | |
| 39 | − error?: string; | |
| 40 | − stdout?: string; | |
| 41 | − stderr?: string; | |
| 42 | − exitCode?: number; | |
| 43 | −} | |
| 44 | − | |
| 45 | −export interface ApprovalRecord { | |
| 46 | − id: string; | |
| 47 | − taskId: string; | |
| 48 | − toolPath: string; | |
| 49 | − input: unknown; | |
| 50 | − status: ApprovalStatus; | |
| 51 | − reason?: string; | |
| 52 | − reviewerId?: string; | |
| 53 | − createdAt: number; | |
| 54 | − resolvedAt?: number; | |
| 55 | −} | |
| 56 | − | |
| 57 | −export interface PendingApprovalRecord extends ApprovalRecord { | |
| 58 | − task: Pick<TaskRecord, "id" | "status" | "runtimeId" | "timeoutMs" | "createdAt">; | |
| 59 | −} | |
| 60 | − | |
| 61 | −export interface TaskEventRecord { | |
| 62 | − id: number; | |
| 63 | − taskId: string; | |
| 64 | − eventName: string; | |
| 65 | − type: string; | |
| 66 | − payload: unknown; | |
| 67 | − createdAt: number; | |
| 68 | −} | |
| 69 | − | |
| 70 | −export interface AccessPolicyRecord { | |
| 71 | − id: string; | |
| 72 | − workspaceId: string; | |
| 73 | − actorId?: string; | |
| 74 | − clientId?: string; | |
| 75 | − toolPathPattern: string; | |
| 76 | − decision: PolicyDecision; | |
| 77 | − priority: number; | |
| 78 | − createdAt: number; | |
| 79 | − updatedAt: number; | |
| 80 | −} | |
| 81 | − | |
| 82 | −export interface CredentialRecord { | |
| 83 | − id: string; | |
| 84 | − workspaceId: string; | |
| 85 | − sourceKey: string; | |
| 86 | − scope: CredentialScope; | |
| 87 | − actorId?: string; | |
| 88 | − provider: CredentialProvider; | |
| 89 | − secretJson: Record<string, unknown>; | |
| 90 | − createdAt: number; | |
| 91 | − updatedAt: number; | |
| 92 | −} | |
| 93 | − | |
| 94 | −export interface ToolSourceRecord { | |
| 95 | − id: string; | |
| 96 | − workspaceId: string; | |
| 97 | − name: string; | |
| 98 | − type: ToolSourceType; | |
| 99 | − config: Record<string, unknown>; | |
| 100 | − enabled: boolean; | |
| 101 | − createdAt: number; | |
| 102 | − updatedAt: number; | |
| 103 | −} | |
| 104 | − | |
| 105 | −// ── Descriptors ─────────────────────────────────────────────────────────────── | |
| 106 | − | |
| 107 | −export interface ToolDescriptor { | |
| 108 | − path: string; | |
| 109 | − description: string; | |
| 110 | − approval: ToolApprovalMode; | |
| 111 | − source?: string; | |
| 112 | − /** Lightweight TS type hint for args (for LLM prompt / discover tool). */ | |
| 113 | − argsType?: string; | |
| 114 | − /** Lightweight TS type hint for return value (for LLM prompt / discover tool). */ | |
| 115 | − returnsType?: string; | |
| 116 | − /** Raw operationId from the OpenAPI spec (used by Monaco for indexed access types). */ | |
| 117 | − operationId?: string; | |
| 118 | − /** @deprecated Kept for cache compat. */ | |
| 119 | − schemaTypes?: Record<string, string>; | |
| 120 | −} | |
| 121 | − | |
| 122 | −export interface OpenApiSourceQuality { | |
| 123 | − sourceKey: string; | |
| 124 | − toolCount: number; | |
| 125 | − unknownArgsCount: number; | |
| 126 | − unknownReturnsCount: number; | |
| 127 | − partialUnknownArgsCount: number; | |
| 128 | − partialUnknownReturnsCount: number; | |
| 129 | − argsQuality: number; | |
| 130 | − returnsQuality: number; | |
| 131 | − overallQuality: number; | |
| 132 | −} | |
| 133 | − | |
| 134 | −export interface AnonymousContext { | |
| 135 | − sessionId: string; | |
| 136 | − workspaceId: string; | |
| 137 | − actorId: string; | |
| 138 | − clientId: string; | |
| 139 | − accountId?: string; | |
| 140 | − userId?: string; | |
| 141 | − createdAt: number; | |
| 142 | − lastSeenAt: number; | |
| 143 | −} | |
| @@ -1,11 +0,0 @@ | ||
| 1 | −{ | |
| 2 | − "compilerOptions": { | |
| 3 | − "strict": true, | |
| 4 | − "moduleResolution": "bundler", | |
| 5 | − "target": "ESNext", | |
| 6 | − "module": "ESNext", | |
| 7 | − "noEmit": true, | |
| 8 | − "skipLibCheck": true | |
| 9 | − }, | |
| 10 | − "include": ["src/**/*.ts"] | |
| 11 | −} | |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | "private": true, |
| 4 | 4 | "type": "module", |
| 5 | 5 | "workspaces": [ |
| 6 | + "executor/convex", | |
| 6 | 7 | "executor/apps/*", |
| 7 | 8 | "executor/packages/*", |
| 8 | 9 | "assistant", |
| @@ -32,7 +33,6 @@ · 23 unmodified lines | ||
| 32 | 33 | "@convex-dev/migrations": "^0.3.1", |
| 33 | 34 | "@convex-dev/stripe": "^0.1.3", |
| 34 | 35 | "@convex-dev/workos-authkit": "^0.1.6", |
| 35 | − "@executor/contracts": "workspace:*", | |
| 36 | 36 | "@workos-inc/node": "^8.2.0", |
| 37 | 37 | "convex": "^1.31.7", |
| 38 | 38 | "convex-helpers": "^0.1.111", |