History

Centralize shared enum validators across core and schema

Rhys Sullivan committed 8 months ago

Commit 96afda91 parent 84fddd56 files · +288 −207Browse files

Unified · Split

6 files
  1. executor/apps/web/src/lib/types.ts +43 −22
  2. executor/packages/core/src/types.ts +102 −22
  3. executor/packages/database/convex/schema.ts +64 −106
  4. executor/packages/database/package.json +1 −0
  5. executor/packages/database/src/database/validators.ts +73 −54
  6. executor/packages/database/src/task/status.ts +5 −3
executor/apps/web/src/lib/types.ts+43−22
@@ -1,5 +1,26 @@
1import type { Id } from "@executor/database/convex/_generated/dataModel";1import type { Id } from "@executor/database/convex/_generated/dataModel";
2import type {2import type {
3 ApprovalStatus as CoreApprovalStatus,
4 ArgumentConditionOperator as CoreArgumentConditionOperator,
5 CredentialProvider as CoreCredentialProvider,
6 CredentialScope as CoreCredentialScope,
7 CredentialScopeType as CoreCredentialScopeType,
8 PolicyApprovalMode as CorePolicyApprovalMode,
9 PolicyDecision as CorePolicyDecision,
10 PolicyEffect as CorePolicyEffect,
11 PolicyMatchType as CorePolicyMatchType,
12 PolicyScopeType as CorePolicyScopeType,
13 SourceAuthType as CoreSourceAuthType,
14 StorageDurability as CoreStorageDurability,
15 StorageInstanceStatus as CoreStorageInstanceStatus,
16 StorageProvider as CoreStorageProvider,
17 StorageScopeType as CoreStorageScopeType,
18 TaskStatus as CoreTaskStatus,
19 ToolApprovalMode as CoreToolApprovalMode,
20 ToolRoleBindingStatus as CoreToolRoleBindingStatus,
21 ToolRoleSelectorType as CoreToolRoleSelectorType,
22 ToolSourceScopeType as CoreToolSourceScopeType,
23 ToolSourceType as CoreToolSourceType,
3 ToolPolicyRecord as CoreToolPolicyRecord,24 ToolPolicyRecord as CoreToolPolicyRecord,
4 ToolPolicyAssignmentRecord as CoreToolPolicyAssignmentRecord,25 ToolPolicyAssignmentRecord as CoreToolPolicyAssignmentRecord,
5 ToolPolicySetRecord as CoreToolPolicySetRecord,26 ToolPolicySetRecord as CoreToolPolicySetRecord,
@@ -8,34 +29,34 @@ import type { · 2 unmodified lines
8 29
9// ── Shared types (inlined from @executor/contracts) ──────────────────────────30// ── Shared types (inlined from @executor/contracts) ──────────────────────────
10 31
11export type TaskStatus = "queued" | "running" | "completed" | "failed" | "timed_out" | "denied";32export type TaskStatus = CoreTaskStatus;
12export type ApprovalStatus = "pending" | "approved" | "denied";33export type ApprovalStatus = CoreApprovalStatus;
13export type PolicyDecision = "allow" | "require_approval" | "deny";34export type PolicyDecision = CorePolicyDecision;
14export type PolicyScopeType = "account" | "organization" | "workspace";35export type PolicyScopeType = CorePolicyScopeType;
15export type PolicyMatchType = "glob" | "exact";36export type PolicyMatchType = CorePolicyMatchType;
16export type PolicyEffect = "allow" | "deny";37export type PolicyEffect = CorePolicyEffect;
17export type PolicyApprovalMode = "inherit" | "auto" | "required";38export type PolicyApprovalMode = CorePolicyApprovalMode;
18export type ToolRoleSelectorType = "all" | "source" | "namespace" | "tool_path";39export type ToolRoleSelectorType = CoreToolRoleSelectorType;
19export type ToolRoleBindingStatus = "active" | "disabled";40export type ToolRoleBindingStatus = CoreToolRoleBindingStatus;
20export type ArgumentConditionOperator = "equals" | "contains" | "starts_with" | "not_equals";41export type ArgumentConditionOperator = CoreArgumentConditionOperator;
21 42
22export interface ArgumentCondition {43export interface ArgumentCondition {
23 key: string;44 key: string;
24 operator: ArgumentConditionOperator;45 operator: ArgumentConditionOperator;
25 value: string;46 value: string;
26}47}
27export type CredentialScope = "account" | "organization" | "workspace";48export type CredentialScope = CoreCredentialScope;
28export type CredentialProvider = "local-convex" | "workos-vault";49export type CredentialProvider = CoreCredentialProvider;
29export type ToolSourceScopeType = "organization" | "workspace";50export type ToolSourceScopeType = CoreToolSourceScopeType;
30export type CredentialScopeType = "account" | "organization" | "workspace";51export type CredentialScopeType = CoreCredentialScopeType;
31export type ToolApprovalMode = "auto" | "required";52export type ToolApprovalMode = CoreToolApprovalMode;
32export type ToolSourceType = "mcp" | "openapi" | "graphql";53export type ToolSourceType = CoreToolSourceType;
33export type StorageScopeType = "scratch" | "account" | "workspace" | "organization";54export type StorageScopeType = CoreStorageScopeType;
34export type StorageDurability = "ephemeral" | "durable";55export type StorageDurability = CoreStorageDurability;
35export type StorageInstanceStatus = "active" | "closed" | "deleted";56export type StorageInstanceStatus = CoreStorageInstanceStatus;
36export type StorageProvider = "agentfs-local" | "agentfs-cloudflare";57export type StorageProvider = CoreStorageProvider;
37 58
38export type SourceAuthType = "none" | "bearer" | "apiKey" | "basic" | "mixed";59export type SourceAuthType = CoreSourceAuthType;
39 60
40export interface SourceAuthProfile {61export interface SourceAuthProfile {
41 type: SourceAuthType;62 type: SourceAuthType;
executor/packages/core/src/types.ts+102−22
@@ -2,29 +2,103 @@ · 1 unmodified line
2 2
3import type { Id } from "../../database/convex/_generated/dataModel.d.ts";3import type { Id } from "../../database/convex/_generated/dataModel.d.ts";
4 4
5export type TaskStatus = "queued" | "running" | "completed" | "failed" | "timed_out" | "denied";5export const ACCOUNT_PROVIDERS = ["workos", "anonymous"] as const;
6export type ApprovalStatus = "pending" | "approved" | "denied";6export type AccountProvider = (typeof ACCOUNT_PROVIDERS)[number];
7export type ToolCallStatus = "requested" | "pending_approval" | "completed" | "failed" | "denied";7
8export const ACCOUNT_STATUSES = ["active", "deleted"] as const;
9export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
10
11export const ORGANIZATION_STATUSES = ["active", "deleted"] as const;
12export type OrganizationStatus = (typeof ORGANIZATION_STATUSES)[number];
13
14export const ORGANIZATION_ROLES = ["owner", "admin", "member", "billing_admin"] as const;
15export type OrganizationRole = (typeof ORGANIZATION_ROLES)[number];
16
17export const ORGANIZATION_MEMBER_STATUSES = ["active", "pending", "removed"] as const;
18export type OrganizationMemberStatus = (typeof ORGANIZATION_MEMBER_STATUSES)[number];
19
20export const BILLING_SUBSCRIPTION_STATUSES = [
21 "incomplete",
22 "incomplete_expired",
23 "trialing",
24 "active",
25 "past_due",
26 "canceled",
27 "unpaid",
28 "paused",
29] as const;
30export type BillingSubscriptionStatus = (typeof BILLING_SUBSCRIPTION_STATUSES)[number];
31
32export const INVITE_STATUSES = ["pending", "accepted", "expired", "revoked", "failed"] as const;
33export type InviteStatus = (typeof INVITE_STATUSES)[number];
34
35export const TASK_STATUSES = ["queued", "running", "completed", "failed", "timed_out", "denied"] as const;
36export type TaskStatus = (typeof TASK_STATUSES)[number];
37
38export const COMPLETED_TASK_STATUSES = ["completed", "failed", "timed_out", "denied"] as const;
39export type CompletedTaskStatus = (typeof COMPLETED_TASK_STATUSES)[number];
40
41export const APPROVAL_STATUSES = ["pending", "approved", "denied"] as const;
42export type ApprovalStatus = (typeof APPROVAL_STATUSES)[number];
43
44export const TOOL_CALL_STATUSES = ["requested", "pending_approval", "completed", "failed", "denied"] as const;
45export type ToolCallStatus = (typeof TOOL_CALL_STATUSES)[number];
46
47export const TERMINAL_TOOL_CALL_STATUSES = ["completed", "failed", "denied"] as const;
48export type TerminalToolCallStatus = (typeof TERMINAL_TOOL_CALL_STATUSES)[number];
49
8export type PolicyDecision = "allow" | "require_approval" | "deny";50export type PolicyDecision = "allow" | "require_approval" | "deny";
9export type PolicyScopeType = "account" | "organization" | "workspace";51
10export type PolicyMatchType = "glob" | "exact";52export const POLICY_SCOPE_TYPES = ["account", "organization", "workspace"] as const;
11export type PolicyEffect = "allow" | "deny";53export type PolicyScopeType = (typeof POLICY_SCOPE_TYPES)[number];
12export type PolicyApprovalMode = "inherit" | "auto" | "required";54
13export type PolicyResourceType = "all_tools" | "source" | "namespace" | "tool_path";55export const POLICY_MATCH_TYPES = ["glob", "exact"] as const;
14export type ToolSourceScopeType = "organization" | "workspace";56export type PolicyMatchType = (typeof POLICY_MATCH_TYPES)[number];
15export type CredentialScopeType = "account" | "organization" | "workspace";57
58export const POLICY_EFFECTS = ["allow", "deny"] as const;
59export type PolicyEffect = (typeof POLICY_EFFECTS)[number];
60
61export const POLICY_APPROVAL_MODES = ["inherit", "auto", "required"] as const;
62export type PolicyApprovalMode = (typeof POLICY_APPROVAL_MODES)[number];
63
64export const POLICY_RESOURCE_TYPES = ["all_tools", "source", "namespace", "tool_path"] as const;
65export type PolicyResourceType = (typeof POLICY_RESOURCE_TYPES)[number];
66
67export const TOOL_SOURCE_SCOPE_TYPES = ["organization", "workspace"] as const;
68export type ToolSourceScopeType = (typeof TOOL_SOURCE_SCOPE_TYPES)[number];
69
70export const CREDENTIAL_SCOPE_TYPES = ["account", "organization", "workspace"] as const;
71export type CredentialScopeType = (typeof CREDENTIAL_SCOPE_TYPES)[number];
16export type CredentialScope = CredentialScopeType;72export type CredentialScope = CredentialScopeType;
17export type CredentialProvider = "local-convex" | "workos-vault";73
18export type ToolApprovalMode = "auto" | "required";74export const CREDENTIAL_PROVIDERS = ["local-convex", "workos-vault"] as const;
19export type ToolSourceType = "mcp" | "openapi" | "graphql";75export type CredentialProvider = (typeof CREDENTIAL_PROVIDERS)[number];
20export type StorageScopeType = "scratch" | "account" | "workspace" | "organization";76
21export type StorageDurability = "ephemeral" | "durable";77export const TOOL_APPROVAL_MODES = ["auto", "required"] as const;
22export type StorageInstanceStatus = "active" | "closed" | "deleted";78export type ToolApprovalMode = (typeof TOOL_APPROVAL_MODES)[number];
23export type StorageProvider = "agentfs-local" | "agentfs-cloudflare";79
80export const TOOL_SOURCE_TYPES = ["mcp", "openapi", "graphql"] as const;
81export type ToolSourceType = (typeof TOOL_SOURCE_TYPES)[number];
82
83export const STORAGE_SCOPE_TYPES = ["scratch", "account", "workspace", "organization"] as const;
84export type StorageScopeType = (typeof STORAGE_SCOPE_TYPES)[number];
85
86export const STORAGE_DURABILITIES = ["ephemeral", "durable"] as const;
87export type StorageDurability = (typeof STORAGE_DURABILITIES)[number];
88
89export const STORAGE_INSTANCE_STATUSES = ["active", "closed", "deleted"] as const;
90export type StorageInstanceStatus = (typeof STORAGE_INSTANCE_STATUSES)[number];
91
92export const STORAGE_PROVIDERS = ["agentfs-local", "agentfs-cloudflare"] as const;
93export type StorageProvider = (typeof STORAGE_PROVIDERS)[number];
94
95export const STORAGE_ACCESS_TYPES = ["opened", "provided", "accessed"] as const;
96export type StorageAccessType = (typeof STORAGE_ACCESS_TYPES)[number];
24 97
25export type JsonSchema = Record<string, unknown>;98export type JsonSchema = Record<string, unknown>;
26 99
27export type SourceAuthType = "none" | "bearer" | "apiKey" | "basic" | "mixed";100export const SOURCE_AUTH_TYPES = ["none", "bearer", "apiKey", "basic", "mixed"] as const;
101export type SourceAuthType = (typeof SOURCE_AUTH_TYPES)[number];
28 102
29export interface SourceAuthProfile {103export interface SourceAuthProfile {
30 type: SourceAuthType;104 type: SourceAuthType;
@@ -96,7 +170,8 @@ export interface ToolCallRecord { · 65 unmodified lines
96 completedAt?: number;170 completedAt?: number;
97}171}
98 172
99export type ArgumentConditionOperator = "equals" | "contains" | "starts_with" | "not_equals";173export const ARGUMENT_CONDITION_OPERATORS = ["equals", "contains", "starts_with", "not_equals"] as const;
174export type ArgumentConditionOperator = (typeof ARGUMENT_CONDITION_OPERATORS)[number];
100 175
101export interface ArgumentCondition {176export interface ArgumentCondition {
102 key: string;177 key: string;
@@ -125,7 +200,11 @@ export interface ToolPolicyRecord { · 22 unmodified lines
125 updatedAt: number;200 updatedAt: number;
126}201}
127 202
128export type ToolRoleSelectorType = "all" | "source" | "namespace" | "tool_path";203export const TOOL_ROLE_SELECTOR_TYPES = ["all", "source", "namespace", "tool_path"] as const;
204export type ToolRoleSelectorType = (typeof TOOL_ROLE_SELECTOR_TYPES)[number];
205
206export const TOOL_ROLE_BINDING_STATUSES = ["active", "disabled"] as const;
207export type ToolRoleBindingStatus = (typeof TOOL_ROLE_BINDING_STATUSES)[number];
129 208
130export interface ToolPolicySetRecord {209export interface ToolPolicySetRecord {
131 id: string;210 id: string;
@@ -162,7 +241,7 @@ export interface ToolPolicyAssignmentRecord { · 30 unmodified lines
162 workspaceId?: Id<"workspaces">;241 workspaceId?: Id<"workspaces">;
163 targetAccountId?: Id<"accounts">;242 targetAccountId?: Id<"accounts">;
164 clientId?: string;243 clientId?: string;
165 status: "active" | "disabled";244 status: ToolRoleBindingStatus;
166 expiresAt?: number;245 expiresAt?: number;
167 createdAt: number;246 createdAt: number;
168 updatedAt: number;247 updatedAt: number;
@@ -315,7 +394,8 @@ export interface SandboxRuntime { · 146 unmodified lines
315 ): Promise<SandboxExecutionResult>;394 ): Promise<SandboxExecutionResult>;
316}395}
317 396
318export type ToolCredentialAuthType = "bearer" | "apiKey" | "basic";397export const TOOL_CREDENTIAL_AUTH_TYPES = ["bearer", "apiKey", "basic"] as const;
398export type ToolCredentialAuthType = (typeof TOOL_CREDENTIAL_AUTH_TYPES)[number];
319 399
320export interface ToolCredentialSpec {400export interface ToolCredentialSpec {
321 sourceKey: string;401 sourceKey: string;
executor/packages/database/convex/schema.ts+64−106
@@ -1,5 +1,34 @@
1import { defineSchema, defineTable } from "convex/server";1import { defineSchema, defineTable } from "convex/server";
2import { v } from "convex/values";2import { v } from "convex/values";
3import {
4 argumentConditionValidator,
5 accountProviderValidator,
6 accountStatusValidator,
7 approvalStatusValidator,
8 billingSubscriptionStatusValidator,
9 credentialProviderValidator,
10 credentialScopeTypeValidator,
11 inviteStatusValidator,
12 jsonObjectValidator,
13 orgMemberStatusValidator,
14 organizationStatusValidator,
15 orgRoleValidator,
16 policyApprovalModeValidator,
17 policyEffectValidator,
18 policyMatchTypeValidator,
19 policyScopeTypeValidator,
20 storageDurabilityValidator,
21 storageInstanceStatusValidator,
22 storageProviderValidator,
23 storageScopeTypeValidator,
24 taskStatusValidator,
25 toolApprovalModeValidator,
26 toolCallStatusValidator,
27 toolRoleBindingStatusValidator,
28 toolRoleSelectorTypeValidator,
29 toolSourceScopeTypeValidator,
30 toolSourceTypeValidator,
31} from "../src/database/validators";
3 32
4// Convex database schema.33// Convex database schema.
5//34//
@@ -10,87 +39,20 @@ import { v } from "convex/values"; · 4 unmodified lines
10// systems and in logs; `_id` stays internal to Convex.39// systems and in logs; `_id` stays internal to Convex.
11// - `accountId` links rows to `accounts` where identity context is needed.40// - `accountId` links rows to `accounts` where identity context is needed.
12//41//
13// The small validators below act like enums for schema fields.
14// Note: Some of these are duplicated as request validators under `executor/packages/database/convex/database/validators.ts`
15// and in a few feature modules. Keep the literal sets aligned to avoid drift.
16
17const accountProvider = v.union(v.literal("workos"), v.literal("anonymous"));
18const accountStatus = v.union(v.literal("active"), v.literal("deleted"));
19const organizationStatus = v.union(v.literal("active"), v.literal("deleted"));
20const orgRole = v.union(v.literal("owner"), v.literal("admin"), v.literal("member"), v.literal("billing_admin"));
21const orgMemberStatus = v.union(v.literal("active"), v.literal("pending"), v.literal("removed"));
22const billingSubscriptionStatus = v.union(
23 v.literal("incomplete"),
24 v.literal("incomplete_expired"),
25 v.literal("trialing"),
26 v.literal("active"),
27 v.literal("past_due"),
28 v.literal("canceled"),
29 v.literal("unpaid"),
30 v.literal("paused"),
31);
32const inviteStatus = v.union(
33 v.literal("pending"),
34 v.literal("accepted"),
35 v.literal("expired"),
36 v.literal("revoked"),
37 v.literal("failed"),
38);
39const taskStatus = v.union(
40 v.literal("queued"),
41 v.literal("running"),
42 v.literal("completed"),
43 v.literal("failed"),
44 v.literal("timed_out"),
45 v.literal("denied"),
46);
47const approvalStatus = v.union(v.literal("pending"), v.literal("approved"), v.literal("denied"));
48const toolCallStatus = v.union(
49 v.literal("requested"),
50 v.literal("pending_approval"),
51 v.literal("completed"),
52 v.literal("failed"),
53 v.literal("denied"),
54);
55const toolApprovalMode = v.union(v.literal("auto"), v.literal("required"));
56const policyScopeType = v.union(v.literal("account"), v.literal("organization"), v.literal("workspace"));
57const policyMatchType = v.union(v.literal("glob"), v.literal("exact"));
58const policyEffect = v.union(v.literal("allow"), v.literal("deny"));
59const policyApprovalMode = v.union(v.literal("inherit"), v.literal("auto"), v.literal("required"));
60const toolRoleSelectorType = v.union(
61 v.literal("all"),
62 v.literal("source"),
63 v.literal("namespace"),
64 v.literal("tool_path"),
65);
66const toolRoleBindingStatus = v.union(v.literal("active"), v.literal("disabled"));
67const toolSourceScopeType = v.union(v.literal("organization"), v.literal("workspace"));
68const credentialScopeType = v.union(v.literal("account"), v.literal("organization"), v.literal("workspace"));
69const storageScopeType = v.union(v.literal("scratch"), v.literal("account"), v.literal("workspace"), v.literal("organization"));
70const storageDurability = v.union(v.literal("ephemeral"), v.literal("durable"));
71const storageInstanceStatus = v.union(v.literal("active"), v.literal("closed"), v.literal("deleted"));
72const storageProvider = v.union(v.literal("agentfs-local"), v.literal("agentfs-cloudflare"));
73const credentialProvider = v.union(
74 v.literal("local-convex"),
75 v.literal("workos-vault"),
76);
77const toolSourceType = v.union(v.literal("mcp"), v.literal("openapi"), v.literal("graphql"));
78const jsonObject = v.record(v.string(), v.any());
79
80export default defineSchema({42export default defineSchema({
81 // User identities (WorkOS-backed or anonymous).43 // User identities (WorkOS-backed or anonymous).
82 //44 //
83 // Primary access patterns:45 // Primary access patterns:
84 // - Lookup by provider + providerAccountId (WorkOS user id / anon id).46 // - Lookup by provider + providerAccountId (WorkOS user id / anon id).
85 accounts: defineTable({47 accounts: defineTable({
86 provider: accountProvider,48 provider: accountProviderValidator,
87 providerAccountId: v.string(), // WorkOS user ID or anon_* UUID49 providerAccountId: v.string(), // WorkOS user ID or anon_* UUID
88 email: v.optional(v.string()),50 email: v.optional(v.string()),
89 name: v.string(),51 name: v.string(),
90 firstName: v.optional(v.string()),52 firstName: v.optional(v.string()),
91 lastName: v.optional(v.string()),53 lastName: v.optional(v.string()),
92 avatarUrl: v.optional(v.string()),54 avatarUrl: v.optional(v.string()),
93 status: accountStatus,55 status: accountStatusValidator,
94 createdAt: v.number(),56 createdAt: v.number(),
95 updatedAt: v.number(),57 updatedAt: v.number(),
96 lastLoginAt: v.optional(v.number()),58 lastLoginAt: v.optional(v.number()),
@@ -128,7 +90,7 @@ export default defineSchema({ · 31 unmodified lines
128 workosOrgId: v.optional(v.string()), // external WorkOS org ID90 workosOrgId: v.optional(v.string()), // external WorkOS org ID
129 slug: v.string(),91 slug: v.string(),
130 name: v.string(),92 name: v.string(),
131 status: organizationStatus,93 status: organizationStatusValidator,
132 createdByAccountId: v.optional(v.id("accounts")),94 createdByAccountId: v.optional(v.id("accounts")),
133 createdAt: v.number(),95 createdAt: v.number(),
134 updatedAt: v.number(),96 updatedAt: v.number(),
@@ -148,8 +110,8 @@ export default defineSchema({ · 13 unmodified lines
148 organizationMembers: defineTable({110 organizationMembers: defineTable({
149 organizationId: v.id("organizations"),111 organizationId: v.id("organizations"),
150 accountId: v.id("accounts"),112 accountId: v.id("accounts"),
151 role: orgRole,113 role: orgRoleValidator,
152 status: orgMemberStatus,114 status: orgMemberStatusValidator,
153 billable: v.boolean(),115 billable: v.boolean(),
154 invitedByAccountId: v.optional(v.id("accounts")),116 invitedByAccountId: v.optional(v.id("accounts")),
155 joinedAt: v.optional(v.number()),117 joinedAt: v.optional(v.number()),
@@ -171,8 +133,8 @@ export default defineSchema({ · 15 unmodified lines
171 organizationId: v.id("organizations"),133 organizationId: v.id("organizations"),
172 workspaceId: v.optional(v.id("workspaces")),134 workspaceId: v.optional(v.id("workspaces")),
173 email: v.string(),135 email: v.string(),
174 role: orgRole,136 role: orgRoleValidator,
175 status: inviteStatus,137 status: inviteStatusValidator,
176 providerInviteId: v.optional(v.string()), // external WorkOS invite ID138 providerInviteId: v.optional(v.string()), // external WorkOS invite ID
177 invitedByAccountId: v.id("accounts"),139 invitedByAccountId: v.id("accounts"),
178 expiresAt: v.number(),140 expiresAt: v.number(),
@@ -215,7 +177,7 @@ export default defineSchema({ · 36 unmodified lines
215 organizationId: v.id("organizations"),177 organizationId: v.id("organizations"),
216 stripeSubscriptionId: v.string(), // external Stripe subscription ID178 stripeSubscriptionId: v.string(), // external Stripe subscription ID
217 stripePriceId: v.string(), // external Stripe price ID179 stripePriceId: v.string(), // external Stripe price ID
218 status: billingSubscriptionStatus,180 status: billingSubscriptionStatusValidator,
219 currentPeriodStart: v.optional(v.number()),181 currentPeriodStart: v.optional(v.number()),
220 currentPeriodEnd: v.optional(v.number()),182 currentPeriodEnd: v.optional(v.number()),
221 cancelAtPeriodEnd: v.boolean(),183 cancelAtPeriodEnd: v.boolean(),
@@ -251,9 +213,9 @@ export default defineSchema({ · 29 unmodified lines
251 workspaceId: v.id("workspaces"),213 workspaceId: v.id("workspaces"),
252 accountId: v.optional(v.id("accounts")),214 accountId: v.optional(v.id("accounts")),
253 clientId: v.optional(v.string()), // client label: "web", "mcp", etc.215 clientId: v.optional(v.string()), // client label: "web", "mcp", etc.
254 status: taskStatus,216 status: taskStatusValidator,
255 timeoutMs: v.number(),217 timeoutMs: v.number(),
256 metadata: jsonObject,218 metadata: jsonObjectValidator,
257 nextEventSequence: v.optional(v.number()),219 nextEventSequence: v.optional(v.number()),
258 error: v.optional(v.string()),220 error: v.optional(v.string()),
259 stdout: v.optional(v.string()),221 stdout: v.optional(v.string()),
@@ -279,8 +241,8 @@ export default defineSchema({ · 19 unmodified lines
279 taskId: v.string(), // references tasks.taskId (not tasks._id)241 taskId: v.string(), // references tasks.taskId (not tasks._id)
280 workspaceId: v.id("workspaces"),242 workspaceId: v.id("workspaces"),
281 toolPath: v.string(),243 toolPath: v.string(),
282 input: jsonObject,244 input: jsonObjectValidator,
283 status: approvalStatus,245 status: approvalStatusValidator,
284 reason: v.optional(v.string()),246 reason: v.optional(v.string()),
285 reviewerId: v.optional(v.string()), // account._id or anon_<uuid>247 reviewerId: v.optional(v.string()), // account._id or anon_<uuid>
286 createdAt: v.number(),248 createdAt: v.number(),
@@ -300,7 +262,7 @@ export default defineSchema({ · 13 unmodified lines
300 callId: v.string(),262 callId: v.string(),
301 workspaceId: v.id("workspaces"),263 workspaceId: v.id("workspaces"),
302 toolPath: v.string(),264 toolPath: v.string(),
303 status: toolCallStatus,265 status: toolCallStatusValidator,
304 approvalId: v.optional(v.string()),266 approvalId: v.optional(v.string()),
305 error: v.optional(v.string()),267 error: v.optional(v.string()),
306 createdAt: v.number(),268 createdAt: v.number(),
@@ -318,7 +280,7 @@ export default defineSchema({ · 11 unmodified lines
318 taskId: v.string(), // references tasks.taskId (not tasks._id)280 taskId: v.string(), // references tasks.taskId (not tasks._id)
319 eventName: v.string(),281 eventName: v.string(),
320 type: v.string(),282 type: v.string(),
321 payload: jsonObject,283 payload: jsonObjectValidator,
322 createdAt: v.number(),284 createdAt: v.number(),
323 })285 })
324 .index("by_task_sequence", ["taskId", "sequence"]),286 .index("by_task_sequence", ["taskId", "sequence"]),
@@ -342,18 +304,14 @@ export default defineSchema({ · 17 unmodified lines
342 ruleId: v.string(), // domain ID: trule_<uuid>304 ruleId: v.string(), // domain ID: trule_<uuid>
343 roleId: v.string(),305 roleId: v.string(),
344 organizationId: v.id("organizations"),306 organizationId: v.id("organizations"),
345 selectorType: toolRoleSelectorType,307 selectorType: toolRoleSelectorTypeValidator,
346 sourceKey: v.optional(v.string()),308 sourceKey: v.optional(v.string()),
347 namespacePattern: v.optional(v.string()),309 namespacePattern: v.optional(v.string()),
348 toolPathPattern: v.optional(v.string()),310 toolPathPattern: v.optional(v.string()),
349 matchType: policyMatchType,311 matchType: policyMatchTypeValidator,
350 effect: policyEffect,312 effect: policyEffectValidator,
351 approvalMode: policyApprovalMode,313 approvalMode: policyApprovalModeValidator,
352 argumentConditions: v.optional(v.array(v.object({314 argumentConditions: v.optional(v.array(argumentConditionValidator)),
353 key: v.string(),
354 operator: v.union(v.literal("equals"), v.literal("contains"), v.literal("starts_with"), v.literal("not_equals")),
355 value: v.string(),
356 }))),
357 priority: v.number(),315 priority: v.number(),
358 createdAt: v.number(),316 createdAt: v.number(),
359 updatedAt: v.number(),317 updatedAt: v.number(),
@@ -367,11 +325,11 @@ export default defineSchema({ · 7 unmodified lines
367 bindingId: v.string(), // domain ID: trbind_<uuid>325 bindingId: v.string(), // domain ID: trbind_<uuid>
368 roleId: v.string(),326 roleId: v.string(),
369 organizationId: v.id("organizations"),327 organizationId: v.id("organizations"),
370 scopeType: policyScopeType,328 scopeType: policyScopeTypeValidator,
371 workspaceId: v.optional(v.id("workspaces")),329 workspaceId: v.optional(v.id("workspaces")),
372 targetAccountId: v.optional(v.id("accounts")),330 targetAccountId: v.optional(v.id("accounts")),
373 clientId: v.optional(v.string()),331 clientId: v.optional(v.string()),
374 status: toolRoleBindingStatus,332 status: toolRoleBindingStatusValidator,
375 expiresAt: v.optional(v.number()),333 expiresAt: v.optional(v.number()),
376 createdAt: v.number(),334 createdAt: v.number(),
377 updatedAt: v.number(),335 updatedAt: v.number(),
@@ -396,14 +354,14 @@ export default defineSchema({ · 18 unmodified lines
396 sourceCredentials: defineTable({354 sourceCredentials: defineTable({
397 bindingId: v.string(), // domain ID: bind_<uuid>355 bindingId: v.string(), // domain ID: bind_<uuid>
398 credentialId: v.string(), // domain ID: conn_<uuid>356 credentialId: v.string(), // domain ID: conn_<uuid>
399 scopeType: credentialScopeType,357 scopeType: credentialScopeTypeValidator,
400 accountId: v.optional(v.id("accounts")),358 accountId: v.optional(v.id("accounts")),
401 organizationId: v.id("organizations"),359 organizationId: v.id("organizations"),
402 workspaceId: v.optional(v.id("workspaces")),360 workspaceId: v.optional(v.id("workspaces")),
403 sourceKey: v.string(),361 sourceKey: v.string(),
404 provider: credentialProvider,362 provider: credentialProviderValidator,
405 secretJson: jsonObject,363 secretJson: jsonObjectValidator,
406 overridesJson: v.optional(jsonObject),364 overridesJson: v.optional(jsonObjectValidator),
407 boundAuthFingerprint: v.optional(v.string()),365 boundAuthFingerprint: v.optional(v.string()),
408 createdAt: v.number(),366 createdAt: v.number(),
409 updatedAt: v.number(),367 updatedAt: v.number(),
@@ -430,13 +388,13 @@ export default defineSchema({ · 20 unmodified lines
430 // - Enforce name uniqueness per owner scope.388 // - Enforce name uniqueness per owner scope.
431 toolSources: defineTable({389 toolSources: defineTable({
432 sourceId: v.string(), // domain ID: src_<uuid>390 sourceId: v.string(), // domain ID: src_<uuid>
433 scopeType: toolSourceScopeType,391 scopeType: toolSourceScopeTypeValidator,
434 organizationId: v.id("organizations"),392 organizationId: v.id("organizations"),
435 workspaceId: v.optional(v.id("workspaces")),393 workspaceId: v.optional(v.id("workspaces")),
436 name: v.string(),394 name: v.string(),
437 type: toolSourceType,395 type: toolSourceTypeValidator,
438 configVersion: v.number(),396 configVersion: v.number(),
439 config: jsonObject,397 config: jsonObjectValidator,
440 specHash: v.optional(v.string()),398 specHash: v.optional(v.string()),
441 authFingerprint: v.optional(v.string()),399 authFingerprint: v.optional(v.string()),
442 enabled: v.boolean(),400 enabled: v.boolean(),
@@ -527,7 +485,7 @@ export default defineSchema({ · 84 unmodified lines
527 normalizedPath: v.string(),485 normalizedPath: v.string(),
528 aliases: v.array(v.string()),486 aliases: v.array(v.string()),
529 description: v.string(),487 description: v.string(),
530 approval: toolApprovalMode,488 approval: toolApprovalModeValidator,
531 source: v.optional(v.string()),489 source: v.optional(v.string()),
532 searchText: v.string(),490 searchText: v.string(),
533 displayInput: v.optional(v.string()),491 displayInput: v.optional(v.string()),
@@ -577,10 +535,10 @@ export default defineSchema({ · 43 unmodified lines
577 // Rows are scope-aware and can be shared across account/workspace/org contexts.535 // Rows are scope-aware and can be shared across account/workspace/org contexts.
578 storageInstances: defineTable({536 storageInstances: defineTable({
579 instanceId: v.string(),537 instanceId: v.string(),
580 scopeType: storageScopeType,538 scopeType: storageScopeTypeValidator,
581 durability: storageDurability,539 durability: storageDurabilityValidator,
582 status: storageInstanceStatus,540 status: storageInstanceStatusValidator,
583 provider: storageProvider,541 provider: storageProviderValidator,
584 backendKey: v.string(),542 backendKey: v.string(),
585 organizationId: v.id("organizations"),543 organizationId: v.id("organizations"),
586 workspaceId: v.optional(v.id("workspaces")),544 workspaceId: v.optional(v.id("workspaces")),
@@ -626,8 +584,8 @@ export default defineSchema({ · 39 unmodified lines
626 accountLinks: defineTable({584 accountLinks: defineTable({
627 sourceAccountId: v.id("accounts"),585 sourceAccountId: v.id("accounts"),
628 targetAccountId: v.id("accounts"),586 targetAccountId: v.id("accounts"),
629 sourceProvider: accountProvider,587 sourceProvider: accountProviderValidator,
630 targetProvider: accountProvider,588 targetProvider: accountProviderValidator,
631 linkReason: v.union(v.literal("anonymous_claim")),589 linkReason: v.union(v.literal("anonymous_claim")),
632 createdAt: v.number(),590 createdAt: v.number(),
633 updatedAt: v.number(),591 updatedAt: v.number(),
executor/packages/database/package.json+1−0
@@ -10,6 +10,7 @@ · 9 unmodified lines
10 "dependencies": {10 "dependencies": {
11 "@convex-dev/rate-limiter": "^0.3.2",11 "@convex-dev/rate-limiter": "^0.3.2",
12 "convex": "^1.31.7",12 "convex": "^1.31.7",
13 "convex-helpers": "^0.1.111",
13 "sql.js": "^1.14.0"14 "sql.js": "^1.14.0"
14 },15 },
15 "main": "./convex/_generated/api.js",16 "main": "./convex/_generated/api.js",
executor/packages/database/src/database/validators.ts+73−54
@@ -1,70 +1,89 @@
1import { v } from "convex/values";1import { v } from "convex/values";
2import { literals } from "convex-helpers/validators";
3import {
4 ACCOUNT_PROVIDERS,
5 ACCOUNT_STATUSES,
6 APPROVAL_STATUSES,
7 ARGUMENT_CONDITION_OPERATORS,
8 BILLING_SUBSCRIPTION_STATUSES,
9 COMPLETED_TASK_STATUSES,
10 CREDENTIAL_PROVIDERS,
11 CREDENTIAL_SCOPE_TYPES,
12 INVITE_STATUSES,
13 ORGANIZATION_MEMBER_STATUSES,
14 ORGANIZATION_ROLES,
15 ORGANIZATION_STATUSES,
16 POLICY_APPROVAL_MODES,
17 POLICY_EFFECTS,
18 POLICY_MATCH_TYPES,
19 POLICY_RESOURCE_TYPES,
20 POLICY_SCOPE_TYPES,
21 STORAGE_ACCESS_TYPES,
22 STORAGE_DURABILITIES,
23 STORAGE_INSTANCE_STATUSES,
24 STORAGE_PROVIDERS,
25 STORAGE_SCOPE_TYPES,
26 TASK_STATUSES,
27 TERMINAL_TOOL_CALL_STATUSES,
28 TOOL_APPROVAL_MODES,
29 TOOL_CALL_STATUSES,
30 TOOL_ROLE_BINDING_STATUSES,
31 TOOL_ROLE_SELECTOR_TYPES,
32 TOOL_SOURCE_SCOPE_TYPES,
33 TOOL_SOURCE_TYPES,
34} from "../../../core/src/types";
35
36// Core enum validators shared by Convex schema and function args.
37export const accountProviderValidator = literals(...ACCOUNT_PROVIDERS);
38export const accountStatusValidator = literals(...ACCOUNT_STATUSES);
39export const organizationStatusValidator = literals(...ORGANIZATION_STATUSES);
40export const orgRoleValidator = literals(...ORGANIZATION_ROLES);
41export const orgMemberStatusValidator = literals(...ORGANIZATION_MEMBER_STATUSES);
42
43export const billingSubscriptionStatusValidator = literals(...BILLING_SUBSCRIPTION_STATUSES);
44
45export const inviteStatusValidator = literals(...INVITE_STATUSES);
46
47export const taskStatusValidator = literals(...TASK_STATUSES);
2 48
3export const jsonObjectValidator = v.record(v.string(), v.any());49export const jsonObjectValidator = v.record(v.string(), v.any());
4 50
5export const completedTaskStatusValidator = v.union(51export const completedTaskStatusValidator = literals(...COMPLETED_TASK_STATUSES);
6 v.literal("completed"),52
7 v.literal("failed"),53export const approvalStatusValidator = literals(...APPROVAL_STATUSES);
8 v.literal("timed_out"),54
9 v.literal("denied"),55export const toolCallStatusValidator = literals(...TOOL_CALL_STATUSES);
10);56
11 57export const toolApprovalModeValidator = literals(...TOOL_APPROVAL_MODES);
12export const approvalStatusValidator = v.union(v.literal("pending"), v.literal("approved"), v.literal("denied"));58
13 59export const terminalToolCallStatusValidator = literals(...TERMINAL_TOOL_CALL_STATUSES);
14export const terminalToolCallStatusValidator = v.union(60
15 v.literal("completed"),61export const policyScopeTypeValidator = literals(...POLICY_SCOPE_TYPES);
16 v.literal("failed"),62export const policyResourceTypeValidator = literals(...POLICY_RESOURCE_TYPES);
17 v.literal("denied"),63export const policyMatchTypeValidator = literals(...POLICY_MATCH_TYPES);
18);64export const policyEffectValidator = literals(...POLICY_EFFECTS);
19 65export const policyApprovalModeValidator = literals(...POLICY_APPROVAL_MODES);
20export const policyScopeTypeValidator = v.union(v.literal("account"), v.literal("organization"), v.literal("workspace"));66export const toolRoleSelectorTypeValidator = literals(...TOOL_ROLE_SELECTOR_TYPES);
21export const policyResourceTypeValidator = v.union(67export const toolRoleBindingStatusValidator = literals(...TOOL_ROLE_BINDING_STATUSES);
22 v.literal("all_tools"),68export const argumentConditionOperatorValidator = literals(...ARGUMENT_CONDITION_OPERATORS);
23 v.literal("source"),
24 v.literal("namespace"),
25 v.literal("tool_path"),
26);
27export const policyMatchTypeValidator = v.union(v.literal("glob"), v.literal("exact"));
28export const policyEffectValidator = v.union(v.literal("allow"), v.literal("deny"));
29export const policyApprovalModeValidator = v.union(v.literal("inherit"), v.literal("auto"), v.literal("required"));
30export const toolRoleSelectorTypeValidator = v.union(
31 v.literal("all"),
32 v.literal("source"),
33 v.literal("namespace"),
34 v.literal("tool_path"),
35);
36export const toolRoleBindingStatusValidator = v.union(v.literal("active"), v.literal("disabled"));
37export const argumentConditionOperatorValidator = v.union(v.literal("equals"), v.literal("contains"), v.literal("starts_with"), v.literal("not_equals"));
38export const argumentConditionValidator = v.object({69export const argumentConditionValidator = v.object({
39 key: v.string(),70 key: v.string(),
40 operator: argumentConditionOperatorValidator,71 operator: argumentConditionOperatorValidator,
41 value: v.string(),72 value: v.string(),
42});73});
43export const toolSourceScopeTypeValidator = v.union(v.literal("organization"), v.literal("workspace"));74export const toolSourceScopeTypeValidator = literals(...TOOL_SOURCE_SCOPE_TYPES);
44export const credentialScopeTypeValidator = v.union(v.literal("account"), v.literal("organization"), v.literal("workspace"));75export const credentialScopeTypeValidator = literals(...CREDENTIAL_SCOPE_TYPES);
45 76
46export const credentialProviderValidator = v.union(77export const credentialProviderValidator = literals(...CREDENTIAL_PROVIDERS);
47 v.literal("local-convex"),
48 v.literal("workos-vault"),
49);
50 78
51export const toolSourceTypeValidator = v.union(v.literal("mcp"), v.literal("openapi"), v.literal("graphql"));79export const toolSourceTypeValidator = literals(...TOOL_SOURCE_TYPES);
52 80
53export const storageScopeTypeValidator = v.union(81export const storageScopeTypeValidator = literals(...STORAGE_SCOPE_TYPES);
54 v.literal("scratch"),
55 v.literal("account"),
56 v.literal("workspace"),
57 v.literal("organization"),
58);
59 82
60export const storageDurabilityValidator = v.union(v.literal("ephemeral"), v.literal("durable"));83export const storageDurabilityValidator = literals(...STORAGE_DURABILITIES);
61 84
62export const storageInstanceStatusValidator = v.union(v.literal("active"), v.literal("closed"), v.literal("deleted"));85export const storageInstanceStatusValidator = literals(...STORAGE_INSTANCE_STATUSES);
63 86
64export const storageProviderValidator = v.union(v.literal("agentfs-local"), v.literal("agentfs-cloudflare"));87export const storageProviderValidator = literals(...STORAGE_PROVIDERS);
65 88
66export const storageAccessTypeValidator = v.union(89export const storageAccessTypeValidator = literals(...STORAGE_ACCESS_TYPES);
67 v.literal("opened"),
68 v.literal("provided"),
69 v.literal("accessed"),
70);
executor/packages/database/src/task/status.ts+5−3
@@ -1,9 +1,11 @@
1import type { TaskStatus } from "../../../core/src/types";1import { COMPLETED_TASK_STATUSES, type CompletedTaskStatus, type TaskStatus } from "../../../core/src/types";
2 2
3type TerminalTaskStatus = Extract<TaskStatus, "completed" | "failed" | "timed_out" | "denied">;3type TerminalTaskStatus = CompletedTaskStatus;
4
5const TERMINAL_TASK_STATUS_SET = new Set<TaskStatus>(COMPLETED_TASK_STATUSES);
4 6
5export function isTerminalTaskStatus(status: TaskStatus): status is TerminalTaskStatus {7export function isTerminalTaskStatus(status: TaskStatus): status is TerminalTaskStatus {
6 return status === "completed" || status === "failed" || status === "timed_out" || status === "denied";8 return TERMINAL_TASK_STATUS_SET.has(status);
7}9}
8 10
9export function taskTerminalEventType(status: TerminalTaskStatus): "task.completed" | "task.failed" | "task.timed_out" | "task.denied" {11export function taskTerminalEventType(status: TerminalTaskStatus): "task.completed" | "task.failed" | "task.timed_out" | "task.denied" {