684 lines
22 KiB
TypeScript
684 lines
22 KiB
TypeScript
import { defineSchema, defineTable } from "convex/server";
|
|
import { v } from "convex/values";
|
|
|
|
/* eslint-disable sort-keys */
|
|
|
|
const attemptClassification = v.union(
|
|
v.literal("Succeeded"),
|
|
v.literal("RetryableFailure"),
|
|
v.literal("NeedsInput"),
|
|
v.literal("Blocked"),
|
|
v.literal("VerificationFailed"),
|
|
v.literal("BudgetExhausted"),
|
|
v.literal("Cancelled"),
|
|
v.literal("PermanentFailure")
|
|
);
|
|
const workStatus = v.union(
|
|
v.literal("proposed"),
|
|
v.literal("defining"),
|
|
v.literal("awaiting-definition-approval"),
|
|
v.literal("designing"),
|
|
v.literal("awaiting-design-approval"),
|
|
v.literal("ready"),
|
|
v.literal("executing"),
|
|
v.literal("needs-input"),
|
|
v.literal("blocked"),
|
|
v.literal("completed"),
|
|
v.literal("failed"),
|
|
v.literal("cancelled")
|
|
);
|
|
|
|
export default defineSchema({
|
|
organizations: defineTable({
|
|
createdAt: v.number(),
|
|
createdBy: v.string(),
|
|
kind: v.union(v.literal("personal"), v.literal("team")),
|
|
name: v.string(),
|
|
}).index("by_createdBy_and_kind", ["createdBy", "kind"]),
|
|
organizationMembers: defineTable({
|
|
createdAt: v.number(),
|
|
organizationId: v.id("organizations"),
|
|
role: v.union(v.literal("owner"), v.literal("member")),
|
|
userId: v.string(),
|
|
})
|
|
.index("by_userId", ["userId"])
|
|
.index("by_organizationId", ["organizationId"])
|
|
.index("by_organizationId_and_userId", ["organizationId", "userId"]),
|
|
gitConnections: defineTable({
|
|
connectedAt: v.number(),
|
|
credentialCiphertext: v.string(),
|
|
credentialIv: v.string(),
|
|
credentialKind: v.union(v.literal("oauth"), v.literal("token")),
|
|
organizationId: v.id("organizations"),
|
|
provider: v.union(v.literal("github"), v.literal("gitea")),
|
|
serverUrl: v.string(),
|
|
updatedAt: v.number(),
|
|
username: v.optional(v.string()),
|
|
})
|
|
.index("by_organizationId", ["organizationId"])
|
|
.index("by_organizationId_and_provider_and_serverUrl", [
|
|
"organizationId",
|
|
"provider",
|
|
"serverUrl",
|
|
]),
|
|
projects: defineTable({
|
|
createdAt: v.number(),
|
|
defaultBranch: v.optional(v.string()),
|
|
description: v.optional(v.string()),
|
|
gitConnectionId: v.optional(v.id("gitConnections")),
|
|
name: v.string(),
|
|
normalizedSourceUrl: v.string(),
|
|
organizationId: v.id("organizations"),
|
|
repositoryPath: v.string(),
|
|
sourceHost: v.string(),
|
|
sourceUrl: v.string(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index("by_organizationId_and_createdAt", ["organizationId", "createdAt"])
|
|
.index("by_organizationId_and_normalizedSourceUrl", [
|
|
"organizationId",
|
|
"normalizedSourceUrl",
|
|
]),
|
|
projectContextDocuments: defineTable({
|
|
content: v.string(),
|
|
createdAt: v.number(),
|
|
kind: v.union(
|
|
v.literal("readme"),
|
|
v.literal("agents"),
|
|
v.literal("product"),
|
|
v.literal("business"),
|
|
v.literal("design"),
|
|
v.literal("tech")
|
|
),
|
|
origin: v.literal("repository"),
|
|
path: v.string(),
|
|
projectId: v.id("projects"),
|
|
sourceUrl: v.string(),
|
|
updatedAt: v.number(),
|
|
}).index("by_projectId_and_path", ["projectId", "path"]),
|
|
conversations: defineTable({
|
|
createdAt: v.number(),
|
|
organizationId: v.id("organizations"),
|
|
}).index("by_organizationId", ["organizationId"]),
|
|
conversationTurns: defineTable({
|
|
attemptNumber: v.optional(v.number()),
|
|
clientRequestId: v.string(),
|
|
completedAt: v.optional(v.number()),
|
|
conversationId: v.id("conversations"),
|
|
createdAt: v.number(),
|
|
error: v.optional(v.string()),
|
|
leaseExpiresAt: v.optional(v.number()),
|
|
leaseOwner: v.optional(v.string()),
|
|
status: v.union(
|
|
v.literal("queued"),
|
|
v.literal("dispatching"),
|
|
v.literal("running"),
|
|
v.literal("completed"),
|
|
v.literal("failed"),
|
|
v.literal("aborted")
|
|
),
|
|
submissionId: v.optional(v.string()),
|
|
})
|
|
.index("by_conversationId_and_clientRequestId", [
|
|
"conversationId",
|
|
"clientRequestId",
|
|
])
|
|
.index("by_status_and_leaseExpiresAt", ["status", "leaseExpiresAt"])
|
|
.index("by_submissionId", ["submissionId"]),
|
|
conversationMessages: defineTable({
|
|
content: v.string(),
|
|
conversationId: v.id("conversations"),
|
|
createdAt: v.number(),
|
|
ordinal: v.number(),
|
|
role: v.union(v.literal("user"), v.literal("assistant")),
|
|
turnId: v.id("conversationTurns"),
|
|
})
|
|
.index("by_conversationId_and_ordinal", ["conversationId", "ordinal"])
|
|
.index("by_turnId_and_role", ["turnId", "role"]),
|
|
conversationAttachments: defineTable({
|
|
createdAt: v.number(),
|
|
filename: v.optional(v.string()),
|
|
messageId: v.id("conversationMessages"),
|
|
mimeType: v.string(),
|
|
storageId: v.id("_storage"),
|
|
}).index("by_messageId", ["messageId"]),
|
|
signals: defineTable({
|
|
conversationId: v.id("conversations"),
|
|
createdAt: v.number(),
|
|
desiredOutcome: v.string(),
|
|
organizationId: v.id("organizations"),
|
|
processedByAgentInstanceId: v.string(),
|
|
processedByAgentName: v.string(),
|
|
projectId: v.id("projects"),
|
|
sourceKey: v.string(),
|
|
summary: v.string(),
|
|
title: v.string(),
|
|
})
|
|
.index("by_organization_and_createdAt", ["organizationId", "createdAt"])
|
|
.index("by_organization_and_sourceKey", ["organizationId", "sourceKey"])
|
|
.index("by_project_and_createdAt", ["projectId", "createdAt"]),
|
|
signalConstraints: defineTable({
|
|
ordinal: v.number(),
|
|
signalId: v.id("signals"),
|
|
value: v.string(),
|
|
}).index("by_signalId_and_ordinal", ["signalId", "ordinal"]),
|
|
signalSources: defineTable({
|
|
messageId: v.id("conversationMessages"),
|
|
ordinal: v.number(),
|
|
rawTextSnapshot: v.string(),
|
|
signalId: v.id("signals"),
|
|
sourceCreatedAt: v.number(),
|
|
})
|
|
.index("by_signalId_and_ordinal", ["signalId", "ordinal"])
|
|
.index("by_messageId", ["messageId"]),
|
|
works: defineTable({
|
|
createdAt: v.number(),
|
|
definitionApprovalVersion: v.optional(v.number()),
|
|
definitionVersion: v.optional(v.number()),
|
|
designApprovalVersion: v.optional(v.number()),
|
|
designVersion: v.optional(v.number()),
|
|
objective: v.string(),
|
|
organizationId: v.id("organizations"),
|
|
projectId: v.id("projects"),
|
|
status: workStatus,
|
|
title: v.string(),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index("by_project_and_createdAt", ["projectId", "createdAt"])
|
|
.index("by_organization_and_createdAt", ["organizationId", "createdAt"]),
|
|
|
|
signalWorkAttachments: defineTable({
|
|
createdAt: v.number(),
|
|
signalId: v.id("signals"),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_signal", ["signalId"])
|
|
.index("by_work", ["workId"])
|
|
.index("by_signal_and_work", ["signalId", "workId"]),
|
|
|
|
workEvents: defineTable({
|
|
createdAt: v.number(),
|
|
idempotencyKey: v.string(),
|
|
kind: v.union(
|
|
v.literal("work.proposed"),
|
|
v.literal("signal.attached"),
|
|
v.literal("definition.requested"),
|
|
v.literal("definition.saved"),
|
|
v.literal("definition.revised"),
|
|
v.literal("definition.approved"),
|
|
v.literal("definition.invalidated"),
|
|
v.literal("question.created"),
|
|
v.literal("question.answered"),
|
|
v.literal("question.withdrawn"),
|
|
v.literal("design.requested"),
|
|
v.literal("design.saved"),
|
|
v.literal("design.revised"),
|
|
v.literal("design.approved"),
|
|
v.literal("design.invalidated"),
|
|
v.literal("planner.failed"),
|
|
v.literal("slice.started"),
|
|
v.literal("slice.completed"),
|
|
v.literal("slice.ready"),
|
|
v.literal("run.started"),
|
|
v.literal("run.completed"),
|
|
v.literal("run.cancelled"),
|
|
v.literal("attempt.claimed"),
|
|
v.literal("attempt.event"),
|
|
v.literal("attempt.completed"),
|
|
v.literal("attempt.reconciled"),
|
|
v.literal("resolver.decided"),
|
|
v.literal("artifact.recorded"),
|
|
v.literal("delivery.recorded"),
|
|
v.literal("delivery.updated")
|
|
),
|
|
payloadJson: v.optional(v.string()),
|
|
referenceId: v.optional(v.string()),
|
|
signalId: v.optional(v.id("signals")),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_work_and_createdAt", ["workId", "createdAt"])
|
|
.index("by_work_and_idempotencyKey", ["workId", "idempotencyKey"]),
|
|
|
|
workDefinitions: defineTable({
|
|
createdAt: v.number(),
|
|
createdBy: v.string(),
|
|
payloadJson: v.string(),
|
|
risk: v.union(v.literal("low"), v.literal("medium"), v.literal("high")),
|
|
status: v.union(
|
|
v.literal("proposed"),
|
|
v.literal("current"),
|
|
v.literal("superseded")
|
|
),
|
|
version: v.number(),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_work_and_version", ["workId", "version"])
|
|
.index("by_work_and_status", ["workId", "status"]),
|
|
|
|
workQuestions: defineTable({
|
|
alternativesJson: v.string(),
|
|
answer: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
definitionVersion: v.number(),
|
|
impact: v.union(v.literal("low"), v.literal("medium"), v.literal("high")),
|
|
prompt: v.string(),
|
|
questionId: v.string(),
|
|
recommendation: v.optional(v.string()),
|
|
status: v.union(
|
|
v.literal("open"),
|
|
v.literal("answered"),
|
|
v.literal("withdrawn")
|
|
),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_workId_and_definitionVersion", ["workId", "definitionVersion"])
|
|
.index("by_workId_and_definitionVersion_and_questionId", [
|
|
"workId",
|
|
"definitionVersion",
|
|
"questionId",
|
|
]),
|
|
|
|
workApprovals: defineTable({
|
|
approvedAt: v.number(),
|
|
approvedBy: v.string(),
|
|
definitionVersion: v.number(),
|
|
designVersion: v.optional(v.number()),
|
|
kind: v.union(v.literal("definition"), v.literal("design")),
|
|
status: v.union(v.literal("active"), v.literal("invalidated")),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_workId_and_kind", ["workId", "kind"])
|
|
.index("by_workId_and_kind_and_definitionVersion_and_designVersion", [
|
|
"workId",
|
|
"kind",
|
|
"definitionVersion",
|
|
"designVersion",
|
|
]),
|
|
|
|
designPackets: defineTable({
|
|
createdAt: v.number(),
|
|
createdBy: v.string(),
|
|
definitionVersion: v.number(),
|
|
payloadJson: v.string(),
|
|
status: v.union(
|
|
v.literal("proposed"),
|
|
v.literal("current"),
|
|
v.literal("superseded")
|
|
),
|
|
version: v.number(),
|
|
workId: v.id("works"),
|
|
}).index("by_work_and_version", ["workId", "version"]),
|
|
|
|
workSlices: defineTable({
|
|
createdAt: v.optional(v.number()),
|
|
designVersion: v.number(),
|
|
objective: v.string(),
|
|
observableBehavior: v.string(),
|
|
ordinal: v.number(),
|
|
payloadJson: v.string(),
|
|
sliceId: v.string(),
|
|
status: v.union(
|
|
v.literal("planned"),
|
|
v.literal("ready"),
|
|
v.literal("running"),
|
|
v.literal("completed"),
|
|
v.literal("blocked")
|
|
),
|
|
title: v.string(),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_workId_and_designVersion", ["workId", "designVersion"])
|
|
.index("by_workId_and_designVersion_and_sliceId", [
|
|
"workId",
|
|
"designVersion",
|
|
"sliceId",
|
|
]),
|
|
|
|
workRuns: defineTable({
|
|
baseRevision: v.optional(v.string()),
|
|
candidateRevision: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
designVersion: v.optional(v.number()),
|
|
endedAt: v.optional(v.number()),
|
|
kitId: v.string(),
|
|
kitVersion: v.string(),
|
|
environmentId: v.optional(v.string()),
|
|
executionKind: v.optional(
|
|
v.union(v.literal("simulated"), v.literal("real"))
|
|
),
|
|
scenario: v.union(
|
|
v.literal("success"),
|
|
v.literal("transient-failure-then-success"),
|
|
v.literal("needs-input"),
|
|
v.literal("permanent-failure"),
|
|
v.literal("cancelled")
|
|
),
|
|
sliceId: v.optional(v.string()),
|
|
sliceRowId: v.optional(v.id("workSlices")),
|
|
startedAt: v.optional(v.number()),
|
|
status: v.union(
|
|
v.literal("ready"),
|
|
v.literal("running"),
|
|
v.literal("terminal"),
|
|
v.literal("cancelled")
|
|
),
|
|
terminalClassification: v.optional(attemptClassification),
|
|
terminalSummary: v.optional(v.string()),
|
|
workflowId: v.optional(v.string()),
|
|
workId: v.id("works"),
|
|
}).index("by_work_and_createdAt", ["workId", "createdAt"]),
|
|
|
|
workAttempts: defineTable({
|
|
classification: v.optional(attemptClassification),
|
|
failureReason: v.optional(
|
|
v.union(
|
|
v.literal("Authentication"),
|
|
v.literal("Cancelled"),
|
|
v.literal("HarnessFailed"),
|
|
v.literal("InvalidInput"),
|
|
v.literal("ProviderUnavailable"),
|
|
v.literal("RepositoryFailed"),
|
|
v.literal("Timeout")
|
|
)
|
|
),
|
|
endedAt: v.optional(v.number()),
|
|
leaseExpiresAt: v.optional(v.number()),
|
|
leaseOwner: v.optional(v.string()),
|
|
number: v.number(),
|
|
runId: v.id("workRuns"),
|
|
startedAt: v.optional(v.number()),
|
|
status: v.union(
|
|
v.literal("queued"),
|
|
v.literal("claimed"),
|
|
v.literal("running"),
|
|
v.literal("terminal")
|
|
),
|
|
summary: v.optional(v.string()),
|
|
workId: v.id("works"),
|
|
workspaceKey: v.optional(v.string()),
|
|
})
|
|
.index("by_runId_and_number", ["runId", "number"])
|
|
.index("by_status_and_leaseExpiresAt", ["status", "leaseExpiresAt"]),
|
|
|
|
workAttemptEvents: defineTable({
|
|
attemptId: v.id("workAttempts"),
|
|
kind: v.string(),
|
|
message: v.string(),
|
|
metadataJson: v.string(),
|
|
occurredAt: v.number(),
|
|
sequence: v.number(),
|
|
}).index("by_attempt_and_sequence", ["attemptId", "sequence"]),
|
|
|
|
resolverDecisions: defineTable({
|
|
attemptId: v.id("workAttempts"),
|
|
attemptNumber: v.number(),
|
|
classification: attemptClassification,
|
|
createdAt: v.number(),
|
|
decision: v.union(v.literal("retry"), v.literal("terminal")),
|
|
resultingWorkStatus: v.optional(workStatus),
|
|
runId: v.id("workRuns"),
|
|
summary: v.string(),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_runId_and_attemptNumber", ["runId", "attemptNumber"])
|
|
.index("by_attemptId", ["attemptId"]),
|
|
|
|
workArtifacts: defineTable({
|
|
attemptId: v.optional(v.id("workAttempts")),
|
|
contentHash: v.optional(v.string()),
|
|
createdAt: v.number(),
|
|
designVersion: v.optional(v.number()),
|
|
environmentId: v.optional(v.string()),
|
|
idempotencyKey: v.string(),
|
|
kind: v.union(
|
|
v.literal("definition"),
|
|
v.literal("design"),
|
|
v.literal("diff"),
|
|
v.literal("test-report"),
|
|
v.literal("verification-report"),
|
|
v.literal("runtime-log"),
|
|
v.literal("screenshot"),
|
|
v.literal("video"),
|
|
v.literal("commit"),
|
|
v.literal("branch"),
|
|
v.literal("pull-request"),
|
|
v.literal("preview"),
|
|
v.literal("deployment"),
|
|
v.literal("other")
|
|
),
|
|
metadataJson: v.string(),
|
|
organizationId: v.id("organizations"),
|
|
producer: v.string(),
|
|
projectId: v.id("projects"),
|
|
provenanceJson: v.string(),
|
|
runId: v.optional(v.id("workRuns")),
|
|
sliceId: v.optional(v.string()),
|
|
sourceRevision: v.optional(v.string()),
|
|
title: v.string(),
|
|
uri: v.optional(v.string()),
|
|
verificationStatus: v.union(
|
|
v.literal("unverified"),
|
|
v.literal("verified"),
|
|
v.literal("rejected")
|
|
),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_workId_and_createdAt", ["workId", "createdAt"])
|
|
.index("by_workId_and_idempotencyKey", ["workId", "idempotencyKey"])
|
|
.index("by_runId_and_createdAt", ["runId", "createdAt"]),
|
|
|
|
workDeliveries: defineTable({
|
|
artifactId: v.optional(v.id("workArtifacts")),
|
|
createdAt: v.number(),
|
|
externalId: v.string(),
|
|
idempotencyKey: v.string(),
|
|
kind: v.union(
|
|
v.literal("branch"),
|
|
v.literal("commit"),
|
|
v.literal("pull-request"),
|
|
v.literal("preview"),
|
|
v.literal("deployment")
|
|
),
|
|
metadataJson: v.string(),
|
|
organizationId: v.id("organizations"),
|
|
projectId: v.id("projects"),
|
|
provider: v.string(),
|
|
sourceRevision: v.string(),
|
|
status: v.union(
|
|
v.literal("recorded"),
|
|
v.literal("ready"),
|
|
v.literal("approved"),
|
|
v.literal("delivered"),
|
|
v.literal("failed"),
|
|
v.literal("cancelled")
|
|
),
|
|
target: v.string(),
|
|
updatedAt: v.number(),
|
|
url: v.optional(v.string()),
|
|
workId: v.id("works"),
|
|
})
|
|
.index("by_workId_and_createdAt", ["workId", "createdAt"])
|
|
.index("by_workId_and_idempotencyKey", ["workId", "idempotencyKey"]),
|
|
|
|
// -----------------------------------------------------------------
|
|
// Flue persistence stores (schema/format version 4).
|
|
// -----------------------------------------------------------------
|
|
|
|
// One-row key/value metadata table, keyed with `schema_version` for the
|
|
// persisted-store versioning obligation.
|
|
flueMeta: defineTable({
|
|
key: v.string(),
|
|
value: v.string(),
|
|
}).index("by_key", ["key"]),
|
|
|
|
// Durable agent-submission lifecycle: one row per submission. Settlement
|
|
// obligations live on the same row so reserve/finalize are atomic with the
|
|
// status transition they gate.
|
|
flueSubmissions: defineTable({
|
|
submissionId: v.string(),
|
|
sessionKey: v.string(),
|
|
sequence: v.number(),
|
|
kind: v.union(v.literal("dispatch"), v.literal("direct")),
|
|
// Client-supplied canonical input/chunks encoded by Flue helpers; exact
|
|
// string compared on idempotent replay.
|
|
inputJson: v.string(),
|
|
chunksJson: v.string(),
|
|
traceCarrierJson: v.optional(v.string()),
|
|
// Admit-time canonical record (Flue ConversationCreatedRecord) for the
|
|
// default session of the default harness.
|
|
recordJson: v.optional(v.string()),
|
|
status: v.union(
|
|
v.literal("queued"),
|
|
v.literal("running"),
|
|
v.literal("terminalizing"),
|
|
v.literal("settled")
|
|
),
|
|
acceptedAt: v.number(),
|
|
canonicalReadyAt: v.optional(v.number()),
|
|
attemptId: v.optional(v.string()),
|
|
inputAppliedAt: v.optional(v.number()),
|
|
recoveryRequestedAt: v.optional(v.number()),
|
|
abortRequestedAt: v.optional(v.number()),
|
|
startedAt: v.optional(v.number()),
|
|
error: v.optional(v.string()),
|
|
attemptCount: v.number(),
|
|
maxRetry: v.number(),
|
|
timeoutAt: v.number(),
|
|
ownerId: v.optional(v.string()),
|
|
leaseExpiresAt: v.number(),
|
|
// Settlement obligation (reserved/finalized on this row).
|
|
settlementRecordId: v.optional(v.string()),
|
|
settlementRecordJson: v.optional(v.string()),
|
|
settledOutcome: v.optional(
|
|
v.union(v.literal("completed"), v.literal("failed"), v.literal("aborted"))
|
|
),
|
|
updatedAt: v.number(),
|
|
})
|
|
.index("by_submissionId", ["submissionId"])
|
|
.index("by_sessionKey_and_sequence", ["sessionKey", "sequence"])
|
|
.index("by_status_and_sequence", ["status", "sequence"])
|
|
.index("by_status_and_leaseExpiresAt", ["status", "leaseExpiresAt"])
|
|
.index("by_status_and_settlementRecordId", [
|
|
"status",
|
|
"settlementRecordId",
|
|
]),
|
|
|
|
// Durable evidence that a submission attempt started and has not yet
|
|
// settled. Append-once by (submissionId, attemptId).
|
|
flueAttemptMarkers: defineTable({
|
|
attemptId: v.string(),
|
|
createdAt: v.number(),
|
|
submissionId: v.string(),
|
|
})
|
|
.index("by_submissionId_and_attemptId", ["submissionId", "attemptId"])
|
|
.index("by_submissionId", ["submissionId"]),
|
|
|
|
// Conversation-stream metadata: one row per stream path.
|
|
flueConversationStreams: defineTable({
|
|
closed: v.boolean(),
|
|
createdAt: v.number(),
|
|
identityJson: v.string(),
|
|
incarnation: v.string(),
|
|
nextOffset: v.number(),
|
|
nextProducerSequence: v.number(),
|
|
path: v.string(),
|
|
producerEpoch: v.number(),
|
|
producerId: v.optional(v.string()),
|
|
}).index("by_path", ["path"]),
|
|
|
|
// Conversation-stream batches: one row per appended batch. Offset is
|
|
// 0-based; `seq` is the row's position in the stream.
|
|
flueConversationBatches: defineTable({
|
|
appendedAt: v.number(),
|
|
attemptId: v.optional(v.string()),
|
|
path: v.string(),
|
|
producerEpoch: v.number(),
|
|
producerId: v.string(),
|
|
producerSequence: v.number(),
|
|
recordsJson: v.string(),
|
|
seq: v.number(),
|
|
submissionId: v.optional(v.string()),
|
|
})
|
|
.index("by_path_and_seq", ["path", "seq"])
|
|
.index("by_path_producer_epoch_producerSequence", [
|
|
"path",
|
|
"producerId",
|
|
"producerEpoch",
|
|
"producerSequence",
|
|
]),
|
|
|
|
// Event-stream metadata: one row per stream path.
|
|
flueEventStreams: defineTable({
|
|
closed: v.boolean(),
|
|
createdAt: v.number(),
|
|
nextSeq: v.number(),
|
|
path: v.string(),
|
|
}).index("by_path", ["path"]),
|
|
|
|
// Event-stream entries: one row per appended event. `onceKey` carries the
|
|
// idempotency key for `appendEventOnce` and is null for plain appends.
|
|
flueEventEntries: defineTable({
|
|
appendedAt: v.number(),
|
|
dataJson: v.string(),
|
|
onceKey: v.optional(v.string()),
|
|
path: v.string(),
|
|
seq: v.number(),
|
|
})
|
|
.index("by_path_and_seq", ["path", "seq"])
|
|
.index("by_path_and_onceKey", ["path", "onceKey"]),
|
|
|
|
// Workflow run records.
|
|
flueRuns: defineTable({
|
|
durationMs: v.optional(v.number()),
|
|
endedAt: v.optional(v.string()),
|
|
errorJson: v.optional(v.string()),
|
|
inputJson: v.optional(v.string()),
|
|
isError: v.optional(v.boolean()),
|
|
resultJson: v.optional(v.string()),
|
|
runId: v.string(),
|
|
startedAt: v.string(),
|
|
status: v.union(
|
|
v.literal("active"),
|
|
v.literal("completed"),
|
|
v.literal("errored")
|
|
),
|
|
traceCarrierJson: v.optional(v.string()),
|
|
workflowName: v.string(),
|
|
})
|
|
.index("by_runId", ["runId"])
|
|
.index("by_startedAt_and_runId", ["startedAt", "runId"])
|
|
.index("by_workflowName_and_startedAt_and_runId", [
|
|
"workflowName",
|
|
"startedAt",
|
|
"runId",
|
|
])
|
|
.index("by_status_startedAt_runId", ["status", "startedAt", "runId"])
|
|
.index("by_status_workflowName_startedAt_runId", [
|
|
"status",
|
|
"workflowName",
|
|
"startedAt",
|
|
"runId",
|
|
]),
|
|
|
|
// Immutable attachment bytes. Identity is (streamPath, attachmentId); reads
|
|
// are additionally scoped by conversationId.
|
|
flueAttachments: defineTable({
|
|
attachmentId: v.string(),
|
|
bytes: v.bytes(),
|
|
conversationId: v.string(),
|
|
createdAt: v.number(),
|
|
digest: v.string(),
|
|
filename: v.optional(v.string()),
|
|
mimeType: v.string(),
|
|
size: v.number(),
|
|
streamPath: v.string(),
|
|
})
|
|
.index("by_streamPath", ["streamPath"])
|
|
.index("by_streamPath_and_attachmentId", ["streamPath", "attachmentId"])
|
|
.index("by_streamPath_and_conversationId_and_attachmentId", [
|
|
"streamPath",
|
|
"conversationId",
|
|
"attachmentId",
|
|
]),
|
|
});
|