History

Update environment configuration and remove deprecated web pages

- Added `EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY` variable to `.env.example` for runtime restrictions. - Updated `README.md` to document the new environment variable. - Removed obsolete web pages related to dashboard, approvals, billing, members, onboarding, organization, static app shell, tasks, and tools to streamline the codebase.

Rhys Sullivan committed 9 months ago

Commit ee0853e1 parent d407eb744 files · +631 −534Browse files

Unified · Split

44 files
  1. .env.example +4 −0
  2. executor/README.md +1 −0
  3. executor/apps/web/src/app/[[...dashboard]]/page.tsx +1 −1
  4. executor/apps/web/src/app/billing/page.tsx +0 −5
  5. executor/apps/web/src/app/members/page.tsx +0 −5
  6. executor/apps/web/src/app/onboarding/page.tsx +0 −5
  7. executor/apps/web/src/app/organization/page.tsx +0 −5
  8. executor/apps/web/src/app/page.tsx +0 −5
  9. executor/apps/web/src/app/static-app-shell/page.tsx +0 −5
  10. executor/apps/web/src/app/tasks/page.tsx +0 −5
  11. executor/apps/web/src/app/tools/page.tsx +0 −5
  12. executor/apps/web/src/components/tasks/task-composer.tsx +20 −15
  13. executor/apps/web/src/components/tools/explorer-tool-detail.tsx +4 −2
  14. executor/apps/web/src/lib/session-context-derivation.ts +1 −1
  15. executor/apps/web/src/lib/tools-source-helpers.ts +20 −2
  16. executor/packages/convex/_generated/api.d.ts +313 −317
  17. executor/packages/convex/auth/accounts.ts +5 −5
  18. executor/packages/convex/auth/bootstrap.ts +1 −1
  19. executor/packages/convex/auth/identity.test.ts +47 −0
  20. executor/packages/convex/auth/identity.ts +14 −12
  21. executor/packages/convex/auth/naming.ts +5 −5
  22. executor/packages/convex/auth/personal_workspace.ts +1 −1
  23. executor/packages/convex/billing.ts +1 −1
  24. executor/packages/convex/database.test.ts +0 −1
  25. executor/packages/convex/database/mappers.ts +0 −1
  26. executor/packages/convex/database/policies.ts +2 −12
  27. executor/packages/convex/database/tasks.ts +0 −2
  28. executor/packages/convex/executor.ts +5 −4
  29. executor/packages/convex/http/mcp_service.ts +2 −1
  30. executor/packages/convex/runtime/task_runner.ts +28 −13
  31. executor/packages/convex/runtime/workspace_tools.ts +1 −2
  32. executor/packages/convex/runtimeCallbacks.ts +0 −2
  33. executor/packages/convex/schema.ts +1 −2
  34. executor/packages/convex/task/finish.ts +0 −1
  35. executor/packages/core/src/mcp-server-contracts.ts +2 −1
  36. executor/packages/core/src/mcp-server-formatting.ts +3 −3
  37. executor/packages/core/src/mcp-server.ts +5 −4
  38. executor/packages/core/src/runtimes/cloudflare-worker-loader-runtime.test.ts +29 −0
  39. executor/packages/core/src/runtimes/runtime-catalog.ts +53 −0
  40. executor/packages/core/src/tools.ts +0 −82
  41. executor/packages/core/src/types.ts +6 −0
  42. executor/scripts/prod/doctor-prod.test.ts +26 −0
  43. executor/scripts/prod/doctor-prod.ts +28 −0
  44. executor/scripts/prod/setup-prod-cloudflare.ts +2 −0
