Move to threads based system.

This commit is contained in:
Gabriel Brown
2026-06-22 10:37:26 -04:00
parent 8ae6c4b533
commit 206b64176b
82 changed files with 6169 additions and 1930 deletions
+232
View File
@@ -219,6 +219,7 @@ const applicationTables = {
syncRuns: defineTable({
spoonId: v.id('spoons'),
ownerId: v.id('users'),
threadId: v.optional(v.id('threads')),
kind: v.union(
v.literal('scheduled_check'),
v.literal('manual_check'),
@@ -241,6 +242,14 @@ const applicationTables = {
aiAssessment: v.optional(v.string()),
mergeRequestUrl: v.optional(v.string()),
error: v.optional(v.string()),
decision: v.optional(
v.union(
v.literal('auto_synced'),
v.literal('thread_created'),
v.literal('ignored'),
v.literal('failed'),
),
),
createdAt: v.number(),
updatedAt: v.number(),
})
@@ -339,6 +348,46 @@ const applicationTables = {
})
.index('by_user', ['userId'])
.index('by_user_provider', ['userId', 'provider']),
aiProviderProfiles: defineTable({
ownerId: v.id('users'),
name: v.string(),
provider: v.union(
v.literal('openai'),
v.literal('anthropic'),
v.literal('google'),
v.literal('openrouter'),
v.literal('requesty'),
v.literal('litellm'),
v.literal('cloudflare_ai_gateway'),
v.literal('custom_openai_compatible'),
v.literal('opencode_openai_login'),
),
authType: v.union(
v.literal('api_key'),
v.literal('opencode_auth_json'),
v.literal('none'),
),
encryptedSecret: v.optional(v.string()),
secretPreview: v.optional(v.string()),
baseUrl: v.optional(v.string()),
defaultModel: v.string(),
modelOptions: v.optional(v.array(v.string())),
reasoningEffort: v.union(
v.literal('none'),
v.literal('minimal'),
v.literal('low'),
v.literal('medium'),
v.literal('high'),
v.literal('xhigh'),
),
enabled: v.boolean(),
isDefault: v.optional(v.boolean()),
createdAt: v.number(),
updatedAt: v.number(),
})
.index('by_owner', ['ownerId'])
.index('by_owner_provider', ['ownerId', 'provider'])
.index('by_owner_enabled', ['ownerId', 'enabled']),
agentRequests: defineTable({
spoonId: v.id('spoons'),
ownerId: v.id('users'),
@@ -395,6 +444,9 @@ const applicationTables = {
spoonId: v.id('spoons'),
ownerId: v.id('users'),
enabled: v.boolean(),
runtime: v.optional(
v.union(v.literal('opencode'), v.literal('openai_direct')),
),
defaultBaseBranch: v.optional(v.string()),
branchPrefix: v.string(),
installCommand: v.optional(v.string()),
@@ -411,6 +463,20 @@ const applicationTables = {
),
maxJobDurationMs: v.number(),
maxOutputBytes: v.number(),
envFilePath: v.optional(
v.union(
v.literal('.env'),
v.literal('.env.local'),
v.literal('.env.production'),
v.literal('.env.production.local'),
v.literal('custom'),
),
),
customEnvFilePath: v.optional(v.string()),
materializeEnvFileByDefault: v.optional(v.boolean()),
autoDetectCommands: v.optional(v.boolean()),
allowUserFileEditing: v.optional(v.boolean()),
aiProviderProfileId: v.optional(v.id('aiProviderProfiles')),
createdAt: v.number(),
updatedAt: v.number(),
})
@@ -420,6 +486,14 @@ const applicationTables = {
spoonId: v.id('spoons'),
ownerId: v.id('users'),
agentRequestId: v.id('agentRequests'),
threadId: v.optional(v.id('threads')),
jobType: v.optional(
v.union(
v.literal('user_change'),
v.literal('maintenance_review'),
v.literal('conflict_resolution'),
),
),
status: v.union(
v.literal('queued'),
v.literal('claimed'),
@@ -433,8 +507,29 @@ const applicationTables = {
v.literal('timed_out'),
),
prompt: v.string(),
runtime: v.optional(
v.union(v.literal('openai_direct'), v.literal('opencode')),
),
workspaceStatus: v.optional(
v.union(
v.literal('not_started'),
v.literal('starting'),
v.literal('active'),
v.literal('idle'),
v.literal('stopped'),
v.literal('expired'),
v.literal('failed'),
),
),
baseBranch: v.string(),
workBranch: v.string(),
opencodeSessionId: v.optional(v.string()),
containerId: v.optional(v.string()),
workspaceUrl: v.optional(v.string()),
workspaceExpiresAt: v.optional(v.number()),
lastHeartbeatAt: v.optional(v.number()),
envFilePath: v.optional(v.string()),
materializeEnvFile: v.optional(v.boolean()),
githubInstallationId: v.optional(v.string()),
forkOwner: v.string(),
forkRepo: v.string(),
@@ -442,6 +537,7 @@ const applicationTables = {
upstreamOwner: v.string(),
upstreamRepo: v.string(),
selectedSecretIds: v.array(v.id('spoonSecrets')),
aiProviderProfileId: v.optional(v.id('aiProviderProfiles')),
model: v.string(),
reasoningEffort: v.union(
v.literal('none'),
@@ -468,6 +564,50 @@ const applicationTables = {
.index('by_request', ['agentRequestId'])
.index('by_status', ['status'])
.index('by_claim', ['status', 'createdAt']),
agentJobMessages: defineTable({
jobId: v.id('agentJobs'),
spoonId: v.id('spoons'),
ownerId: v.id('users'),
role: v.union(
v.literal('user'),
v.literal('assistant'),
v.literal('system'),
v.literal('tool'),
),
content: v.string(),
status: v.union(
v.literal('queued'),
v.literal('streaming'),
v.literal('completed'),
v.literal('failed'),
),
metadata: v.optional(v.string()),
createdAt: v.number(),
updatedAt: v.number(),
})
.index('by_job', ['jobId'])
.index('by_owner', ['ownerId']),
agentWorkspaceChanges: defineTable({
jobId: v.id('agentJobs'),
spoonId: v.id('spoons'),
ownerId: v.id('users'),
path: v.string(),
source: v.union(
v.literal('user'),
v.literal('agent'),
v.literal('command'),
),
changeType: v.union(
v.literal('added'),
v.literal('modified'),
v.literal('deleted'),
v.literal('renamed'),
),
diff: v.optional(v.string()),
createdAt: v.number(),
})
.index('by_job', ['jobId'])
.index('by_path', ['jobId', 'path']),
agentJobEvents: defineTable({
jobId: v.id('agentJobs'),
spoonId: v.id('spoons'),
@@ -523,6 +663,98 @@ const applicationTables = {
.index('by_job', ['jobId'])
.index('by_spoon', ['spoonId'])
.index('by_owner', ['ownerId']),
threads: defineTable({
ownerId: v.id('users'),
spoonId: v.optional(v.id('spoons')),
title: v.string(),
summary: v.optional(v.string()),
source: v.union(
v.literal('user_request'),
v.literal('upstream_update'),
v.literal('merge_conflict'),
v.literal('manual_review'),
v.literal('system'),
),
status: v.union(
v.literal('open'),
v.literal('queued'),
v.literal('running'),
v.literal('waiting_for_user'),
v.literal('changes_ready'),
v.literal('draft_pr_opened'),
v.literal('resolved'),
v.literal('ignored'),
v.literal('failed'),
v.literal('cancelled'),
),
priority: v.union(v.literal('low'), v.literal('normal'), v.literal('high')),
upstreamFrom: v.optional(v.string()),
upstreamTo: v.optional(v.string()),
forkHeadAtCreation: v.optional(v.string()),
mergeBaseAtCreation: v.optional(v.string()),
relatedSyncRunId: v.optional(v.id('syncRuns')),
relatedAgentRequestId: v.optional(v.id('agentRequests')),
latestAgentJobId: v.optional(v.id('agentJobs')),
maintenanceOutcome: v.optional(
v.union(
v.literal('auto_synced'),
v.literal('sync_recommended'),
v.literal('ignored'),
v.literal('review_pr_recommended'),
v.literal('manual_review_required'),
v.literal('conflict_resolution_required'),
v.literal('failed'),
v.literal('unknown'),
),
),
ignoredCommitShas: v.optional(v.array(v.string())),
ignoredReason: v.optional(v.string()),
createdAt: v.number(),
updatedAt: v.number(),
resolvedAt: v.optional(v.number()),
})
.index('by_owner', ['ownerId'])
.index('by_owner_status', ['ownerId', 'status'])
.index('by_spoon', ['spoonId'])
.index('by_source', ['ownerId', 'source'])
.index('by_created', ['createdAt']),
threadMessages: defineTable({
threadId: v.id('threads'),
ownerId: v.id('users'),
spoonId: v.optional(v.id('spoons')),
role: v.union(
v.literal('user'),
v.literal('assistant'),
v.literal('system'),
v.literal('tool'),
),
content: v.string(),
status: v.union(
v.literal('queued'),
v.literal('streaming'),
v.literal('completed'),
v.literal('failed'),
),
metadata: v.optional(v.string()),
createdAt: v.number(),
updatedAt: v.number(),
})
.index('by_thread', ['threadId'])
.index('by_owner', ['ownerId']),
ignoredUpstreamChanges: defineTable({
spoonId: v.id('spoons'),
ownerId: v.id('users'),
upstreamFrom: v.optional(v.string()),
upstreamTo: v.string(),
commitShas: v.array(v.string()),
reason: v.string(),
decidedBy: v.union(v.literal('agent'), v.literal('user')),
threadId: v.optional(v.id('threads')),
createdAt: v.number(),
})
.index('by_spoon', ['spoonId'])
.index('by_owner', ['ownerId'])
.index('by_upstream_to', ['spoonId', 'upstreamTo']),
};
export default defineSchema({