.env.example+4−0
@@ -7,6 +7,10 @@ CONVEX_DEPLOYMENT=dev:your-deployment-slug · 6 unmodified lines
77 CONVEX_URL=https://your-deployment.convex.cloud
88 CONVEX_SITE_URL=https://your-deployment.convex.site
99
10+# ── Runtime restriction (set in Convex prod) ──
11+# Set to 1 to force task execution to Cloudflare dynamic worker runtime only.
12+# EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY=1
13+
1014 # ── WorkOS (optional — enables auth, omit for guest-only mode) ──
1115 # WORKOS_CLIENT_ID=client_...
1216 # WORKOS_API_KEY=sk_test_...
executor/README.md+1−0
@@ -136,6 +136,7 @@ Important env vars (see root `.env.example` for the base template): · 135 unmodified lines
136136 - Core:
137137 - `CONVEX_URL`
138138 - `CONVEX_SITE_URL`
139+ - `EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY` (`1` in production to force Cloudflare dynamic worker runtime)
139140 - WorkOS (optional auth/org features):
140141 - `WORKOS_CLIENT_ID`
141142 - `WORKOS_API_KEY`
executor/apps/web/src/app/[[...dashboard]]/page.tsxrenamed from executor/apps/web/src/app/approvals/page.tsx+1−1
@@ -1,5 +1,5 @@
11 import { StaticAppShell } from "@/components/static-app-shell";
22
3−export default function ApprovalsPage() {
3+export default function DashboardRoutesPage() {
44 return <StaticAppShell />;
55 }
executor/apps/web/src/app/billing/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { redirect } from "next/navigation";
2−
3−export default function BillingPage() {
4− redirect("/organization?tab=billing");
5−}
executor/apps/web/src/app/members/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { redirect } from "next/navigation";
2−
3−export default function MembersPage() {
4− redirect("/organization?tab=members");
5−}
executor/apps/web/src/app/onboarding/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function OnboardingPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/app/organization/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function OrganizationPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/app/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function DashboardPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/app/static-app-shell/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function StaticAppShellPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/app/tasks/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function TasksPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/app/tools/page.tsx+0−5
@@ -1,5 +0,0 @@
1−import { StaticAppShell } from "@/components/static-app-shell";
2−
3−export default function ToolsPage() {
4− return <StaticAppShell />;
5−}
executor/apps/web/src/components/tasks/task-composer.tsx+20−15
@@ -1,6 +1,6 @@
11 "use client";
22
3−import { useState } from "react";
3+import { useEffect, useState } from "react";
44 import { Play, Send } from "lucide-react";
55 import { useMutation, useQuery } from "convex/react";
66 import { toast } from "sonner";
@@ -21,20 +21,12 @@ import { useSession } from "@/lib/session-context"; · 14 unmodified lines
2121 import type { RuntimeTargetDescriptor } from "@/lib/types";
2222 import { useWorkspaceTools } from "@/hooks/use-workspace-tools";
2323
24−const DEFAULT_CODE = `// Example: call some tools and return a compact result
25−const time = await tools.utils.get_time();
26−const sum = await tools.math.add({ a: 7, b: 35 });
27−
28−// This will require approval:
29−await tools.admin.send_announcement({
30− channel: "general",
31− message: "Hello from executor!"
24+const DEFAULT_CODE = `// Example: discover tools and return matching tool names
25+const found = await tools.discover({
26+ query: "Discover",
3227 });
3328
34−return {
35− isoTime: time.iso,
36− total: sum.result,
37−};`;
29+return found.results.map((tool) => tool.path);`;
3830 const DEFAULT_TIMEOUT_MS = 300_000;
3931
4032 export function TaskComposer() {
@@ -47,14 +39,27 @@ export function TaskComposer() { · 6 unmodified lines
4739 const runtimes = useQuery(convexApi.workspace.listRuntimeTargets, {});
4840 const createTask = useMutation(convexApi.executor.createTask);
4941 const { tools, dtsUrls, loadingTools, loadingTypes } = useWorkspaceTools(context ?? null);
42+ const runtimeTargets = runtimes ?? [];
43+
44+ useEffect(() => {
45+ if (runtimeTargets.length === 0) {
46+ return;
47+ }
48+ if (!runtimeTargets.some((runtime: RuntimeTargetDescriptor) => runtime.id === runtimeId)) {
49+ setRuntimeId(runtimeTargets[0]!.id);
50+ }
51+ }, [runtimeId, runtimeTargets]);
5052
5153 const handleSubmit = async () => {
5254 if (!context || !code.trim()) return;
5355 setSubmitting(true);
5456 try {
57+ const selectedRuntimeId = runtimeTargets.some((runtime: RuntimeTargetDescriptor) => runtime.id === runtimeId)
58+ ? runtimeId
59+ : undefined;
5560 const data = await createTask({
5661 code,
57− runtimeId,
62+ runtimeId: selectedRuntimeId,
5863 timeoutMs: Number.parseInt(timeoutMs, 10) || DEFAULT_TIMEOUT_MS,
5964 workspaceId: context.workspaceId,
6065 sessionId: context.sessionId,
@@ -87,7 +92,7 @@ export function TaskComposer() { · 26 unmodified lines
8792 <SelectValue />
8893 </SelectTrigger>
8994 <SelectContent>
90− {(runtimes ?? []).map((r: RuntimeTargetDescriptor) => (
95+ {runtimeTargets.map((r: RuntimeTargetDescriptor) => (
9196 <SelectItem key={r.id} value={r.id} className="text-xs">
9297 {r.label}
9398 </SelectItem>
executor/apps/web/src/components/tools/explorer-tool-detail.tsx+4−2
@@ -11,6 +11,8 @@ const codePlugin = createCodePlugin({ · 10 unmodified lines
1111
1212 export function ToolDetail({ tool, depth }: { tool: ToolDescriptor; depth: number }) {
1313 const insetLeft = depth * 20 + 8 + 16 + 8;
14+ const argsType = tool.strictArgsType?.trim() || tool.argsType;
15+ const returnsType = tool.strictReturnsType?.trim() || tool.returnsType;
1416
1517 return (
1618 <div className="space-y-2.5 pb-3 pt-1 pr-2" style={{ paddingLeft: insetLeft }}>
@@ -36,8 +38,8 @@ export function ToolDetail({ tool, depth }: { tool: ToolDescriptor; depth: numbe · 19 unmodified lines
3638 )}
3739 </div>
3840
39− {tool.argsType && <TypeSignature raw={tool.argsType} label="Arguments" />}
40− {tool.returnsType && <TypeSignature raw={tool.returnsType} label="Returns" />}
41+ {argsType && <TypeSignature raw={argsType} label="Arguments" />}
42+ {returnsType && <TypeSignature raw={returnsType} label="Returns" />}
4143 </div>
4244 );
4345 }
executor/apps/web/src/lib/session-context-derivation.ts+1−1
@@ -5,7 +5,7 @@ export interface WorkosAccount { · 4 unmodified lines
55 _id: Id<"accounts">;
66 provider: "workos" | "anonymous";
77 providerAccountId: string;
8− email: string;
8+ email?: string;
99 name: string;
1010 avatarUrl?: string | null;
1111 }
executor/apps/web/src/lib/tools-source-helpers.ts+20−2
@@ -271,8 +271,26 @@ export function withUniqueSourceName(baseName: string, takenNames: Set<string>): · 270 unmodified lines
271271 }
272272 }
273273
274−export function catalogSourceName(item: { providerName: string; name: string }): string {
274+function sourceNameFromCatalogId(id: string): string {
275+ let slug = sanitizeSourceName(id);
276+ slug = slug
277+ .replace(/-openapi$/, "")
278+ .replace(/-graphql-api$/, "")
279+ .replace(/-rest-api$/, "")
280+ .replace(/-api$/, "")
281+ .replace(/-rest$/, "");
282+ return slug || "catalog";
283+}
284+
285+export function catalogSourceName(item: { id?: string; providerName: string; name: string }): string {
286+ if (item.id && item.id.trim().length > 0) {
287+ return sourceNameFromCatalogId(item.id);
288+ }
289+
275290 const owner = sanitizeSourceName(item.providerName || "catalog");
276291 const title = sanitizeSourceName(item.name);
277− return sanitizeSourceName(`${owner}-${title}`);
292+ if (!title || title === "api" || title === "openapi" || title === "graphql") {
293+ return owner;
294+ }
295+ return title;
278296 }
executor/packages/convex/_generated/api.d.ts+313−317
@@ -326,7 +326,6 @@ export declare const api: { · 325 unmodified lines
326326 error?: string;
327327 exitCode?: number;
328328 internalSecret: string;
329− result?: any;
330329 runId: string;
331330 status: "completed" | "failed" | "timed_out" | "denied";
332331 },
@@ -557,6 +556,60 @@ export declare const internal: { · 224 unmodified lines
557556 >;
558557 };
559558 database: {
559+ anonymous_session: {
560+ bootstrapAnonymousSession: FunctionReference<
561+ "mutation",
562+ "internal",
563+ { sessionId?: string },
564+ any
565+ >;
566+ };
567+ approvals: {
568+ createApproval: FunctionReference<
569+ "mutation",
570+ "internal",
571+ { id: string; input?: any; taskId: string; toolPath: string },
572+ any
573+ >;
574+ getApproval: FunctionReference<
575+ "query",
576+ "internal",
577+ { approvalId: string },
578+ any
579+ >;
580+ getApprovalInWorkspace: FunctionReference<
581+ "query",
582+ "internal",
583+ { approvalId: string; workspaceId: Id<"workspaces"> },
584+ any
585+ >;
586+ listApprovals: FunctionReference<
587+ "query",
588+ "internal",
589+ {
590+ status?: "pending" | "approved" | "denied";
591+ workspaceId: Id<"workspaces">;
592+ },
593+ any
594+ >;
595+ listPendingApprovals: FunctionReference<
596+ "query",
597+ "internal",
598+ { workspaceId: Id<"workspaces"> },
599+ any
600+ >;
601+ resolveApproval: FunctionReference<
602+ "mutation",
603+ "internal",
604+ {
605+ approvalId: string;
606+ decision: "approved" | "denied";
607+ reason?: string;
608+ reviewerId?: string;
609+ },
610+ any
611+ >;
612+ };
560613 bootstrapAnonymousSession: FunctionReference<
561614 "mutation",
562615 "internal",
@@ -602,6 +655,41 @@ export declare const internal: { · 39 unmodified lines
602655 { eventName: string; payload: any; taskId: string; type: string },
603656 any
604657 >;
658+ credentials: {
659+ listCredentialProviders: FunctionReference<"query", "internal", {}, any>;
660+ listCredentials: FunctionReference<
661+ "query",
662+ "internal",
663+ { workspaceId: Id<"workspaces"> },
664+ any
665+ >;
666+ resolveCredential: FunctionReference<
667+ "query",
668+ "internal",
669+ {
670+ actorId?: string;
671+ scope: "workspace" | "actor";
672+ sourceKey: string;
673+ workspaceId: Id<"workspaces">;
674+ },
675+ any
676+ >;
677+ upsertCredential: FunctionReference<
678+ "mutation",
679+ "internal",
680+ {
681+ actorId?: string;
682+ id?: string;
683+ overridesJson?: any;
684+ provider?: "local-convex" | "workos-vault";
685+ scope: "workspace" | "actor";
686+ secretJson: any;
687+ sourceKey: string;
688+ workspaceId: Id<"workspaces">;
689+ },
690+ any
691+ >;
692+ };
605693 deleteToolSource: FunctionReference<
606694 "mutation",
607695 "internal",
@@ -721,7 +809,6 @@ export declare const internal: { · 113 unmodified lines
721809 {
722810 error?: string;
723811 exitCode?: number;
724− result?: any;
725812 status: "completed" | "failed" | "timed_out" | "denied";
726813 taskId: string;
727814 },
@@ -733,6 +820,94 @@ export declare const internal: { · 5 unmodified lines
733820 { taskId: string },
734821 any
735822 >;
823+ oauth: {
824+ consumeAnonymousOauthAuthorizationCode: FunctionReference<
825+ "mutation",
826+ "internal",
827+ { code: string },
828+ any
829+ >;
830+ countAnonymousOauthAuthorizationCodes: FunctionReference<
831+ "query",
832+ "internal",
833+ {},
834+ any
835+ >;
836+ getActiveAnonymousOauthSigningKey: FunctionReference<
837+ "query",
838+ "internal",
839+ {},
840+ any
841+ >;
842+ getAnonymousOauthClient: FunctionReference<
843+ "query",
844+ "internal",
845+ { clientId: string },
846+ any
847+ >;
848+ purgeExpiredAnonymousOauthAuthorizationCodes: FunctionReference<
849+ "mutation",
850+ "internal",
851+ { now: number },
852+ any
853+ >;
854+ registerAnonymousOauthClient: FunctionReference<
855+ "mutation",
856+ "internal",
857+ { clientId: string; clientName?: string; redirectUris: Array<string> },
858+ any
859+ >;
860+ storeAnonymousOauthAuthorizationCode: FunctionReference<
861+ "mutation",
862+ "internal",
863+ {
864+ actorId: string;
865+ clientId: string;
866+ code: string;
867+ codeChallenge: string;
868+ codeChallengeMethod: string;
869+ createdAt: number;
870+ expiresAt: number;
871+ redirectUri: string;
872+ tokenClaims?: any;
873+ },
874+ any
875+ >;
876+ storeAnonymousOauthSigningKey: FunctionReference<
877+ "mutation",
878+ "internal",
879+ {
880+ algorithm: string;
881+ keyId: string;
882+ privateKeyJwk: any;
883+ publicKeyJwk: any;
884+ },
885+ any
886+ >;
887+ };
888+ policies: {
889+ listAccessPolicies: FunctionReference<
890+ "query",
891+ "internal",
892+ { workspaceId: Id<"workspaces"> },
893+ any
894+ >;
895+ listRuntimeTargets: FunctionReference<"query", "internal", {}, any>;
896+ upsertAccessPolicy: FunctionReference<
897+ "mutation",
898+ "internal",
899+ {
900+ actorId?: string;
901+ clientId?: string;
902+ decision: "allow" | "require_approval" | "deny";
903+ id?: string;
904+ priority?: number;
905+ toolPathPattern: string;
906+ workspaceId: Id<"workspaces">;
907+ },
908+ any
909+ >;
910+ };
736911 purgeExpiredAnonymousOauthAuthorizationCodes: FunctionReference<
737912 "mutation",
738913 "internal",
@@ -800,6 +975,142 @@ export declare const internal: { · 61 unmodified lines
800975 },
801976 any
802977 >;
978+ task_events: {
979+ createTaskEvent: FunctionReference<
980+ "mutation",
981+ "internal",
982+ { eventName: string; payload: any; taskId: string; type: string },
983+ any
984+ >;
985+ listTaskEvents: FunctionReference<
986+ "query",
987+ "internal",
988+ { taskId: string },
989+ any
990+ >;
991+ };
992+ tasks: {
993+ createTask: FunctionReference<
994+ "mutation",
995+ "internal",
996+ {
997+ actorId: string;
998+ clientId?: string;
999+ code: string;
1000+ id: string;
1001+ metadata?: any;
1002+ runtimeId: string;
1003+ timeoutMs?: number;
1004+ workspaceId: Id<"workspaces">;
1005+ },
1006+ any
1007+ >;
1008+ getTask: FunctionReference<"query", "internal", { taskId: string }, any>;
1009+ getTaskInWorkspace: FunctionReference<
1010+ "query",
1011+ "internal",
1012+ { taskId: string; workspaceId: Id<"workspaces"> },
1013+ any
1014+ >;
1015+ listQueuedTaskIds: FunctionReference<
1016+ "query",
1017+ "internal",
1018+ { limit?: number },
1019+ any
1020+ >;
1021+ listTasks: FunctionReference<
1022+ "query",
1023+ "internal",
1024+ { workspaceId: Id<"workspaces"> },
1025+ any
1026+ >;
1027+ markTaskFinished: FunctionReference<
1028+ "mutation",
1029+ "internal",
1030+ {
1031+ error?: string;
1032+ exitCode?: number;
1033+ status: "completed" | "failed" | "timed_out" | "denied";
1034+ taskId: string;
1035+ },
1036+ any
1037+ >;
1038+ markTaskRunning: FunctionReference<
1039+ "mutation",
1040+ "internal",
1041+ { taskId: string },
1042+ any
1043+ >;
1044+ };
1045+ tool_calls: {
1046+ finishToolCall: FunctionReference<
1047+ "mutation",
1048+ "internal",
1049+ {
1050+ callId: string;
1051+ error?: string;
1052+ status: "completed" | "failed" | "denied";
1053+ taskId: string;
1054+ },
1055+ any
1056+ >;
1057+ getToolCall: FunctionReference<
1058+ "query",
1059+ "internal",
1060+ { callId: string; taskId: string },
1061+ any
1062+ >;
1063+ listToolCalls: FunctionReference<
1064+ "query",
1065+ "internal",
1066+ { taskId: string },
1067+ any
1068+ >;
1069+ setToolCallPendingApproval: FunctionReference<
1070+ "mutation",
1071+ "internal",
1072+ { approvalId: string; callId: string; taskId: string },
1073+ any
1074+ >;
1075+ upsertToolCallRequested: FunctionReference<
1076+ "mutation",
1077+ "internal",
1078+ {
1079+ callId: string;
1080+ taskId: string;
1081+ toolPath: string;
1082+ workspaceId: Id<"workspaces">;
1083+ },
1084+ any
1085+ >;
1086+ };
1087+ tool_sources: {
1088+ deleteToolSource: FunctionReference<
1089+ "mutation",
1090+ "internal",
1091+ { sourceId: string; workspaceId: Id<"workspaces"> },
1092+ any
1093+ >;
1094+ listToolSources: FunctionReference<
1095+ "query",
1096+ "internal",
1097+ { workspaceId: Id<"workspaces"> },
1098+ any
1099+ >;
1100+ upsertToolSource: FunctionReference<
1101+ "mutation",
1102+ "internal",
1103+ {
1104+ config: any;
1105+ enabled?: boolean;
1106+ id?: string;
1107+ name: string;
1108+ type: "mcp" | "openapi" | "graphql";
1109+ workspaceId: Id<"workspaces">;
1110+ },
1111+ any
1112+ >;
1113+ };
8031114 upsertAccessPolicy: FunctionReference<
8041115 "mutation",
8051116 "internal",
@@ -854,320 +1165,6 @@ export declare const internal: { · 48 unmodified lines
8541165 any
8551166 >;
8561167 };
857− database_anonymous_session: {
858− bootstrapAnonymousSession: FunctionReference<
859− "mutation",
860− "internal",
861− { sessionId?: string },
862− any
863− >;
864− };
865− database_approvals: {
866− createApproval: FunctionReference<
867− "mutation",
868− "internal",
869− { id: string; input?: any; taskId: string; toolPath: string },
870− any
871− >;
872− getApproval: FunctionReference<
873− "query",
874− "internal",
875− { approvalId: string },
876− any
877− >;
878− getApprovalInWorkspace: FunctionReference<
879− "query",
880− "internal",
881− { approvalId: string; workspaceId: Id<"workspaces"> },
882− any
883− >;
884− listApprovals: FunctionReference<
885− "query",
886− "internal",
887− {
888− status?: "pending" | "approved" | "denied";
889− workspaceId: Id<"workspaces">;
890− },
891− any
892− >;
893− listPendingApprovals: FunctionReference<
894− "query",
895− "internal",
896− { workspaceId: Id<"workspaces"> },
897− any
898− >;
899− resolveApproval: FunctionReference<
900− "mutation",
901− "internal",
902− {
903− approvalId: string;
904− decision: "approved" | "denied";
905− reason?: string;
906− reviewerId?: string;
907− },
908− any
909− >;
910− };
911− database_credentials: {
912− listCredentialProviders: FunctionReference<"query", "internal", {}, any>;
913− listCredentials: FunctionReference<
914− "query",
915− "internal",
916− { workspaceId: Id<"workspaces"> },
917− any
918− >;
919− resolveCredential: FunctionReference<
920− "query",
921− "internal",
922− {
923− actorId?: string;
924− scope: "workspace" | "actor";
925− sourceKey: string;
926− workspaceId: Id<"workspaces">;
927− },
928− any
929− >;
930− upsertCredential: FunctionReference<
931− "mutation",
932− "internal",
933− {
934− actorId?: string;
935− id?: string;
936− overridesJson?: any;
937− provider?: "local-convex" | "workos-vault";
938− scope: "workspace" | "actor";
939− secretJson: any;
940− sourceKey: string;
941− workspaceId: Id<"workspaces">;
942− },
943− any
944− >;
945− };
946− database_oauth: {
947− consumeAnonymousOauthAuthorizationCode: FunctionReference<
948− "mutation",
949− "internal",
950− { code: string },
951− any
952− >;
953− countAnonymousOauthAuthorizationCodes: FunctionReference<
954− "query",
955− "internal",
956− {},
957− any
958− >;
959− getActiveAnonymousOauthSigningKey: FunctionReference<
960− "query",
961− "internal",
962− {},
963− any
964− >;
965− getAnonymousOauthClient: FunctionReference<
966− "query",
967− "internal",
968− { clientId: string },
969− any
970− >;
971− purgeExpiredAnonymousOauthAuthorizationCodes: FunctionReference<
972− "mutation",
973− "internal",
974− { now: number },
975− any
976− >;
977− registerAnonymousOauthClient: FunctionReference<
978− "mutation",
979− "internal",
980− { clientId: string; clientName?: string; redirectUris: Array<string> },
981− any
982− >;
983− storeAnonymousOauthAuthorizationCode: FunctionReference<
984− "mutation",
985− "internal",
986− {
987− actorId: string;
988− clientId: string;
989− code: string;
990− codeChallenge: string;
991− codeChallengeMethod: string;
992− createdAt: number;
993− expiresAt: number;
994− redirectUri: string;
995− tokenClaims?: any;
996− },
997− any
998− >;
999− storeAnonymousOauthSigningKey: FunctionReference<
1000− "mutation",
1001− "internal",
1002− {
1003− algorithm: string;
1004− keyId: string;
1005− privateKeyJwk: any;
1006− publicKeyJwk: any;
1007− },
1008− any
1009− >;
1010− };
1011− database_policies: {
1012− listAccessPolicies: FunctionReference<
1013− "query",
1014− "internal",
1015− { workspaceId: Id<"workspaces"> },
1016− any
1017− >;
1018− listRuntimeTargets: FunctionReference<"query", "internal", {}, any>;
1019− upsertAccessPolicy: FunctionReference<
1020− "mutation",
1021− "internal",
1022− {
1023− actorId?: string;
1024− clientId?: string;
1025− decision: "allow" | "require_approval" | "deny";
1026− id?: string;
1027− priority?: number;
1028− toolPathPattern: string;
1029− workspaceId: Id<"workspaces">;
1030− },
1031− any
1032− >;
1033− };
1034− database_task_events: {
1035− createTaskEvent: FunctionReference<
1036− "mutation",
1037− "internal",
1038− { eventName: string; payload: any; taskId: string; type: string },
1039− any
1040− >;
1041− listTaskEvents: FunctionReference<
1042− "query",
1043− "internal",
1044− { taskId: string },
1045− any
1046− >;
1047− };
1048− database_tasks: {
1049− createTask: FunctionReference<
1050− "mutation",
1051− "internal",
1052− {
1053− actorId: string;
1054− clientId?: string;
1055− code: string;
1056− id: string;
1057− metadata?: any;
1058− runtimeId: string;
1059− timeoutMs?: number;
1060− workspaceId: Id<"workspaces">;
1061− },
1062− any
1063− >;
1064− getTask: FunctionReference<"query", "internal", { taskId: string }, any>;
1065− getTaskInWorkspace: FunctionReference<
1066− "query",
1067− "internal",
1068− { taskId: string; workspaceId: Id<"workspaces"> },
1069− any
1070− >;
1071− listQueuedTaskIds: FunctionReference<
1072− "query",
1073− "internal",
1074− { limit?: number },
1075− any
1076− >;
1077− listTasks: FunctionReference<
1078− "query",
1079− "internal",
1080− { workspaceId: Id<"workspaces"> },
1081− any
1082− >;
1083− markTaskFinished: FunctionReference<
1084− "mutation",
1085− "internal",
1086− {
1087− error?: string;
1088− exitCode?: number;
1089− result?: any;
1090− status: "completed" | "failed" | "timed_out" | "denied";
1091− taskId: string;
1092− },
1093− any
1094− >;
1095− markTaskRunning: FunctionReference<
1096− "mutation",
1097− "internal",
1098− { taskId: string },
1099− any
1100− >;
1101− };
1102− database_tool_calls: {
1103− finishToolCall: FunctionReference<
1104− "mutation",
1105− "internal",
1106− {
1107− callId: string;
1108− error?: string;
1109− status: "completed" | "failed" | "denied";
1110− taskId: string;
1111− },
1112− any
1113− >;
1114− getToolCall: FunctionReference<
1115− "query",
1116− "internal",
1117− { callId: string; taskId: string },
1118− any
1119− >;
1120− listToolCalls: FunctionReference<
1121− "query",
1122− "internal",
1123− { taskId: string },
1124− any
1125− >;
1126− setToolCallPendingApproval: FunctionReference<
1127− "mutation",
1128− "internal",
1129− { approvalId: string; callId: string; taskId: string },
1130− any
1131− >;
1132− upsertToolCallRequested: FunctionReference<
1133− "mutation",
1134− "internal",
1135− {
1136− callId: string;
1137− taskId: string;
1138− toolPath: string;
1139− workspaceId: Id<"workspaces">;
1140− },
1141− any
1142− >;
1143− };
1144− database_tool_sources: {
1145− deleteToolSource: FunctionReference<
1146− "mutation",
1147− "internal",
1148− { sourceId: string; workspaceId: Id<"workspaces"> },
1149− any
1150− >;
1151− listToolSources: FunctionReference<
1152− "query",
1153− "internal",
1154− { workspaceId: Id<"workspaces"> },
1155− any
1156− >;
1157− upsertToolSource: FunctionReference<
1158− "mutation",
1159− "internal",
1160− {
1161− config: any;
1162− enabled?: boolean;
1163− id?: string;
1164− name: string;
1165− type: "mcp" | "openapi" | "graphql";
1166− workspaceId: Id<"workspaces">;
1167− },
1168− any
1169− >;
1170− };
11711168 executor: {
11721169 completeRuntimeRun: FunctionReference<
11731170 "mutation",
@@ -1176,7 +1173,6 @@ export declare const internal: { · 2 unmodified lines
11761173 durationMs?: number;
11771174 error?: string;
11781175 exitCode?: number;
1179− result?: any;
11801176 runId: string;
11811177 status: "completed" | "failed" | "timed_out" | "denied";
11821178 },
executor/packages/convex/auth/accounts.ts+5−5
@@ -3,7 +3,7 @@ import type { DbCtx } from "./types"; · 2 unmodified lines
33
44 type UpsertWorkosAccountArgs = {
55 workosUserId: string;
6− email: string;
6+ email?: string;
77 fullName: string;
88 firstName?: string;
99 lastName?: string;
@@ -15,13 +15,13 @@ type UpsertWorkosAccountArgs = { · 5 unmodified lines
1515 export async function upsertWorkosAccount(ctx: DbCtx, args: UpsertWorkosAccountArgs) {
1616 let account = await getAccountByWorkosId(ctx, args.workosUserId);
1717 const patchData = {
18− email: args.email,
1918 name: args.fullName,
20− firstName: args.firstName,
21− lastName: args.lastName,
22− avatarUrl: args.avatarUrl,
2319 status: "active" as const,
2420 updatedAt: args.now,
21+ ...(args.email ? { email: args.email } : null),
22+ ...(args.firstName !== undefined ? { firstName: args.firstName } : null),
23+ ...(args.lastName !== undefined ? { lastName: args.lastName } : null),
24+ ...(args.avatarUrl !== undefined ? { avatarUrl: args.avatarUrl } : null),
2525 ...(args.includeLastLoginAt ? { lastLoginAt: args.now } : null),
2626 };
2727
executor/packages/convex/auth/bootstrap.ts+1−1
@@ -16,7 +16,7 @@ async function seedHintedOrganizationMembership( · 15 unmodified lines
1616 args: {
1717 accountId: AccountId;
1818 hintedWorkosOrgId?: string;
19− email: string;
19+ email?: string;
2020 now: number;
2121 },
2222 ) {
executor/packages/convex/auth/identity.test.ts+47−0
@@ -0,0 +1,47 @@
1+import { expect, test } from "bun:test";
2+import { resolveIdentityProfile } from "./identity";
3+
4+test("resolveIdentityProfile uses stable user label when identity is sparse", () => {
5+ const subject = "user_01KH1TVHS4WJCPQG2XQJGMMJMJ";
6+
7+ const profile = resolveIdentityProfile({
8+ identity: { subject },
9+ authKitProfile: null,
10+ });
11+
12+ expect(profile.email).toBeUndefined();
13+ expect(profile.fullName).toBe("User GMMJMJ");
14+});
15+
16+test("resolveIdentityProfile reads standard identity claims", () => {
17+ const profile = resolveIdentityProfile({
18+ identity: {
19+ subject: "user_abc123",
20+ email: "alex@example.com",
21+ given_name: "Alex",
22+ family_name: "Doe",
23+ picture: "https://cdn.example.com/alex.png",
24+ organization_id: "org_123",
25+ },
26+ authKitProfile: null,
27+ });
28+
29+ expect(profile.email).toBe("alex@example.com");
30+ expect(profile.firstName).toBe("Alex");
31+ expect(profile.lastName).toBe("Doe");
32+ expect(profile.fullName).toBe("Alex Doe");
33+ expect(profile.avatarUrl).toBe("https://cdn.example.com/alex.png");
34+ expect(profile.hintedWorkosOrgId).toBe("org_123");
35+});
36+
37+test("resolveIdentityProfile prefers explicit identity name", () => {
38+ const profile = resolveIdentityProfile({
39+ identity: {
40+ subject: "user_abc123",
41+ name: "Alexandra D.",
42+ },
43+ authKitProfile: null,
44+ });
45+
46+ expect(profile.fullName).toBe("Alexandra D.");
47+});
executor/packages/convex/auth/identity.ts+14−12
@@ -8,6 +8,10 @@ type WorkosProfile = { · 7 unmodified lines
88 profilePictureUrl?: string | null;
99 };
1010
11+function deriveFallbackUserLabel(workosUserId: string): string {
12+ return `User ${workosUserId.slice(-6)}`;
13+}
14+
1115 export function getIdentityString(identity: Record<string, unknown>, keys: string[]): string | undefined {
1216 for (const key of keys) {
1317 const value = identity[key];
@@ -36,28 +40,26 @@ export function resolveIdentityProfile(args: { · 22 unmodified lines
3640
3741 const email =
3842 authKitProfile?.email
39− ?? getIdentityString(identity, ["email", "https://workos.com/email", "upn"])
40− ?? `${identity.subject}@workos.executor.local`;
43+ ?? getIdentityString(identity, ["email", "upn"]);
4144
4245 const firstName = authKitProfile?.firstName
43− ?? getIdentityString(identity, ["given_name", "first_name", "https://workos.com/first_name"]);
46+ ?? getIdentityString(identity, ["given_name", "first_name"]);
4447
4548 const lastName = authKitProfile?.lastName
46− ?? getIdentityString(identity, ["family_name", "last_name", "https://workos.com/last_name"]);
49+ ?? getIdentityString(identity, ["family_name", "last_name"]);
50+
51+ const combinedName = [firstName, lastName].filter(Boolean).join(" ").trim();
4752
4853 const fullName =
49− (getIdentityString(identity, ["name", "https://workos.com/name"]) ?? [firstName, lastName].filter(Boolean).join(" "))
50− || email;
54+ getIdentityString(identity, ["name", "full_name"])
55+ ?? (combinedName.length > 0 ? combinedName : undefined)
56+ ?? deriveFallbackUserLabel(identity.subject);
5157
5258 const avatarUrl =
5359 (authKitProfile?.profilePictureUrl ?? undefined)
54− ?? getIdentityString(identity, ["picture", "avatar_url", "https://workos.com/profile_picture_url"]);
60+ ?? getIdentityString(identity, ["picture", "avatar_url", "profile_picture_url"]);
5561
56− const hintedWorkosOrgId = getIdentityString(identity, [
57− "org_id",
58− "organization_id",
59− "https://workos.com/organization_id",
60− ]);
62+ const hintedWorkosOrgId = getIdentityString(identity, ["org_id", "organization_id"]);
6163
6264 return {
6365 email,
executor/packages/convex/auth/naming.ts+5−5
@@ -38,7 +38,7 @@ export function isGeneratedPersonalWorkspaceName(name: string, workosUserId: str · 37 unmodified lines
3838 return new RegExp(`^${escapeRegex(workosUserId)}'s workspace$`, "i").test(name);
3939 }
4040
41−function deriveOwnerLabel(args: { firstName?: string; fullName?: string; email: string; workosUserId: string }): string {
41+function deriveOwnerLabel(args: { firstName?: string; fullName?: string; email?: string; workosUserId: string }): string {
4242 const firstName = args.firstName?.trim();
4343 if (firstName && !/^my$/i.test(firstName)) {
4444 return firstName;
@@ -49,7 +49,7 @@ function deriveOwnerLabel(args: { firstName?: string; fullName?: string; email: · 4 unmodified lines
4949 return fullName;
5050 }
5151
52− const emailLocalPart = args.email.split("@")[0]?.trim();
52+ const emailLocalPart = args.email?.split("@")[0]?.trim();
5353 if (emailLocalPart) {
5454 const normalized = emailLocalPart
5555 .replace(/[._-]+/g, " ")
@@ -64,7 +64,7 @@ function deriveOwnerLabel(args: { firstName?: string; fullName?: string; email: · 8 unmodified lines
6464 return `User ${args.workosUserId.slice(-6)}`;
6565 }
6666
67−export function derivePersonalNames(args: { firstName?: string; fullName?: string; email: string; workosUserId: string }) {
67+export function derivePersonalNames(args: { firstName?: string; fullName?: string; email?: string; workosUserId: string }) {
6868 const ownerLabel = deriveOwnerLabel(args);
6969 return {
7070 organizationName: `${ownerLabel}'s Organization`,
@@ -72,8 +72,8 @@ export function derivePersonalNames(args: { firstName?: string; fullName?: strin · 1 unmodified line
7272 };
7373 }
7474
75−export function buildPersonalWorkspaceSlugSeed(email: string, workosUserId: string): string {
76− const baseSlug = slugify(email.split("@")[0] ?? workosUserId, "workspace");
75+export function buildPersonalWorkspaceSlugSeed(email: string | undefined, workosUserId: string): string {
76+ const baseSlug = slugify(email?.split("@")[0] ?? workosUserId, "workspace");
7777 return `${baseSlug}-${workosUserId.slice(-6)}`;
7878 }
7979
executor/packages/convex/auth/personal_workspace.ts+1−1
@@ -10,7 +10,7 @@ import { · 9 unmodified lines
1010 import type { AccountId, DbCtx } from "./types";
1111
1212 type PersonalWorkspaceOptions = {
13− email: string;
13+ email?: string;
1414 firstName?: string;
1515 fullName?: string;
1616 workosUserId: string;
executor/packages/convex/billing.ts+1−1
@@ -160,7 +160,7 @@ export const createCustomerPortal = action({ · 159 unmodified lines
160160 handler: async (ctx, args): Promise<{ url: string }> => {
161161 const access: {
162162 role: string;
163− email: string;
163+ email?: string;
164164 organizationName: string;
165165 billableMembers: number;
166166 customerId: string | null;
executor/packages/convex/database.test.ts+0−1
@@ -59,7 +59,6 @@ test("task lifecycle supports queue, run, and complete", async () => { · 58 unmodified lines
5959 const finished = await t.mutation(internal.database.markTaskFinished as any, {
6060 taskId: "task_1",
6161 status: "completed",
62− result: { ok: true },
6362 exitCode: 0,
6463 });
6564 expect(finished?.status).toBe("completed");
executor/packages/convex/database/mappers.ts+0−1
@@ -58,7 +58,6 @@ export function mapTask(doc: Doc<"tasks">) { · 57 unmodified lines
5858 startedAt: doc.startedAt,
5959 completedAt: doc.completedAt,
6060 error: doc.error,
61− result: doc.result,
6261 exitCode: doc.exitCode,
6362 };
6463 }
executor/packages/convex/database/policies.ts+2−12
@@ -1,23 +1,13 @@
11 import { v } from "convex/values";
22 import { internalMutation, internalQuery } from "../_generated/server";
3+import { listRuntimeTargets as listAvailableRuntimeTargets } from "../../core/src/runtimes/runtime-catalog";
34 import { mapPolicy } from "./mappers";
45 import { policyDecisionValidator } from "./validators";
56
67 export const listRuntimeTargets = internalQuery({
78 args: {},
89 handler: async () => {
9− return [
10− {
11− id: "local-bun",
12− label: "Local JS Runtime",
13− description: "Runs generated code in-process using Bun",
14− },
15− {
16− id: "cloudflare-worker-loader",
17− label: "Cloudflare Worker Loader",
18− description: "Runs generated code in a Cloudflare Worker",
19− },
20− ];
10+ return listAvailableRuntimeTargets();
2111 },
2212 });
2313
executor/packages/convex/database/tasks.ts+0−2
@@ -115,7 +115,6 @@ export const markTaskFinished = internalMutation({ · 114 unmodified lines
115115 args: {
116116 taskId: v.string(),
117117 status: completedTaskStatusValidator,
118− result: v.optional(v.any()),
119118 exitCode: v.optional(v.number()),
120119 error: v.optional(v.string()),
121120 },
@@ -132,7 +131,6 @@ export const markTaskFinished = internalMutation({ · 10 unmodified lines
132131 const now = Date.now();
133132 await ctx.db.patch(doc._id, {
134133 status: args.status,
135− result: args.result,
136134 exitCode: args.exitCode,
137135 error: args.error,
138136 completedAt: now,
executor/packages/convex/executor.ts+5−4
@@ -5,7 +5,7 @@ import type { MutationCtx } from "./_generated/server"; · 4 unmodified lines
55 import { internalMutation } from "./_generated/server";
66 import { workspaceMutation } from "../core/src/function-builders";
77 import { actorIdForAccount } from "../core/src/identity";
8−import { isKnownRuntimeId } from "../core/src/runtimes/runtime-catalog";
8+import { defaultRuntimeId, isKnownRuntimeId, isRuntimeEnabled } from "../core/src/runtimes/runtime-catalog";
99 import type { ApprovalRecord, TaskRecord } from "../core/src/types";
1010 import { isTerminalTaskStatus, taskTerminalEventType } from "./task/status";
1111 import { DEFAULT_TASK_TIMEOUT_MS } from "./task/constants";
@@ -29,10 +29,13 @@ async function createTaskRecord( · 17 unmodified lines
2929 throw new Error("Task code is required");
3030 }
3131
32− const runtimeId = args.runtimeId ?? "local-bun";
32+ const runtimeId = args.runtimeId ?? defaultRuntimeId();
3333 if (!isKnownRuntimeId(runtimeId)) {
3434 throw new Error(`Unsupported runtime: ${runtimeId}`);
3535 }
36+ if (!isRuntimeEnabled(runtimeId)) {
37+ throw new Error(`Runtime is disabled for this deployment: ${runtimeId}`);
38+ }
3639
3740 const taskId = `task_${crypto.randomUUID()}`;
3841 const task = (await ctx.runMutation(internal.database.createTask, {
@@ -212,7 +215,6 @@ export const completeRuntimeRun = internalMutation({ · 173 unmodified lines
212215 args: {
213216 runId: v.string(),
214217 status: v.union(v.literal("completed"), v.literal("failed"), v.literal("timed_out"), v.literal("denied")),
215− result: v.optional(v.any()),
216218 exitCode: v.optional(v.number()),
217219 error: v.optional(v.string()),
218220 durationMs: v.optional(v.number()),
@@ -230,7 +232,6 @@ export const completeRuntimeRun = internalMutation({ · 11 unmodified lines
230232 const finished = await markTaskFinished(ctx, {
231233 taskId: args.runId,
232234 status: args.status,
233− result: args.result,
234235 exitCode: args.exitCode,
235236 error: args.error,
236237 });
executor/packages/convex/http/mcp_service.ts+2−1
@@ -4,6 +4,7 @@ import type { Id } from "../_generated/dataModel"; · 3 unmodified lines
44 import type {
55 AnonymousContext,
66 PendingApprovalRecord,
7+ TaskExecutionOutcome,
78 TaskRecord,
89 ToolDescriptor,
910 } from "../../core/src/types";
@@ -25,7 +26,7 @@ export function createMcpExecutorService(ctx: ActionCtx) { · 15 unmodified lines
2526 };
2627 return await ctx.runMutation(internal.executor.createTaskInternal, taskInput);
2728 },
28− runTaskNow: async (taskId: string): Promise<null> => {
29+ runTaskNow: async (taskId: string): Promise<TaskExecutionOutcome | null> => {
2930 return await ctx.runAction(internal.executorNode.runTask, { taskId });
3031 },
3132 getTask: async (taskId: string, workspaceId?: Id<"workspaces">): Promise<TaskRecord | null> => {
executor/packages/convex/runtime/task_runner.ts+28−13
@@ -9,9 +9,10 @@ import { · 8 unmodified lines
99 CLOUDFLARE_WORKER_LOADER_RUNTIME_ID,
1010 isCloudflareWorkerLoaderConfigured,
1111 isKnownRuntimeId,
12+ isRuntimeEnabled,
1213 } from "../../core/src/runtimes/runtime-catalog";
1314 import { runCodeWithAdapter } from "../../core/src/runtimes/runtime-core";
14−import type { TaskRecord } from "../../core/src/types";
15+import type { TaskExecutionOutcome, TaskRecord } from "../../core/src/types";
1516 import { describeError } from "../../core/src/utils";
1617 import { publishTaskEvent } from "./events";
1718 import { taskTerminalEventType } from "../task/status";
@@ -21,7 +22,7 @@ import { invokeTool } from "./tool_invocation"; · 3 unmodified lines
2122 async function markTaskFailedAndPublish(
2223 ctx: ActionCtx,
2324 args: { taskId: string; error: string },
24−): Promise<void> {
25+): Promise<TaskExecutionOutcome | null> {
2526 const failed = await markTaskFinished(ctx, {
2627 taskId: args.taskId,
2728 status: "failed",
@@ -29,7 +30,7 @@ async function markTaskFailedAndPublish( · 1 unmodified line
2930 });
3031
3132 if (!failed) {
32− return;
33+ return null;
3334 }
3435
3536 await publishTaskEvent(ctx, args.taskId, "task", "task.failed", {
@@ -38,6 +39,8 @@ async function markTaskFailedAndPublish( · 2 unmodified lines
3839 error: failed.error,
3940 ...(failed.completedAt ? { completedAt: failed.completedAt } : {}),
4041 });
42+
43+ return { task: failed };
4144 }
4245
4346 async function publishTerminalTaskResult(
@@ -62,26 +65,31 @@ async function publishTerminalTaskResult( · 18 unmodified lines
6265 export async function runQueuedTask(
6366 ctx: ActionCtx,
6467 args: { taskId: string },
65−): Promise<null> {
68+): Promise<TaskExecutionOutcome | null> {
6669 const task = (await ctx.runQuery(internal.database.getTask, { taskId: args.taskId })) as TaskRecord | null;
6770 if (!task || task.status !== "queued") {
6871 return null;
6972 }
7073
7174 if (!isKnownRuntimeId(task.runtimeId)) {
72− await markTaskFailedAndPublish(ctx, {
75+ return await markTaskFailedAndPublish(ctx, {
7376 taskId: args.taskId,
7477 error: `Runtime not found: ${task.runtimeId}`,
7578 });
76− return null;
79+ }
80+
81+ if (!isRuntimeEnabled(task.runtimeId)) {
82+ return await markTaskFailedAndPublish(ctx, {
83+ taskId: args.taskId,
84+ error: `Runtime is disabled for this deployment: ${task.runtimeId}`,
85+ });
7786 }
7887
7988 if (task.runtimeId === CLOUDFLARE_WORKER_LOADER_RUNTIME_ID && !isCloudflareWorkerLoaderConfigured()) {
80− await markTaskFailedAndPublish(ctx, {
89+ return await markTaskFailedAndPublish(ctx, {
8190 taskId: args.taskId,
8291 error: `Runtime is not configured: ${task.runtimeId}`,
8392 });
84− return null;
8593 }
8694
8795 try {
@@ -106,17 +114,15 @@ export async function runQueuedTask( · 18 unmodified lines
106114 });
107115
108116 if (!dispatchResult.ok) {
109− await markTaskFailedAndPublish(ctx, {
117+ return await markTaskFailedAndPublish(ctx, {
110118 taskId: args.taskId,
111119 error: dispatchResult.error,
112120 });
113− return null;
114121 }
115122
116123 const finished = await markTaskFinished(ctx, {
117124 taskId: args.taskId,
118125 status: dispatchResult.status,
119− result: dispatchResult.result,
120126 exitCode: dispatchResult.exitCode,
121127 error: dispatchResult.error,
122128 });
@@ -131,7 +137,11 @@ export async function runQueuedTask( · 8 unmodified lines
131137 finished,
132138 durationMs: dispatchResult.durationMs,
133139 });
134− return null;
140+ return {
141+ task: finished,
142+ result: dispatchResult.result,
143+ durationMs: dispatchResult.durationMs,
144+ };
135145 }
136146
137147 const runtimeResult = await (async () => {
@@ -153,7 +163,6 @@ export async function runQueuedTask( · 15 unmodified lines
153163 const finished = await markTaskFinished(ctx, {
154164 taskId: args.taskId,
155165 status: runtimeResult.status,
156− result: runtimeResult.result,
157166 exitCode: runtimeResult.exitCode,
158167 error: runtimeResult.error,
159168 });
@@ -168,6 +177,11 @@ export async function runQueuedTask( · 8 unmodified lines
168177 finished,
169178 durationMs: runtimeResult.durationMs,
170179 });
180+ return {
181+ task: finished,
182+ result: runtimeResult.result,
183+ durationMs: runtimeResult.durationMs,
184+ };
171185 } catch (error) {
172186 const message = describeError(error);
173187 const denied = message.startsWith(APPROVAL_DENIED_PREFIX);
@@ -183,6 +197,7 @@ export async function runQueuedTask( · 9 unmodified lines
183197 status: denied ? "denied" : "failed",
184198 finished,
185199 });
200+ return { task: finished };
186201 }
187202 }
188203
executor/packages/convex/runtime/workspace_tools.ts+1−2
@@ -11,7 +11,6 @@ import { · 10 unmodified lines
1111 type WorkspaceToolSnapshot,
1212 } from "../../core/src/tool-sources";
1313 import type { ExternalToolSourceConfig } from "../../core/src/tool-source-types";
14−import { DEFAULT_TOOLS } from "../../core/src/tools";
1514 import type {
1615 AccessPolicyRecord,
1716 OpenApiSourceQuality,
@@ -22,7 +21,7 @@ import type { · 4 unmodified lines
2221 import { computeOpenApiSourceQuality, listVisibleToolDescriptors } from "./tool_descriptors";
2322 import { loadSourceArtifact, normalizeExternalToolSource, sourceSignature } from "./tool_source_loading";
2423
25−const baseTools = new Map<string, ToolDefinition>(DEFAULT_TOOLS.map((tool) => [tool.path, tool]));
24+const baseTools = new Map<string, ToolDefinition>();
2625
2726 export interface DtsStorageEntry {
2827 sourceKey: string;
executor/packages/convex/runtimeCallbacks.ts+0−2
@@ -37,7 +37,6 @@ export const completeRun = mutation({ · 36 unmodified lines
3737 internalSecret: v.string(),
3838 runId: v.string(),
3939 status: v.union(v.literal("completed"), v.literal("failed"), v.literal("timed_out"), v.literal("denied")),
40− result: v.optional(v.any()),
4140 exitCode: v.optional(v.number()),
4241 error: v.optional(v.string()),
4342 durationMs: v.optional(v.number()),
@@ -48,7 +47,6 @@ export const completeRun = mutation({ · 4 unmodified lines
4847 return await ctx.runMutation(internal.executor.completeRuntimeRun, {
4948 runId: args.runId,
5049 status: args.status,
51− result: args.result,
5250 exitCode: args.exitCode,
5351 error: args.error,
5452 durationMs: args.durationMs,
executor/packages/convex/schema.ts+1−2
@@ -53,7 +53,7 @@ export default defineSchema({ · 52 unmodified lines
5353 accounts: defineTable({
5454 provider: accountProvider,
5555 providerAccountId: v.string(), // WorkOS user ID or anon_* UUID
56− email: v.string(),
56+ email: v.optional(v.string()),
5757 name: v.string(),
5858 firstName: v.optional(v.string()),
5959 lastName: v.optional(v.string()),
@@ -188,7 +188,6 @@ export default defineSchema({ · 128 unmodified lines
188188 error: v.optional(v.string()),
189189 stdout: v.optional(v.string()),
190190 stderr: v.optional(v.string()),
191− result: v.optional(v.any()),
192191 exitCode: v.optional(v.number()),
193192 createdAt: v.number(),
194193 updatedAt: v.number(),
executor/packages/convex/task/finish.ts+0−1
@@ -8,7 +8,6 @@ type TaskFinishContext = Pick<ActionCtx, "runMutation"> | Pick<MutationCtx, "run · 7 unmodified lines
88 export interface MarkTaskFinishedInput {
99 taskId: string;
1010 status: TerminalTaskStatus;
11− result?: unknown;
1211 exitCode?: number;
1312 error?: string;
1413 }
executor/packages/core/src/mcp-server-contracts.ts+2−1
@@ -3,6 +3,7 @@ import type { · 2 unmodified lines
33 AnonymousContext,
44 CreateTaskInput,
55 PendingApprovalRecord,
6+ TaskExecutionOutcome,
67 TaskRecord,
78 ToolDescriptor,
89 } from "./types";
@@ -10,7 +11,7 @@ import type { Id } from "../../convex/_generated/dataModel"; · 1 unmodified line
1011
1112 export interface McpExecutorService {
1213 createTask(input: CreateTaskInput): Promise<{ task: TaskRecord }>;
13− runTaskNow?(taskId: string): Promise<null>;
14+ runTaskNow?(taskId: string): Promise<TaskExecutionOutcome | null>;
1415 getTask(taskId: string, workspaceId?: Id<"workspaces">): Promise<TaskRecord | null>;
1516 subscribe(taskId: string, workspaceId: Id<"workspaces">, listener: (event: LiveTaskEvent) => void): () => void;
1617 bootstrapAnonymousContext(sessionId?: string): Promise<AnonymousContext>;
executor/packages/core/src/mcp-server-formatting.ts+3−3
@@ -15,7 +15,7 @@ function listTopLevelToolKeys(tools: ToolDescriptor[]): string[] { · 14 unmodified lines
1515 return [...keys].sort();
1616 }
1717
18−export function summarizeTask(task: TaskRecord): string {
18+export function summarizeTask(task: TaskRecord, result: unknown = task.result): string {
1919 const maxResultPreviewChars = 30_000;
2020 const lines = [
2121 `taskId: ${task.id}`,
@@ -32,8 +32,8 @@ export function summarizeTask(task: TaskRecord): string { · 10 unmodified lines
3232 }
3333
3434 let text = lines.join("\n");
35− if (task.result !== undefined) {
36− const serialized = Result.try(() => JSON.stringify(task.result, null, 2)).unwrapOr(String(task.result));
35+ if (result !== undefined) {
36+ const serialized = Result.try(() => JSON.stringify(result, null, 2)).unwrapOr(String(result));
3737 if (serialized.length > maxResultPreviewChars) {
3838 text += asCodeBlock(
3939 "json",
executor/packages/core/src/mcp-server.ts+5−4
@@ -87,8 +87,8 @@ function createRunCodeTool( · 86 unmodified lines
8787 }
8888
8989 if (service.runTaskNow) {
90− await service.runTaskNow(created.task.id);
91− const task = await service.getTask(created.task.id, context.workspaceId);
90+ const runOutcome = await service.runTaskNow(created.task.id);
91+ const task = runOutcome?.task ?? await service.getTask(created.task.id, context.workspaceId);
9292 if (!task) {
9393 return {
9494 content: [textContent(`Task ${created.task.id} not found after execution`)],
@@ -96,16 +96,17 @@ function createRunCodeTool( · 1 unmodified line
9696 };
9797 }
9898
99+ const result = runOutcome?.result ?? task.result;
99100 const isError = task.status !== "completed";
100101 return {
101− content: [textContent(summarizeTask(task))],
102+ content: [textContent(summarizeTask(task, result))],
102103 structuredContent: {
103104 taskId: task.id,
104105 status: task.status,
105106 runtimeId: task.runtimeId,
106107 exitCode: task.exitCode,
107108 error: task.error,
108− result: task.result,
109+ result,
109110 workspaceId: context.workspaceId,
110111 actorId: context.actorId,
111112 sessionId: context.sessionId,
executor/packages/core/src/runtimes/cloudflare-worker-loader-runtime.test.ts+29−0
@@ -153,6 +153,35 @@ describe("runtime catalog", () => { · 152 unmodified lines
153153 expect(isCloudflareWorkerLoaderConfigured()).toBe(true);
154154 });
155155
156+ test("can restrict runtime targets to cloudflare dynamic worker", async () => {
157+ const {
158+ CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY,
159+ CLOUDFLARE_WORKER_LOADER_RUNTIME_ID,
160+ LOCAL_BUN_RUNTIME_ID,
161+ defaultRuntimeId,
162+ isRuntimeEnabled,
163+ listRuntimeTargets,
164+ } = await import("./runtime-catalog");
165+
166+ const previous = process.env[CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY];
167+ try {
168+ process.env[CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY] = "1";
169+
170+ expect(defaultRuntimeId()).toBe(CLOUDFLARE_WORKER_LOADER_RUNTIME_ID);
171+ expect(isRuntimeEnabled(LOCAL_BUN_RUNTIME_ID)).toBe(false);
172+ expect(isRuntimeEnabled(CLOUDFLARE_WORKER_LOADER_RUNTIME_ID)).toBe(true);
173+ expect(listRuntimeTargets().map((target) => target.id)).toEqual([
174+ CLOUDFLARE_WORKER_LOADER_RUNTIME_ID,
175+ ]);
176+ } finally {
177+ if (previous === undefined) {
178+ delete process.env[CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY];
179+ } else {
180+ process.env[CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY] = previous;
181+ }
182+ }
183+ });
184+
156185 test("getCloudflareWorkerLoaderConfig reads env vars", async () => {
157186 const { getCloudflareWorkerLoaderConfig } = await import("./runtime-catalog");
158187 const config = getCloudflareWorkerLoaderConfig();
executor/packages/core/src/runtimes/runtime-catalog.ts+53−0
@@ -2,6 +2,9 @@ · 1 unmodified line
22
33 export const LOCAL_BUN_RUNTIME_ID = "local-bun";
44 export const CLOUDFLARE_WORKER_LOADER_RUNTIME_ID = "cloudflare-worker-loader";
5+export const CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY = "EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY";
6+
7+const TRUTHY_ENV_VALUES = new Set(["1", "true", "yes", "on"]);
58
69 const KNOWN_RUNTIME_IDS = new Set([
710 LOCAL_BUN_RUNTIME_ID,
@@ -12,6 +15,56 @@ export function isKnownRuntimeId(runtimeId: string): boolean { · 4 unmodified lines
1215 return KNOWN_RUNTIME_IDS.has(runtimeId);
1316 }
1417
18+function isTruthyEnvValue(value: string | undefined): boolean {
19+ if (!value) {
20+ return false;
21+ }
22+ return TRUTHY_ENV_VALUES.has(value.trim().toLowerCase());
23+}
24+
25+export function isCloudflareDynamicWorkerOnlyMode(): boolean {
26+ return isTruthyEnvValue(process.env[CLOUDFLARE_DYNAMIC_WORKER_ONLY_ENV_KEY]);
27+}
28+
29+export function isRuntimeEnabled(runtimeId: string): boolean {
30+ if (!isKnownRuntimeId(runtimeId)) {
31+ return false;
32+ }
33+ if (!isCloudflareDynamicWorkerOnlyMode()) {
34+ return true;
35+ }
36+ return runtimeId === CLOUDFLARE_WORKER_LOADER_RUNTIME_ID;
37+}
38+
39+export function defaultRuntimeId(): string {
40+ return isCloudflareDynamicWorkerOnlyMode()
41+ ? CLOUDFLARE_WORKER_LOADER_RUNTIME_ID
42+ : LOCAL_BUN_RUNTIME_ID;
43+}
44+
45+export interface RuntimeTargetDescriptor {
46+ id: string;
47+ label: string;
48+ description: string;
49+}
50+
51+const RUNTIME_TARGETS: RuntimeTargetDescriptor[] = [
52+ {
53+ id: LOCAL_BUN_RUNTIME_ID,
54+ label: "Local JS Runtime",
55+ description: "Runs generated code in-process using Bun",
56+ },
57+ {
58+ id: CLOUDFLARE_WORKER_LOADER_RUNTIME_ID,
59+ label: "Cloudflare Worker Loader",
60+ description: "Runs generated code in a Cloudflare Worker",
61+ },
62+];
63+
64+export function listRuntimeTargets(): RuntimeTargetDescriptor[] {
65+ return RUNTIME_TARGETS.filter((target) => isRuntimeEnabled(target.id));
66+}
67+
1568 // ── Cloudflare Worker Loader config ──────────────────────────────────────────
1669
1770 export interface CloudflareWorkerLoaderConfig {
executor/packages/core/src/tools.ts+0−82
@@ -1,82 +0,0 @@
1−import type { ToolDefinition } from "./types";
2−import { asRecord } from "./utils";
3−
4−export const DEFAULT_TOOLS: ToolDefinition[] = [
5− {
6− path: "utils.get_time",
7− description: "Return current server time.",
8− approval: "auto",
9− source: "local",
10− metadata: {
11− argsType: "{}",
12− returnsType: "{ iso: string; unix: number }",
13− },
14− run: async () => ({
15− iso: new Date().toISOString(),
16− unix: Date.now(),
17− }),
18− },
19− {
20− path: "math.add",
21− description: "Add two numbers.",
22− approval: "auto",
23− source: "local",
24− metadata: {
25− argsType: "{ a: number; b: number }",
26− returnsType: "{ result: number }",
27− },
28− run: async (input) => {
29− const payload = asRecord(input);
30− const a = Number(payload.a ?? 0);
31− const b = Number(payload.b ?? 0);
32− if (!Number.isFinite(a) || !Number.isFinite(b)) {
33− throw new Error("math.add expects numeric a and b");
34− }
35− return { result: a + b };
36− },
37− },
38− {
39− path: "admin.send_announcement",
40− description: "Mock announcement sender that requires approval.",
41− approval: "required",
42− source: "local",
43− metadata: {
44− argsType: "{ channel?: string; message: string }",
45− returnsType: "{ sent: true; channel: string; message: string }",
46− },
47− run: async (input) => {
48− const payload = asRecord(input);
49− const channel = String(payload.channel ?? "general");
50− const message = String(payload.message ?? "");
51− if (!message.trim()) {
52− throw new Error("admin.send_announcement requires a non-empty message");
53− }
54− return {
55− sent: true,
56− channel,
57− message,
58− };
59− },
60− },
61− {
62− path: "admin.delete_data",
63− description: "Mock destructive operation that requires approval.",
64− approval: "required",
65− source: "local",
66− metadata: {
67− argsType: "{ key: string }",
68− returnsType: "{ deleted: true; key: string }",
69− },
70− run: async (input) => {
71− const payload = asRecord(input);
72− const key = String(payload.key ?? "");
73− if (!key.trim()) {
74− throw new Error("admin.delete_data requires key");
75− }
76− return {
77− deleted: true,
78− key,
79− };
80− },
81− },
82−];
executor/packages/core/src/types.ts+6−0
@@ -39,6 +39,12 @@ export interface TaskRecord { · 38 unmodified lines
3939 exitCode?: number;
4040 }
4141
42+export interface TaskExecutionOutcome {
43+ task: TaskRecord;
44+ result?: unknown;
45+ durationMs?: number;
46+}
47+
4248 export interface ApprovalRecord {
4349 id: string;
4450 taskId: string;
executor/scripts/prod/doctor-prod.test.ts+26−0
@@ -24,6 +24,7 @@ describe("buildEnvChecks", () => { · 23 unmodified lines
2424 expect(byName.get("env:CLOUDFLARE_SANDBOX_RUN_URL")?.ok).toBe(false);
2525 expect(byName.get("env:CLOUDFLARE_SANDBOX_AUTH_TOKEN")?.ok).toBe(false);
2626 expect(byName.get("env:EXECUTOR_INTERNAL_TOKEN")?.ok).toBe(false);
27+ expect(byName.get("env:EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY")?.ok).toBe(false);
2728 expect(byName.get("env:CONVEX_URL")?.ok).toBe(false);
2829 expect(byName.get("env:WORKOS_CLIENT_ID")?.ok).toBe(false);
2930 expect(byName.get("env:WORKOS_API_KEY")?.ok).toBe(false);
@@ -40,6 +41,7 @@ describe("buildEnvChecks", () => { · 10 unmodified lines
4041 ["CLOUDFLARE_SANDBOX_RUN_URL", "https://executor-sandbox-host.example.workers.dev/v1/runs"],
4142 ["CLOUDFLARE_SANDBOX_AUTH_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
4243 ["EXECUTOR_INTERNAL_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
44+ ["EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY", "1"],
4345 ["CONVEX_URL", "https://perceptive-pigeon-577.convex.site"],
4446 ["WORKOS_CLIENT_ID", "client_123"],
4547 ["WORKOS_API_KEY", "sk_test_123"],
@@ -61,6 +63,7 @@ describe("buildEnvChecks", () => { · 15 unmodified lines
6163 ["CLOUDFLARE_SANDBOX_RUN_URL", "https://executor-sandbox-host.example.workers.dev/v1/runs"],
6264 ["CLOUDFLARE_SANDBOX_AUTH_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
6365 ["EXECUTOR_INTERNAL_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
66+ ["EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY", "1"],
6467 ["CONVEX_URL", "https://perceptive-pigeon-577.convex.cloud"],
6568 ["CONVEX_SITE_URL", "https://perceptive-pigeon-577.convex.site"],
6669 ["WORKOS_CLIENT_ID", "client_123"],
@@ -77,4 +80,27 @@ describe("buildEnvChecks", () => { · 10 unmodified lines
7780 const failures = checks.filter((check) => !check.ok);
7881 expect(failures).toEqual([]);
7982 });
83+
84+ test("requires cloudflare-only runtime mode to be truthy", () => {
85+ const env = new Map<string, string>([
86+ ["CLOUDFLARE_SANDBOX_RUN_URL", "https://executor-sandbox-host.example.workers.dev/v1/runs"],
87+ ["CLOUDFLARE_SANDBOX_AUTH_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
88+ ["EXECUTOR_INTERNAL_TOKEN", "abcdefghijklmnopqrstuvwxyz0123456789"],
89+ ["EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY", "0"],
90+ ["CONVEX_URL", "https://perceptive-pigeon-577.convex.cloud"],
91+ ["CONVEX_SITE_URL", "https://perceptive-pigeon-577.convex.site"],
92+ ["WORKOS_CLIENT_ID", "client_123"],
93+ ["WORKOS_API_KEY", "sk_test_123"],
94+ ["WORKOS_WEBHOOK_SECRET", "whsec_123"],
95+ ["WORKOS_COOKIE_PASSWORD", "cookie_password_123"],
96+ ["STRIPE_SECRET_KEY", "sk_live_123"],
97+ ["STRIPE_WEBHOOK_SECRET", "whsec_live_123"],
98+ ["STRIPE_PRICE_ID", "price_123"],
99+ ["MCP_AUTHORIZATION_SERVER", "https://auth.example.com"],
100+ ]);
101+
102+ const checks = buildEnvChecks(env);
103+ const runtimeModeCheck = checks.find((check) => check.name === "runtime restriction mode");
104+ expect(runtimeModeCheck?.ok).toBe(false);
105+ });
80106 });
executor/scripts/prod/doctor-prod.ts+28−0
@@ -20,6 +20,7 @@ const REQUIRED_PROD_ENV_KEYS = [ · 19 unmodified lines
2020 "CLOUDFLARE_SANDBOX_RUN_URL",
2121 "CLOUDFLARE_SANDBOX_AUTH_TOKEN",
2222 "EXECUTOR_INTERNAL_TOKEN",
23+ "EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY",
2324 "CONVEX_URL",
2425 ] as const;
2526
@@ -113,6 +114,13 @@ function parseUrl(input: string): URL | null { · 87 unmodified lines
113114 }
114115 }
115116
117+function isTruthy(value: string | undefined): boolean {
118+ if (!value) {
119+ return false;
120+ }
121+ return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
122+}
123+
116124 function deploymentPrefix(hostname: string, suffix: ".convex.cloud" | ".convex.site"): string | null {
117125 if (!hostname.endsWith(suffix)) {
118126 return null;
@@ -254,6 +262,19 @@ export function buildEnvChecks(env: Map<string, string>): DoctorCheck[] { · 135 unmodified lines
254262 addTokenStrengthCheck(report, "EXECUTOR_INTERNAL_TOKEN", internalSecret);
255263 }
256264
265+ const cloudflareOnlyMode = env.get("EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY")?.trim();
266+ if (cloudflareOnlyMode) {
267+ const enabled = isTruthy(cloudflareOnlyMode);
268+ addCheck(
269+ report,
270+ "runtime restriction mode",
271+ enabled,
272+ enabled
273+ ? "Cloudflare dynamic worker runtime-only mode is enabled"
274+ : `expected truthy value, got '${cloudflareOnlyMode}'`,
275+ );
276+ }
277+
257278 for (const key of OPTIONAL_BILLING_ENV_KEYS) {
258279 const value = env.get(key)?.trim();
259280 addCheck(
@@ -272,12 +293,19 @@ function guidanceForFailure(checkName: string): string[] { · 12 unmodified lines
272293 checkName === "env:CLOUDFLARE_SANDBOX_RUN_URL"
273294 || checkName === "env:CLOUDFLARE_SANDBOX_AUTH_TOKEN"
274295 || checkName === "env:EXECUTOR_INTERNAL_TOKEN"
296+ || checkName === "env:EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY"
275297 ) {
276298 return [
277299 "Run `bun run setup:prod:cloudflare --deploy` (or `bun run setup:prod:all`) to deploy sandbox host and set required Cloudflare runtime env vars.",
278300 ];
279301 }
280302
303+ if (checkName === "runtime restriction mode") {
304+ return [
305+ "Set `EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY=1` in Convex prod env to force Cloudflare dynamic worker runtime.",
306+ ];
307+ }
308+
281309 if (checkName === "env:CONVEX_URL") {
282310 return [
283311 "Set Convex API URL: `bunx convex env set CONVEX_URL https://<deployment>.convex.cloud --prod`.",
executor/scripts/prod/setup-prod-cloudflare.ts+2−0
@@ -260,12 +260,14 @@ async function main(): Promise<void> { · 259 unmodified lines
260260 await setConvexEnv("CLOUDFLARE_SANDBOX_RUN_URL", runUrl);
261261 await setConvexEnv("CLOUDFLARE_SANDBOX_AUTH_TOKEN", authToken);
262262 await setConvexEnv("EXECUTOR_INTERNAL_TOKEN", internalToken);
263+ await setConvexEnv("EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY", "1");
263264 await setConvexEnv("CONVEX_URL", convexUrl);
264265
265266 console.log("\n[setup] cloudflare production runtime configured");
266267 console.log(` CLOUDFLARE_SANDBOX_RUN_URL: ${runUrl}`);
267268 console.log(` CLOUDFLARE_SANDBOX_AUTH_TOKEN: ${redact(authToken)}`);
268269 console.log(` EXECUTOR_INTERNAL_TOKEN: ${redact(internalToken)}`);
270+ console.log(" EXECUTOR_CLOUDFLARE_DYNAMIC_WORKER_ONLY: 1");
269271 console.log(` CONVEX_URL: ${convexUrl}`);
270272
271273 if (options.runDoctor) {