Files
qinglong/packages/ql3-local-sqlite/src/storage/schema.ts
T

4929 lines
252 KiB
TypeScript

import { sql } from 'drizzle-orm';
// Canonical Drizzle declaration of the complete Local SQLite storage contract.
import {
type AnySQLiteColumn,
blob,
check,
foreignKey,
index,
integer,
primaryKey,
sqliteTable,
text,
uniqueIndex,
} from 'drizzle-orm/sqlite-core';
export const localSchemaMigrations = sqliteTable(
'QingLong3SchemaMigrations',
{
migrationId: text('migration_id').primaryKey(),
streamId: text('stream_id').notNull(),
dialect: text('dialect').notNull(),
checksum: text('checksum').notNull(),
appliedAtMs: integer('applied_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_migrations_dialect_check',
sql`${table.dialect} = 'sqlite'`,
),
check(
'ql3_local_migrations_checksum_check',
sql`length(${table.checksum}) = 64 and ${table.checksum} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_migrations_applied_at_check',
sql`${table.appliedAtMs} >= 0`,
),
],
);
export const localSchemaCapabilities = sqliteTable(
'QingLong3SchemaCapabilities',
{
contractName: text('contract_name').primaryKey(),
contractVersion: integer('contract_version').notNull(),
migrationId: text('migration_id')
.notNull()
.references(() => localSchemaMigrations.migrationId),
capabilities: text('capabilities', { mode: 'json' })
.$type<Record<string, number>>()
.notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_capabilities_version_check',
sql`${table.contractVersion} >= 1`,
),
check(
'ql3_local_capabilities_json_check',
sql`json_valid(${table.capabilities}) and json_type(${table.capabilities}) = 'object'`,
),
check(
'ql3_local_capabilities_updated_at_check',
sql`${table.updatedAtMs} >= 0`,
),
],
);
export const runs = sqliteTable(
'Runs',
{
id: text('id').primaryKey(),
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
taskRevision: text('task_revision').notNull(),
taskName: text('task_name'),
taskSnapshotRef: text('task_snapshot_ref'),
legacyCronId: integer('legacy_cron_id'),
parentRunId: text('parent_run_id').references(
(): AnySQLiteColumn => runs.id,
),
retryOfRunId: text('retry_of_run_id').references(
(): AnySQLiteColumn => runs.id,
),
triggerId: text('trigger_id'),
triggerType: text('trigger_type').notNull(),
executionOrigin: text('execution_origin').notNull(),
executionOwner: text('execution_owner').notNull(),
triggeredBy: text('triggered_by'),
requestId: text('request_id'),
scheduledForMs: integer('scheduled_for_ms'),
status: text('status').notNull(),
version: integer('version').notNull().default(0),
eventSequence: integer('event_sequence').notNull().default(0),
priority: integer('priority').notNull().default(0),
idempotencyKey: text('idempotency_key'),
inputRef: text('input_ref'),
outputRef: text('output_ref'),
createdAtMs: integer('created_at_ms').notNull(),
queuedAtMs: integer('queued_at_ms'),
startedAtMs: integer('started_at_ms'),
finishedAtMs: integer('finished_at_ms'),
cancelRequestedAtMs: integer('cancel_requested_at_ms'),
cancelReason: text('cancel_reason'),
errorCode: text('error_code'),
errorSummary: text('error_summary'),
},
(table) => [
check(
'ql3_local_runs_execution_origin_check',
sql`${table.executionOrigin} in ('manual','scheduled_system','scheduled_node','once','boot','grpc','subscription','system','script','legacy_import')`,
),
check(
'ql3_local_runs_execution_owner_check',
sql`${table.executionOwner} in ('legacy','runtime')`,
),
check(
'ql3_local_runs_status_check',
sql`${table.status} in ('created','queued','dispatching','running','waiting_approval','retry_wait','lost','succeeded','failed','cancelled','timed_out')`,
),
check('ql3_local_runs_version_check', sql`${table.version} >= 0`),
check(
'ql3_local_runs_event_sequence_check',
sql`${table.eventSequence} >= 0`,
),
check(
'ql3_local_runs_time_check',
sql`${table.createdAtMs} >= 0 and (${table.scheduledForMs} is null or ${table.scheduledForMs} >= 0) and (${table.queuedAtMs} is null or ${table.queuedAtMs} >= 0) and (${table.startedAtMs} is null or ${table.startedAtMs} >= 0) and (${table.finishedAtMs} is null or ${table.finishedAtMs} >= 0) and (${table.cancelRequestedAtMs} is null or ${table.cancelRequestedAtMs} >= 0)`,
),
check(
'ql3_local_runs_cancel_reason_check',
sql`${table.cancelReason} is null or ${table.cancelReason} in ('user','policy','shutdown','reconcile','timeout')`,
),
uniqueIndex('ql3_local_runs_project_idempotency_uidx')
.on(table.projectId, table.idempotencyKey)
.where(sql`${table.idempotencyKey} is not null`),
index('ql3_local_runs_project_created_idx').on(
table.projectId,
table.createdAtMs,
table.id,
),
index('ql3_local_runs_task_created_idx').on(
table.taskId,
table.createdAtMs,
table.id,
),
index('ql3_local_runs_cancel_requested_idx').on(
table.status,
table.cancelRequestedAtMs,
table.id,
),
index('ql3_local_runs_lost_retry_idx').on(
table.executionOwner,
table.status,
table.id,
),
index('ql3_local_runs_dispatch_idx')
.on(
table.executionOwner,
table.status,
sql`${table.priority} desc`,
table.queuedAtMs,
table.id,
)
.where(
sql`${table.executionOwner} = 'runtime' and ${table.status} = 'queued' and ${table.cancelRequestedAtMs} is null`,
),
],
);
export const stepRuns = sqliteTable(
'StepRuns',
{
id: text('id').primaryKey(),
runId: text('run_id')
.notNull()
.references(() => runs.id, { onDelete: 'cascade' }),
parentStepRunId: text('parent_step_run_id'),
stepKey: text('step_key').notNull(),
kind: text('kind').notNull(),
definitionRef: text('definition_ref').notNull(),
definitionDigest: text('definition_digest').notNull(),
required: integer('required', { mode: 'boolean' }).notNull(),
status: text('status').notNull(),
version: integer('version').notNull(),
attemptCount: integer('attempt_count').notNull(),
inputRef: text('input_ref'),
outputRef: text('output_ref'),
approvalRequestId: text('approval_request_id'),
readyAtMs: integer('ready_at_ms'),
startedAtMs: integer('started_at_ms'),
finishedAtMs: integer('finished_at_ms'),
resultCode: text('result_code'),
errorSummary: text('error_summary'),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
lastMutationId: text('last_mutation_id').notNull(),
stepRunDigest: text('step_run_digest').notNull(),
stepRunJson: text('step_run_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.runId, table.parentStepRunId],
foreignColumns: [table.runId, table.id],
name: 'ql3_step_runs_parent_fk',
}).onDelete('restrict'),
check(
'ql3_step_runs_identity_check',
sql`length(${table.id}) between 1 and 128 and length(${table.runId}) between 1 and 128 and (${table.parentStepRunId} is null or (length(${table.parentStepRunId}) between 1 and 128 and ${table.parentStepRunId} <> ${table.id})) and length(${table.stepKey}) between 1 and 128 and length(cast(${table.definitionRef} as blob)) between 1 and 512`,
),
check(
'ql3_step_runs_kind_check',
sql`${table.kind} in ('task','tool','model','agent','condition','approval','subworkflow')`,
),
check(
'ql3_step_runs_status_check',
sql`${table.status} in ('pending','ready','waiting_approval','running','lost','succeeded','failed','skipped','cancelled','timed_out')`,
),
check(
'ql3_step_runs_digest_check',
sql`length(${table.definitionDigest}) = 64 and ${table.definitionDigest} not glob '*[^0-9a-f]*' and length(${table.stepRunDigest}) = 64 and ${table.stepRunDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_step_runs_counter_check',
sql`${table.required} in (0, 1) and ${table.version} between 1 and 2147483647 and ${table.attemptCount} between 0 and 64`,
),
check(
'ql3_step_runs_reference_check',
sql`(${table.inputRef} is null or length(cast(${table.inputRef} as blob)) between 1 and 512) and (${table.outputRef} is null or length(cast(${table.outputRef} as blob)) between 1 and 512) and (${table.approvalRequestId} is null or length(${table.approvalRequestId}) between 1 and 128)`,
),
check(
'ql3_step_runs_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs} and (${table.readyAtMs} is null or ${table.readyAtMs} between ${table.createdAtMs} and ${table.updatedAtMs}) and (${table.startedAtMs} is null or (${table.readyAtMs} is not null and ${table.startedAtMs} between ${table.readyAtMs} and ${table.updatedAtMs})) and (${table.finishedAtMs} is null or (${table.finishedAtMs} between ${table.createdAtMs} and ${table.updatedAtMs} and (${table.readyAtMs} is null or ${table.finishedAtMs} >= ${table.readyAtMs}) and (${table.startedAtMs} is null or ${table.finishedAtMs} >= ${table.startedAtMs})))`,
),
check(
'ql3_step_runs_state_shape_check',
sql`(${table.status} = 'pending' and ${table.readyAtMs} is null and ${table.startedAtMs} is null and ${table.finishedAtMs} is null) or (${table.status} in ('ready','waiting_approval') and ${table.readyAtMs} is not null and ${table.startedAtMs} is null and ${table.finishedAtMs} is null) or (${table.status} in ('running','lost') and ${table.readyAtMs} is not null and ${table.startedAtMs} is not null and ${table.finishedAtMs} is null) or (${table.status} in ('succeeded','failed','skipped','cancelled','timed_out') and ${table.finishedAtMs} is not null)`,
),
check(
'ql3_step_runs_result_shape_check',
sql`(${table.status} = 'waiting_approval' and ${table.approvalRequestId} is not null) or ${table.status} <> 'waiting_approval'`,
),
check(
'ql3_step_runs_result_value_check',
sql`(${table.outputRef} is null or ${table.status} = 'succeeded') and ((${table.status} = 'succeeded' and ${table.resultCode} is null and ${table.errorSummary} is null) or (${table.status} in ('failed','skipped','cancelled','timed_out','lost') and ${table.resultCode} is not null) or (${table.status} in ('pending','ready','waiting_approval','running') and ${table.resultCode} is null and ${table.errorSummary} is null)) and (${table.resultCode} is null or (length(${table.resultCode}) between 1 and 64 and ${table.resultCode} not glob '*[^a-z0-9_]*' and substr(${table.resultCode}, 1, 1) glob '[a-z]')) and (${table.errorSummary} is null or length(cast(${table.errorSummary} as blob)) between 1 and 2048)`,
),
check(
'ql3_step_runs_mutation_identity_check',
sql`length(${table.lastMutationId}) between 1 and 128`,
),
check(
'ql3_step_runs_json_check',
sql`length(cast(${table.stepRunJson} as blob)) between 2 and 16384 and json_valid(${table.stepRunJson}) and json_type(${table.stepRunJson}) = 'object' and json_extract(${table.stepRunJson}, '$.schema') = 'qinglong/step-run@v1' and json_extract(${table.stepRunJson}, '$.id') = ${table.id} and json_extract(${table.stepRunJson}, '$.runId') = ${table.runId} and json_extract(${table.stepRunJson}, '$.parentStepRunId') is ${table.parentStepRunId} and json_extract(${table.stepRunJson}, '$.stepKey') = ${table.stepKey} and json_extract(${table.stepRunJson}, '$.kind') = ${table.kind} and json_extract(${table.stepRunJson}, '$.definitionRef') = ${table.definitionRef} and json_extract(${table.stepRunJson}, '$.definitionDigest') = ${table.definitionDigest} and json_extract(${table.stepRunJson}, '$.required') is ${table.required} and json_extract(${table.stepRunJson}, '$.status') = ${table.status} and json_extract(${table.stepRunJson}, '$.version') = ${table.version} and json_extract(${table.stepRunJson}, '$.attemptCount') is ${table.attemptCount} and json_extract(${table.stepRunJson}, '$.inputRef') is ${table.inputRef} and json_extract(${table.stepRunJson}, '$.outputRef') is ${table.outputRef} and json_extract(${table.stepRunJson}, '$.approvalRequestId') is ${table.approvalRequestId} and json_extract(${table.stepRunJson}, '$.readyAtMs') is ${table.readyAtMs} and json_extract(${table.stepRunJson}, '$.startedAtMs') is ${table.startedAtMs} and json_extract(${table.stepRunJson}, '$.finishedAtMs') is ${table.finishedAtMs} and json_extract(${table.stepRunJson}, '$.resultCode') is ${table.resultCode} and json_extract(${table.stepRunJson}, '$.errorSummary') is ${table.errorSummary} and json_extract(${table.stepRunJson}, '$.createdAtMs') is ${table.createdAtMs} and json_extract(${table.stepRunJson}, '$.updatedAtMs') is ${table.updatedAtMs} and json_extract(${table.stepRunJson}, '$.lastMutationId') = ${table.lastMutationId} and json_extract(${table.stepRunJson}, '$.stepRunDigest') = ${table.stepRunDigest}`,
),
uniqueIndex('ql3_step_runs_run_id_uidx').on(table.runId, table.id),
uniqueIndex('ql3_step_runs_run_step_uidx').on(table.runId, table.stepKey),
index('ql3_step_runs_run_status_idx').on(
table.runId,
table.status,
table.id,
),
index('ql3_step_runs_recovery_idx')
.on(table.status, table.updatedAtMs, table.id)
.where(sql`${table.status} in ('waiting_approval','running','lost')`),
],
);
export const runAttempts = sqliteTable(
'RunAttempts',
{
id: text('id').primaryKey(),
runId: text('run_id')
.notNull()
.references(() => runs.id, { onDelete: 'cascade' }),
stepRunId: text('step_run_id'),
attempt: integer('attempt').notNull(),
status: text('status').notNull(),
executorType: text('executor_type').notNull(),
workerId: text('worker_id'),
workerSessionId: text('worker_session_id'),
workerGeneration: integer('worker_generation'),
executorHandle: text('executor_handle'),
pid: integer('pid'),
logArtifactId: text('log_artifact_id'),
leaseToken: text('lease_token'),
leaseTokenDigest: text('lease_token_digest'),
leaseGeneration: integer('lease_generation'),
leaseVersion: integer('lease_version'),
leaseExpiresAtMs: integer('lease_expires_at_ms'),
offerId: text('offer_id'),
deadlineAtMs: integer('deadline_at_ms'),
callbackTokenHash: text('callback_token_hash'),
callbackSequence: integer('callback_sequence').notNull().default(0),
createdAtMs: integer('created_at_ms').notNull(),
startedAtMs: integer('started_at_ms'),
finishedAtMs: integer('finished_at_ms'),
exitCode: integer('exit_code'),
errorCode: text('error_code'),
errorSummary: text('error_summary'),
},
(table) => [
check('ql3_local_attempts_attempt_check', sql`${table.attempt} >= 1`),
check(
'ql3_local_attempts_status_check',
sql`${table.status} in ('claimed','starting','running','succeeded','failed','cancelled','timed_out','lost')`,
),
check(
'ql3_local_attempts_callback_sequence_check',
sql`${table.callbackSequence} >= 0`,
),
check(
'ql3_local_attempts_time_check',
sql`${table.createdAtMs} >= 0 and (${table.startedAtMs} is null or ${table.startedAtMs} >= 0) and (${table.finishedAtMs} is null or ${table.finishedAtMs} >= 0) and (${table.leaseExpiresAtMs} is null or ${table.leaseExpiresAtMs} >= 0) and (${table.deadlineAtMs} is null or ${table.deadlineAtMs} >= 0)`,
),
uniqueIndex('ql3_local_attempts_run_attempt_uidx').on(
table.runId,
table.attempt,
),
index('ql3_local_attempts_run_status_idx').on(
table.runId,
table.status,
table.id,
),
index('ql3_local_attempts_lease_idx').on(table.leaseExpiresAtMs, table.id),
index('ql3_local_attempts_deadline_idx').on(
table.status,
table.deadlineAtMs,
table.id,
),
index('ql3_run_log_retention_candidate_idx')
.on(table.executorType, table.status, table.finishedAtMs, table.id)
.where(sql`${table.logArtifactId} is not null`),
],
);
export const runEvents = sqliteTable(
'RunEvents',
{
id: text('id').primaryKey(),
runId: text('run_id')
.notNull()
.references(() => runs.id, { onDelete: 'cascade' }),
sequence: integer('sequence').notNull(),
type: text('type').notNull(),
dedupeKey: text('dedupe_key'),
actorType: text('actor_type').notNull(),
actorId: text('actor_id'),
attemptId: text('attempt_id').references(() => runAttempts.id, {
onDelete: 'set null',
}),
stepRunId: text('step_run_id'),
payload: text('payload', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check('ql3_local_events_sequence_check', sql`${table.sequence} >= 1`),
check(
'ql3_local_events_actor_type_check',
sql`${table.actorType} in ('user','api_app','trigger','agent','mcp_client','worker','executor','system','legacy_shell','scheduler','reconciler','compatibility')`,
),
check(
'ql3_local_events_payload_check',
sql`json_valid(${table.payload}) and json_type(${table.payload}) = 'object'`,
),
check('ql3_local_events_created_at_check', sql`${table.createdAtMs} >= 0`),
uniqueIndex('ql3_local_events_run_sequence_uidx').on(
table.runId,
table.sequence,
),
uniqueIndex('ql3_local_events_run_dedupe_uidx')
.on(table.runId, table.dedupeKey)
.where(sql`${table.dedupeKey} is not null`),
index('ql3_local_events_run_created_idx').on(
table.runId,
table.createdAtMs,
table.id,
),
],
);
export const stepRunMutations = sqliteTable(
'StepRunMutations',
{
mutationId: text('mutation_id').primaryKey(),
mutationDigest: text('mutation_digest').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
stepRunDigest: text('step_run_digest').notNull(),
eventId: text('event_id')
.notNull()
.references(() => runEvents.id, { onDelete: 'cascade' }),
eventSequence: integer('event_sequence').notNull(),
runVersion: integer('run_version').notNull(),
stepRunJson: text('step_run_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
},
(table) => [
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_step_run_mutations_step_fk',
}).onDelete('cascade'),
check(
'ql3_step_run_mutations_identity_check',
sql`length(${table.mutationId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.eventId}) between 1 and 128`,
),
check(
'ql3_step_run_mutations_digest_check',
sql`length(${table.mutationDigest}) = 64 and ${table.mutationDigest} not glob '*[^0-9a-f]*' and length(${table.stepRunDigest}) = 64 and ${table.stepRunDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_step_run_mutations_counter_check',
sql`${table.eventSequence} between 1 and 2147483647 and ${table.runVersion} between 1 and 2147483647 and ${table.committedAtMs} >= 0`,
),
check(
'ql3_step_run_mutations_json_check',
sql`length(cast(${table.stepRunJson} as blob)) between 2 and 16384 and json_valid(${table.stepRunJson}) and json_type(${table.stepRunJson}) = 'object' and json_extract(${table.stepRunJson}, '$.schema') = 'qinglong/step-run@v1' and json_extract(${table.stepRunJson}, '$.id') = ${table.stepRunId} and json_extract(${table.stepRunJson}, '$.runId') = ${table.runId} and json_extract(${table.stepRunJson}, '$.lastMutationId') = ${table.mutationId} and json_extract(${table.stepRunJson}, '$.stepRunDigest') = ${table.stepRunDigest}`,
),
uniqueIndex('ql3_step_run_mutations_event_uidx').on(table.eventId),
index('ql3_step_run_mutations_step_idx').on(
table.runId,
table.stepRunId,
table.eventSequence,
table.mutationId,
),
],
);
export const runRetryPolicies = sqliteTable(
'RunRetryPolicies',
{
runId: text('run_id')
.primaryKey()
.references(() => runs.id, { onDelete: 'cascade' }),
maxAttempts: integer('max_attempts').notNull(),
retryOnLost: integer('retry_on_lost', { mode: 'boolean' }).notNull(),
safety: text('safety').notNull(),
backoffBaseMs: integer('backoff_base_ms').notNull(),
backoffMaxMs: integer('backoff_max_ms').notNull(),
nextAttemptAtMs: integer('next_attempt_at_ms'),
version: integer('version').notNull().default(0),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_retry_max_attempts_check',
sql`${table.maxAttempts} between 1 and 16`,
),
check('ql3_local_retry_on_lost_check', sql`${table.retryOnLost} in (0, 1)`),
check(
'ql3_local_retry_safety_check',
sql`${table.safety} in ('unknown','idempotent','deduplicated')`,
),
check(
'ql3_local_retry_backoff_check',
sql`${table.backoffBaseMs} between 0 and 86400000 and ${table.backoffMaxMs} between ${table.backoffBaseMs} and 86400000`,
),
check('ql3_local_retry_version_check', sql`${table.version} >= 0`),
check(
'ql3_local_retry_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs} and (${table.nextAttemptAtMs} is null or ${table.nextAttemptAtMs} >= 0)`,
),
index('ql3_local_retry_due_idx')
.on(table.nextAttemptAtMs, table.runId)
.where(sql`${table.nextAttemptAtMs} is not null`),
],
);
export const localCompletionReceiptJournal = sqliteTable(
'LocalCompletionReceiptJournal',
{
attemptId: text('attempt_id')
.primaryKey()
.references(() => runAttempts.id, { onDelete: 'cascade' }),
runId: text('run_id')
.notNull()
.references(() => runs.id, { onDelete: 'cascade' }),
state: text('state').notNull(),
quarantineRef: text('quarantine_ref'),
purgeAfterMs: integer('purge_after_ms'),
registeredAtMs: integer('registered_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_receipt_journal_state_check',
sql`${table.state} in ('pending','quarantined')`,
),
check(
'ql3_local_receipt_journal_time_check',
sql`${table.registeredAtMs} >= 0 and ${table.updatedAtMs} >= ${table.registeredAtMs} and (${table.purgeAfterMs} is null or ${table.purgeAfterMs} >= ${table.updatedAtMs})`,
),
check(
'ql3_local_receipt_journal_shape_check',
sql`(${table.state} = 'pending' and ${table.quarantineRef} is null and ${table.purgeAfterMs} is null) or (${table.state} = 'quarantined' and ${table.quarantineRef} is not null and ${table.purgeAfterMs} is not null)`,
),
index('ql3_local_receipt_journal_scan_idx').on(
table.state,
table.updatedAtMs,
table.attemptId,
),
index('ql3_local_receipt_journal_purge_idx')
.on(table.purgeAfterMs, table.attemptId)
.where(sql`${table.state} = 'quarantined'`),
],
);
export const runAttemptLogArtifactTombstones = sqliteTable(
'QingLong3RunAttemptLogArtifactTombstones',
{
logArtifactId: text('log_artifact_id').primaryKey(),
projectId: text('project_id').notNull(),
runId: text('run_id')
.notNull()
.references(() => runs.id, { onDelete: 'cascade' }),
attemptId: text('attempt_id')
.notNull()
.references(() => runAttempts.id, { onDelete: 'cascade' }),
executorType: text('executor_type').notNull(),
finishedAtMs: integer('finished_at_ms').notNull(),
eligibleAtMs: integer('eligible_at_ms').notNull(),
retiredAtMs: integer('retired_at_ms').notNull(),
disposition: text('disposition').notNull(),
byteLength: integer('byte_length').notNull(),
truncated: text('truncated').notNull(),
maximumBytes: integer('maximum_bytes'),
truncationObservedAtMs: integer('truncation_observed_at_ms'),
recordDigest: text('record_digest').notNull(),
},
(table) => [
uniqueIndex('ql3_run_log_tombstone_attempt_uidx').on(table.attemptId),
index('ql3_run_log_tombstone_retired_idx').on(
table.retiredAtMs,
table.attemptId,
),
check(
'ql3_run_log_tombstone_executor_check',
sql`${table.executorType} = 'local_process'`,
),
check(
'ql3_run_log_tombstone_disposition_check',
sql`${table.disposition} in ('deleted','already_absent')`,
),
check(
'ql3_run_log_tombstone_truncated_check',
sql`${table.truncated} in ('true','false','unknown')`,
),
check(
'ql3_run_log_tombstone_identity_check',
sql`length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.attemptId}) between 1 and 128 and length(${table.logArtifactId}) = 36 and substr(${table.logArtifactId}, 1, 6) = 'local-' and substr(${table.logArtifactId}, 7) not glob '*[^0-9a-f]*'`,
),
check(
'ql3_run_log_tombstone_time_check',
sql`${table.finishedAtMs} >= 0 and ${table.eligibleAtMs} >= ${table.finishedAtMs} and ${table.retiredAtMs} >= ${table.eligibleAtMs}`,
),
check(
'ql3_run_log_tombstone_size_check',
sql`${table.byteLength} between 0 and 1073741824 and (${table.disposition} <> 'already_absent' or ${table.byteLength} = 0)`,
),
check(
'ql3_run_log_tombstone_truncation_shape_check',
sql`(${table.truncated} = 'unknown' and ${table.maximumBytes} is null and ${table.truncationObservedAtMs} is null) or (${table.truncated} in ('true','false') and ${table.maximumBytes} >= 1 and ${table.truncationObservedAtMs} >= 0)`,
),
check(
'ql3_run_log_tombstone_digest_check',
sql`length(${table.recordDigest}) = 64 and ${table.recordDigest} not glob '*[^0-9a-f]*'`,
),
],
);
export const runAttemptLogRetentionState = sqliteTable(
'QingLong3RunAttemptLogRetentionState',
{
maintenanceId: text('maintenance_id').primaryKey(),
cursorFinishedAtMs: integer('cursor_finished_at_ms'),
cursorAttemptId: text('cursor_attempt_id'),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_run_log_retention_state_id_check',
sql`${table.maintenanceId} = 'local-run-attempt-log'`,
),
check(
'ql3_run_log_retention_state_cursor_check',
sql`(${table.cursorFinishedAtMs} is null and ${table.cursorAttemptId} is null) or (${table.cursorFinishedAtMs} >= 0 and length(${table.cursorAttemptId}) between 1 and 128)`,
),
check(
'ql3_run_log_retention_state_time_check',
sql`${table.updatedAtMs} >= 0`,
),
],
);
export const localExecutionContextRecipes = sqliteTable(
'QingLong3LocalExecutionContextRecipes',
{
contextRef: text('context_ref').primaryKey(),
environmentJson: text('environment_json').notNull(),
contentDigest: text('content_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_context_ref_check',
sql`length(${table.contextRef}) = 80 and ${table.contextRef} glob 'localctx:sha256:[0-9a-f]*' and length(replace(${table.contextRef}, 'localctx:sha256:', '')) = 64`,
),
check(
'ql3_local_context_environment_check',
sql`json_valid(${table.environmentJson}) and json_type(${table.environmentJson}) = 'array' and length(${table.environmentJson}) <= 262144`,
),
check(
'ql3_local_context_digest_check',
sql`length(${table.contentDigest}) = 64 and ${table.contentDigest} not glob '*[^0-9a-f]*' and ${table.contextRef} = 'localctx:sha256:' || ${table.contentDigest}`,
),
check('ql3_local_context_created_check', sql`${table.createdAtMs} >= 0`),
],
);
export const localTaskExecutionRevisions = sqliteTable(
'QingLong3LocalTaskExecutionRevisions',
{
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
taskRevision: text('task_revision').notNull(),
executorType: text('executor_type').notNull(),
commandJson: text('command_json').notNull(),
workingDirectory: text('working_directory'),
timeoutMs: integer('timeout_ms'),
contextRef: text('context_ref')
.notNull()
.references(() => localExecutionContextRecipes.contextRef, {
onDelete: 'restrict',
}),
contentDigest: text('content_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({
columns: [table.projectId, table.taskId, table.taskRevision],
}),
check(
'ql3_local_revision_executor_check',
sql`${table.executorType} = 'local_process'`,
),
check(
'ql3_local_revision_command_check',
sql`json_valid(${table.commandJson}) and json_type(${table.commandJson}) = 'object' and length(${table.commandJson}) between 1 and 131072`,
),
check(
'ql3_local_revision_working_directory_check',
sql`${table.workingDirectory} is null or (length(${table.workingDirectory}) between 1 and 4096 and substr(${table.workingDirectory}, 1, 1) = '/')`,
),
check(
'ql3_local_revision_timeout_check',
sql`${table.timeoutMs} is null or ${table.timeoutMs} between 1 and 31536000000`,
),
check(
'ql3_local_revision_digest_check',
sql`length(${table.contentDigest}) = 64 and ${table.contentDigest} not glob '*[^0-9a-f]*'`,
),
check('ql3_local_revision_created_check', sql`${table.createdAtMs} >= 0`),
],
);
export const localSecretEnvelopes = sqliteTable(
'QingLong3LocalSecretEnvelopes',
{
projectId: text('project_id').notNull(),
name: text('secret_name').notNull(),
version: integer('version').notNull(),
mutationId: text('mutation_id').notNull(),
keyId: text('key_id').notNull(),
algorithm: text('algorithm').notNull(),
nonce: blob('nonce', { mode: 'buffer' }).notNull(),
ciphertext: blob('ciphertext', { mode: 'buffer' }).notNull(),
authTag: blob('auth_tag', { mode: 'buffer' }).notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.name, table.version] }),
check(
'ql3_local_secret_project_check',
sql`length(${table.projectId}) between 1 and 128 and ${table.projectId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*'`,
),
check(
'ql3_local_secret_name_check',
sql`length(${table.name}) between 1 and 128 and ${table.name} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*'`,
),
check(
'ql3_local_secret_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_secret_mutation_check',
sql`length(${table.mutationId}) between 1 and 64`,
),
check(
'ql3_local_secret_key_check',
sql`length(${table.keyId}) between 1 and 128 and ${table.keyId} not glob '*[^A-Za-z0-9._-]*'`,
),
check(
'ql3_local_secret_algorithm_check',
sql`${table.algorithm} = 'aes-256-gcm'`,
),
check(
'ql3_local_secret_crypto_shape_check',
sql`length(${table.nonce}) = 12 and length(${table.ciphertext}) <= 16384 and length(${table.authTag}) = 16`,
),
check('ql3_local_secret_created_check', sql`${table.createdAtMs} >= 0`),
uniqueIndex('ql3_local_secret_mutation_uidx').on(
table.projectId,
table.name,
table.mutationId,
),
index('ql3_local_secret_current_idx').on(
table.projectId,
table.name,
sql`${table.version} desc`,
),
index('ql3_local_secret_key_usage_idx').on(
table.keyId,
table.projectId,
table.name,
table.version,
),
],
);
export const localProjects = sqliteTable(
'QingLong3Projects',
{
id: text('id').primaryKey(),
name: text('name').notNull(),
slug: text('slug').notNull(),
status: text('status').notNull(),
version: integer('version').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_projects_id_check',
sql`length(${table.id}) between 1 and 128 and ${table.id} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*'`,
),
check(
'ql3_local_projects_name_check',
sql`length(${table.name}) between 1 and 255`,
),
check(
'ql3_local_projects_slug_check',
sql`length(${table.slug}) between 1 and 128 and ${table.slug} = lower(${table.slug}) and ${table.slug} not glob '*[^a-z0-9-]*' and substr(${table.slug}, 1, 1) not glob '[^a-z0-9]' and substr(${table.slug}, -1, 1) not glob '[^a-z0-9]'`,
),
check(
'ql3_local_projects_status_check',
sql`${table.status} in ('active', 'archived')`,
),
check(
'ql3_local_projects_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_projects_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
uniqueIndex('ql3_local_projects_slug_uidx').on(table.slug),
],
);
export const approvalRequests = sqliteTable(
'QingLong3ApprovalRequests',
{
requestId: text('request_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
version: integer('version').notNull(),
state: text('state').notNull(),
actionType: text('action_type').notNull(),
actionRef: text('action_ref').notNull(),
actionDigest: text('action_digest').notNull(),
previewDigest: text('preview_digest').notNull(),
requestedByType: text('requested_by_type').notNull(),
requestedById: text('requested_by_id').notNull(),
decisionId: text('decision_id'),
consumptionId: text('consumption_id'),
dispatchId: text('dispatch_id'),
expiresAtMs: integer('expires_at_ms').notNull(),
requestJson: text('request_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
requestDigest: text('request_digest').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_approval_requests_identity_check',
sql`length(${table.requestId}) between 1 and 128 and length(${table.actionType}) between 1 and 128 and length(${table.actionRef}) between 1 and 255`,
),
check(
'ql3_approval_requests_state_version_check',
sql`(${table.state} = 'pending' and ${table.version} = 1) or (${table.state} in ('approved','rejected') and ${table.version} = 2) or (${table.state} = 'consumed' and ${table.version} = 3)`,
),
check(
'ql3_approval_requests_digest_check',
sql`length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and length(${table.previewDigest}) = 64 and ${table.previewDigest} not glob '*[^0-9a-f]*' and length(${table.requestDigest}) = 64 and ${table.requestDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_approval_requests_subject_check',
sql`${table.requestedByType} in ('user','api_app','mcp_client','agent','system','worker') and length(${table.requestedById}) between 1 and 255`,
),
check(
'ql3_approval_requests_mutation_tuple_check',
sql`(${table.version} = 1 and ${table.decisionId} is null and ${table.consumptionId} is null and ${table.dispatchId} is null) or (${table.version} = 2 and ${table.decisionId} is not null and ${table.consumptionId} is null and ${table.dispatchId} is null) or (${table.version} = 3 and ${table.decisionId} is not null and ${table.consumptionId} is not null and ${table.dispatchId} is not null)`,
),
check(
'ql3_approval_requests_json_check',
sql`length(${table.requestJson}) between 2 and 65536 and json_valid(${table.requestJson}) and json_type(${table.requestJson}) = 'object' and json_extract(${table.requestJson}, '$.id') = ${table.requestId} and json_extract(${table.requestJson}, '$.projectId') = ${table.projectId} and json_extract(${table.requestJson}, '$.version') = ${table.version} and json_extract(${table.requestJson}, '$.state') = ${table.state} and json_extract(${table.requestJson}, '$.action.actionType') = ${table.actionType} and json_extract(${table.requestJson}, '$.action.actionRef') = ${table.actionRef} and json_extract(${table.requestJson}, '$.action.actionDigest') = ${table.actionDigest} and json_extract(${table.requestJson}, '$.action.previewDigest') = ${table.previewDigest}`,
),
check(
'ql3_approval_requests_time_check',
sql`${table.expiresAtMs} > 0 and ${table.updatedAtMs} >= 0`,
),
uniqueIndex('ql3_approval_requests_decision_uidx').on(table.decisionId),
uniqueIndex('ql3_approval_requests_consumption_uidx').on(
table.consumptionId,
),
uniqueIndex('ql3_approval_requests_dispatch_uidx').on(table.dispatchId),
index('ql3_approval_requests_pending_idx')
.on(table.expiresAtMs, table.requestId)
.where(sql`${table.state} = 'pending'`),
index('ql3_approval_requests_project_idx').on(
table.projectId,
table.updatedAtMs,
table.requestId,
),
],
);
export const approvedActionDispatches = sqliteTable(
'QingLong3ApprovedActionDispatches',
{
dispatchId: text('dispatch_id').primaryKey(),
approvalRequestId: text('approval_request_id')
.notNull()
.references(() => approvalRequests.requestId, { onDelete: 'restrict' }),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
actionType: text('action_type').notNull(),
actionRef: text('action_ref').notNull(),
actionDigest: text('action_digest').notNull(),
previewDigest: text('preview_digest').notNull(),
dispatchJson: text('dispatch_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
dispatchDigest: text('dispatch_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
uniqueIndex('ql3_approved_action_dispatch_request_uidx').on(
table.approvalRequestId,
),
uniqueIndex('ql3_approved_action_dispatch_lifecycle_uidx').on(
table.dispatchId,
table.projectId,
table.actionType,
table.actionDigest,
table.previewDigest,
),
check(
'ql3_approved_action_dispatch_identity_check',
sql`length(${table.dispatchId}) between 1 and 128 and length(${table.actionType}) between 1 and 128 and length(${table.actionRef}) between 1 and 255`,
),
check(
'ql3_approved_action_dispatch_digest_check',
sql`length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and length(${table.previewDigest}) = 64 and ${table.previewDigest} not glob '*[^0-9a-f]*' and length(${table.dispatchDigest}) = 64 and ${table.dispatchDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_approved_action_dispatch_json_check',
sql`length(${table.dispatchJson}) between 2 and 65536 and json_valid(${table.dispatchJson}) and json_type(${table.dispatchJson}) = 'object' and json_extract(${table.dispatchJson}, '$.id') = ${table.dispatchId} and json_extract(${table.dispatchJson}, '$.approvalRequestId') = ${table.approvalRequestId} and json_extract(${table.dispatchJson}, '$.projectId') = ${table.projectId} and json_extract(${table.dispatchJson}, '$.action.actionType') = ${table.actionType} and json_extract(${table.dispatchJson}, '$.action.actionRef') = ${table.actionRef} and json_extract(${table.dispatchJson}, '$.action.actionDigest') = ${table.actionDigest} and json_extract(${table.dispatchJson}, '$.action.previewDigest') = ${table.previewDigest}`,
),
check(
'ql3_approved_action_dispatch_time_check',
sql`${table.createdAtMs} >= 0`,
),
index('ql3_approved_action_dispatch_project_idx').on(
table.projectId,
table.createdAtMs,
table.dispatchId,
),
],
);
export const approvedActionExecutions = sqliteTable(
'QingLong3ApprovedActionExecutions',
{
dispatchId: text('dispatch_id')
.primaryKey()
.references(() => approvedActionDispatches.dispatchId, {
onDelete: 'restrict',
}),
dispatchDigest: text('dispatch_digest').notNull(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
status: text('status').notNull(),
version: integer('version').notNull(),
attemptCount: integer('attempt_count').notNull(),
maxAttempts: integer('max_attempts').notNull(),
eligibleAtMs: integer('eligible_at_ms'),
nextAttemptAtMs: integer('next_attempt_at_ms'),
leaseOwner: text('lease_owner'),
leaseToken: text('lease_token'),
leaseExpiresAtMs: integer('lease_expires_at_ms'),
startedAtMs: integer('started_at_ms'),
resultMutationId: text('result_mutation_id'),
resultCode: text('result_code'),
resultDigest: text('result_digest'),
completedAtMs: integer('completed_at_ms'),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
executionJson: text('execution_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
executionDigest: text('execution_digest').notNull(),
},
(table) => [
check(
'ql3_approved_action_execution_state_check',
sql`${table.status} in ('pending','leased','executing','retry_wait','succeeded','failed','blocked') and ${table.version} between 0 and 2147483647 and ${table.attemptCount} between 0 and 16 and ${table.maxAttempts} between 1 and 16 and ${table.attemptCount} <= ${table.maxAttempts}`,
),
check(
'ql3_approved_action_execution_lease_check',
sql`(${table.leaseOwner} is null and ${table.leaseToken} is null and ${table.leaseExpiresAtMs} is null) or (length(${table.leaseOwner}) between 1 and 128 and length(${table.leaseToken}) between 1 and 128 and ${table.leaseExpiresAtMs} > ${table.updatedAtMs})`,
),
check(
'ql3_approved_action_execution_result_check',
sql`(${table.resultMutationId} is null and ${table.resultCode} is null) or (length(${table.resultMutationId}) between 1 and 128 and length(${table.resultCode}) between 1 and 64)`,
),
check(
'ql3_approved_action_execution_digest_check',
sql`length(${table.dispatchDigest}) = 64 and ${table.dispatchDigest} not glob '*[^0-9a-f]*' and (${table.resultDigest} is null or (length(${table.resultDigest}) = 64 and ${table.resultDigest} not glob '*[^0-9a-f]*')) and length(${table.executionDigest}) = 64 and ${table.executionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_approved_action_execution_json_check',
sql`length(${table.executionJson}) between 2 and 65536 and json_valid(${table.executionJson}) and json_type(${table.executionJson}) = 'object' and json_extract(${table.executionJson}, '$.schema') = 'qinglong/approved-action-execution@v1' and json_extract(${table.executionJson}, '$.dispatchId') = ${table.dispatchId} and json_extract(${table.executionJson}, '$.dispatchDigest') = ${table.dispatchDigest} and json_extract(${table.executionJson}, '$.projectId') = ${table.projectId} and json_extract(${table.executionJson}, '$.status') = ${table.status} and json_extract(${table.executionJson}, '$.version') = ${table.version} and json_extract(${table.executionJson}, '$.attemptCount') = ${table.attemptCount} and json_extract(${table.executionJson}, '$.maxAttempts') = ${table.maxAttempts} and json_extract(${table.executionJson}, '$.executionDigest') = ${table.executionDigest}`,
),
check(
'ql3_approved_action_execution_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
index('ql3_approved_action_execution_due_idx')
.on(table.eligibleAtMs, table.dispatchId)
.where(sql`${table.status} in ('pending','leased','retry_wait')`),
index('ql3_approved_action_execution_recovery_idx')
.on(table.leaseExpiresAtMs, table.dispatchId)
.where(sql`${table.status} = 'executing'`),
index('ql3_approved_action_execution_project_idx').on(
table.projectId,
table.updatedAtMs,
table.dispatchId,
),
],
);
export const pluginPackageInstallProposals = sqliteTable(
'QingLong3PluginPackageInstallProposals',
{
actionRef: text('action_ref').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
actionType: text('action_type').notNull(),
permission: text('permission').notNull(),
actionDigest: text('action_digest').notNull(),
previewDigest: text('preview_digest').notNull(),
proposedByType: text('proposed_by_type').notNull(),
proposedById: text('proposed_by_id').notNull(),
fenceProjectVersion: integer('fence_project_version').notNull(),
fenceBindingVersion: integer('fence_binding_version'),
createdAtMs: integer('created_at_ms').notNull(),
proposalJson: text('proposal_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
proposalDigest: text('proposal_digest').notNull(),
},
(table) => [
check(
'ql3_plugin_package_proposal_identity_check',
sql`length(${table.actionRef}) between 1 and 255 and ${table.actionType} = 'plugin_package.install' and ${table.permission} = 'package.manage' and ${table.proposedByType} in ('user','api_app','mcp_client','agent','system','worker') and length(${table.proposedById}) between 1 and 255`,
),
check(
'ql3_plugin_package_proposal_digest_check',
sql`length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and length(${table.previewDigest}) = 64 and ${table.previewDigest} not glob '*[^0-9a-f]*' and length(${table.proposalDigest}) = 64 and ${table.proposalDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_proposal_json_check',
sql`length(${table.proposalJson}) between 2 and 262144 and json_valid(${table.proposalJson}) and json_type(${table.proposalJson}) = 'object' and json_extract(${table.proposalJson}, '$.schema') = 'qinglong/plugin-package-install-proposal@v1' and json_extract(${table.proposalJson}, '$.actionRef') = ${table.actionRef} and json_extract(${table.proposalJson}, '$.projectId') = ${table.projectId} and json_extract(${table.proposalJson}, '$.actionType') = ${table.actionType} and json_extract(${table.proposalJson}, '$.permission') = ${table.permission} and json_extract(${table.proposalJson}, '$.actionDigest') = ${table.actionDigest} and json_extract(${table.proposalJson}, '$.previewDigest') = ${table.previewDigest} and json_extract(${table.proposalJson}, '$.proposedBy.type') = ${table.proposedByType} and json_extract(${table.proposalJson}, '$.proposedBy.id') = ${table.proposedById} and json_extract(${table.proposalJson}, '$.proposalFence.projectVersion') = ${table.fenceProjectVersion} and json_extract(${table.proposalJson}, '$.proposalFence.bindingVersion') is ${table.fenceBindingVersion} and json_extract(${table.proposalJson}, '$.createdAtMs') = ${table.createdAtMs} and json_extract(${table.proposalJson}, '$.proposalDigest') = ${table.proposalDigest}`,
),
check(
'ql3_plugin_package_proposal_time_check',
sql`${table.fenceProjectVersion} > 0 and (${table.fenceBindingVersion} is null or ${table.fenceBindingVersion} > 0) and ${table.createdAtMs} >= 0`,
),
index('ql3_plugin_package_proposal_project_idx').on(
table.projectId,
table.createdAtMs,
table.actionRef,
),
],
);
export const pluginPackageInstalls = sqliteTable(
'QingLong3PluginPackageInstalls',
{
installationId: text('installation_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
packageName: text('package_name').notNull(),
packageVersion: text('package_version').notNull(),
operation: text('operation').notNull(),
lockDigest: text('lock_digest').notNull(),
targetGeneration: integer('target_generation').notNull(),
previousActiveLockDigest: text('previous_active_lock_digest'),
activeLockDigest: text('active_lock_digest'),
state: text('state').notNull(),
version: integer('version').notNull(),
lastMutationId: text('last_mutation_id').notNull(),
lastMutationDigest: text('last_mutation_digest').notNull(),
lockJson: text('lock_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
recordJson: text('record_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
recordDigest: text('record_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
check(
'ql3_plugin_package_installs_identity_check',
sql`length(${table.installationId}) between 1 and 128 and length(${table.packageName}) between 1 and 253 and length(${table.packageVersion}) between 1 and 128 and length(${table.lastMutationId}) between 1 and 128`,
),
check(
'ql3_plugin_package_installs_operation_check',
sql`${table.operation} in ('install','reinstall','upgrade','rollback')`,
),
check(
'ql3_plugin_package_installs_state_check',
sql`${table.state} in ('queued','staged','activating','active','failed')`,
),
check(
'ql3_plugin_package_installs_version_check',
sql`${table.targetGeneration} between 1 and 2147483647 and ${table.version} between 1 and 2147483647`,
),
check(
'ql3_plugin_package_installs_digest_check',
sql`length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and (${table.previousActiveLockDigest} is null or (length(${table.previousActiveLockDigest}) = 64 and ${table.previousActiveLockDigest} not glob '*[^0-9a-f]*')) and (${table.activeLockDigest} is null or (length(${table.activeLockDigest}) = 64 and ${table.activeLockDigest} not glob '*[^0-9a-f]*')) and length(${table.lastMutationDigest}) = 64 and ${table.lastMutationDigest} not glob '*[^0-9a-f]*' and length(${table.recordDigest}) = 64 and ${table.recordDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_installs_record_check',
sql`length(${table.lockJson}) between 2 and 262144 and json_valid(${table.lockJson}) and json_type(${table.lockJson}) = 'object' and json_extract(${table.lockJson}, '$.lockDigest') = ${table.lockDigest} and json_extract(${table.lockJson}, '$.projectId') = ${table.projectId} and json_extract(${table.lockJson}, '$.packageName') = ${table.packageName} and length(${table.recordJson}) between 2 and 262144 and json_valid(${table.recordJson}) and json_type(${table.recordJson}) = 'object' and json_extract(${table.recordJson}, '$.installationId') = ${table.installationId} and json_extract(${table.recordJson}, '$.projectId') = ${table.projectId} and json_extract(${table.recordJson}, '$.packageName') = ${table.packageName} and json_extract(${table.recordJson}, '$.lockDigest') = ${table.lockDigest} and json_extract(${table.recordJson}, '$.state') = ${table.state} and json_extract(${table.recordJson}, '$.version') = ${table.version} and json_extract(${table.recordJson}, '$.recordDigest') = ${table.recordDigest}`,
),
check(
'ql3_plugin_package_installs_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
index('ql3_plugin_package_installs_recovery_idx')
.on(table.state, table.packageName, table.installationId)
.where(sql`${table.state} in ('queued','staged','activating')`),
index('ql3_plugin_package_installs_project_history_idx').on(
table.projectId,
table.packageName,
table.createdAtMs,
table.installationId,
),
uniqueIndex('ql3_plugin_package_installs_snapshot_source_uidx').on(
table.projectId,
table.packageName,
table.installationId,
table.targetGeneration,
table.lockDigest,
),
uniqueIndex('ql3_plugin_package_installs_quarantine_target_uidx').on(
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
table.recordDigest,
),
],
);
export const pluginPackageInstallHeads = sqliteTable(
'QingLong3PluginPackageInstallHeads',
{
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
packageName: text('package_name').notNull(),
installationId: text('installation_id')
.notNull()
.references(() => pluginPackageInstalls.installationId, {
onDelete: 'restrict',
}),
},
(table) => [
primaryKey({ columns: [table.projectId, table.packageName] }),
uniqueIndex('ql3_plugin_package_install_heads_install_uidx').on(
table.installationId,
),
check(
'ql3_plugin_package_install_heads_identity_check',
sql`length(${table.packageName}) between 1 and 253 and length(${table.installationId}) between 1 and 128`,
),
],
);
export const pluginPackageInstallMutations = sqliteTable(
'QingLong3PluginPackageInstallMutations',
{
installationId: text('installation_id')
.notNull()
.references(() => pluginPackageInstalls.installationId, {
onDelete: 'restrict',
}),
mutationId: text('mutation_id').notNull(),
mutationDigest: text('mutation_digest').notNull(),
resultingRecordDigest: text('resulting_record_digest').notNull(),
occurredAtMs: integer('occurred_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.installationId, table.mutationId] }),
check(
'ql3_plugin_package_install_mutations_identity_check',
sql`length(${table.mutationId}) between 1 and 128`,
),
check(
'ql3_plugin_package_install_mutations_digest_check',
sql`length(${table.mutationDigest}) = 64 and ${table.mutationDigest} not glob '*[^0-9a-f]*' and length(${table.resultingRecordDigest}) = 64 and ${table.resultingRecordDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_install_mutations_time_check',
sql`${table.occurredAtMs} >= 0`,
),
index('ql3_plugin_package_install_mutations_result_idx').on(
table.installationId,
table.resultingRecordDigest,
),
],
);
export const pluginPackageMaterializedRevisions = sqliteTable(
'QingLong3PluginPackageMaterializedRevisions',
{
generationDigest: text('generation_digest').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
packageName: text('package_name').notNull(),
generation: integer('generation').notNull(),
lockDigest: text('lock_digest').notNull(),
manifestDigest: text('manifest_digest').notNull(),
revisionDigest: text('revision_digest').notNull(),
revisionJson: text('revision_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_plugin_package_materialized_revision_identity_check',
sql`length(${table.packageName}) between 1 and 63 and ${table.generation} between 1 and 2147483647`,
),
check(
'ql3_plugin_package_materialized_revision_digest_check',
sql`length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.manifestDigest}) = 64 and ${table.manifestDigest} not glob '*[^0-9a-f]*' and length(${table.revisionDigest}) = 64 and ${table.revisionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_materialized_revision_json_check',
sql`length(${table.revisionJson}) between 2 and 25165824 and json_valid(${table.revisionJson}) and json_type(${table.revisionJson}) = 'object' and json_extract(${table.revisionJson}, '$.schema') = 'qinglong/plugin-package-materialized-revision@v1' and json_extract(${table.revisionJson}, '$.generation.generationDigest') = ${table.generationDigest} and json_extract(${table.revisionJson}, '$.generation.projectId') = ${table.projectId} and json_extract(${table.revisionJson}, '$.generation.packageName') = ${table.packageName} and json_extract(${table.revisionJson}, '$.generation.generation') = ${table.generation} and json_extract(${table.revisionJson}, '$.generation.lockDigest') = ${table.lockDigest} and json_extract(${table.revisionJson}, '$.manifestDigest') = ${table.manifestDigest} and json_extract(${table.revisionJson}, '$.revisionDigest') = ${table.revisionDigest}`,
),
check(
'ql3_plugin_package_materialized_revision_time_check',
sql`${table.createdAtMs} >= 0`,
),
uniqueIndex('ql3_plugin_package_materialized_revision_generation_uidx').on(
table.projectId,
table.packageName,
table.generation,
),
index('ql3_plugin_package_materialized_revision_lock_idx').on(
table.lockDigest,
table.generationDigest,
),
uniqueIndex(
'ql3_plugin_package_materialized_revision_snapshot_source_uidx',
).on(
table.projectId,
table.packageName,
table.generation,
table.generationDigest,
table.lockDigest,
table.revisionDigest,
),
],
);
export const projectToolDefinitionSnapshots = sqliteTable(
'QingLong3ProjectToolDefinitionSnapshots',
{
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
activeVectorDigest: text('active_vector_digest').notNull(),
definitionsDigest: text('definitions_digest').notNull(),
snapshotDigest: text('snapshot_digest').notNull(),
snapshotJson: text('snapshot_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.activeVectorDigest] }),
check(
'ql3_project_tool_definition_snapshot_identity_check',
sql`length(${table.projectId}) between 1 and 128`,
),
check(
'ql3_project_tool_definition_snapshot_digest_check',
sql`length(${table.activeVectorDigest}) = 64 and ${table.activeVectorDigest} not glob '*[^0-9a-f]*' and length(${table.definitionsDigest}) = 64 and ${table.definitionsDigest} not glob '*[^0-9a-f]*' and length(${table.snapshotDigest}) = 64 and ${table.snapshotDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_project_tool_definition_snapshot_json_check',
sql`length(${table.snapshotJson}) between 2 and 8388608 and json_valid(${table.snapshotJson}) and json_type(${table.snapshotJson}) = 'object' and json_extract(${table.snapshotJson}, '$.schema') = 'qinglong/project-tool-definition-snapshot@v1' and json_extract(${table.snapshotJson}, '$.projectId') = ${table.projectId} and json_extract(${table.snapshotJson}, '$.activeVectorDigest') = ${table.activeVectorDigest} and json_extract(${table.snapshotJson}, '$.definitionsDigest') = ${table.definitionsDigest} and json_extract(${table.snapshotJson}, '$.snapshotDigest') = ${table.snapshotDigest}`,
),
check(
'ql3_project_tool_definition_snapshot_time_check',
sql`${table.committedAtMs} >= 0`,
),
uniqueIndex('ql3_project_tool_definition_snapshot_digest_uidx').on(
table.snapshotDigest,
),
uniqueIndex('ql3_project_tool_definition_snapshot_withdrawal_uidx').on(
table.projectId,
table.activeVectorDigest,
table.snapshotDigest,
),
index('ql3_project_tool_definition_snapshot_current_idx').on(
table.projectId,
sql`${table.committedAtMs} desc`,
table.activeVectorDigest,
),
],
);
export const projectToolDefinitionSnapshotSources = sqliteTable(
'QingLong3ProjectToolDefinitionSnapshotSources',
{
projectId: text('project_id').notNull(),
activeVectorDigest: text('active_vector_digest').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
generation: integer('generation').notNull(),
generationDigest: text('generation_digest').notNull(),
lockDigest: text('lock_digest').notNull(),
revisionDigest: text('revision_digest').notNull(),
},
(table) => [
primaryKey({
columns: [table.projectId, table.activeVectorDigest, table.packageName],
}),
foreignKey({
columns: [table.projectId, table.activeVectorDigest],
foreignColumns: [
projectToolDefinitionSnapshots.projectId,
projectToolDefinitionSnapshots.activeVectorDigest,
],
name: 'ql3_project_tool_definition_snapshot_source_snapshot_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.projectId,
table.packageName,
table.installationId,
table.generation,
table.lockDigest,
],
foreignColumns: [
pluginPackageInstalls.projectId,
pluginPackageInstalls.packageName,
pluginPackageInstalls.installationId,
pluginPackageInstalls.targetGeneration,
pluginPackageInstalls.lockDigest,
],
name: 'ql3_project_tool_definition_snapshot_source_install_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.projectId,
table.packageName,
table.generation,
table.generationDigest,
table.lockDigest,
table.revisionDigest,
],
foreignColumns: [
pluginPackageMaterializedRevisions.projectId,
pluginPackageMaterializedRevisions.packageName,
pluginPackageMaterializedRevisions.generation,
pluginPackageMaterializedRevisions.generationDigest,
pluginPackageMaterializedRevisions.lockDigest,
pluginPackageMaterializedRevisions.revisionDigest,
],
name: 'ql3_project_tool_definition_snapshot_source_revision_fk',
}).onDelete('restrict'),
check(
'ql3_project_tool_definition_snapshot_source_identity_check',
sql`length(${table.packageName}) between 1 and 63 and length(${table.installationId}) between 1 and 128 and ${table.generation} between 1 and 2147483647`,
),
check(
'ql3_project_tool_definition_snapshot_source_digest_check',
sql`length(${table.activeVectorDigest}) = 64 and ${table.activeVectorDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.revisionDigest}) = 64 and ${table.revisionDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_project_tool_definition_snapshot_source_generation_idx').on(
table.generationDigest,
table.projectId,
table.packageName,
),
index('ql3_project_tool_definition_snapshot_source_install_idx').on(
table.installationId,
table.activeVectorDigest,
),
],
);
export const taskDefinitions = sqliteTable(
'QingLong3TaskDefinitions',
{
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
taskId: text('task_id').notNull(),
currentRevision: integer('current_revision').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.taskId] }),
check(
'ql3_task_definitions_id_check',
sql`length(${table.projectId}) between 1 and 128 and length(${table.taskId}) between 1 and 128 and ${table.projectId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*' and ${table.taskId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*'`,
),
check(
'ql3_task_definitions_revision_check',
sql`${table.currentRevision} between 1 and 2147483647`,
),
check(
'ql3_task_definitions_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
],
);
export const taskDefinitionRevisions = sqliteTable(
'QingLong3TaskDefinitionRevisions',
{
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
revision: integer('revision').notNull(),
mutationId: text('mutation_id').notNull(),
name: text('name').notNull(),
description: text('description'),
kind: text('kind').notNull(),
specJson: text('spec_json').notNull(),
labelsJson: text('labels_json').notNull(),
enabled: integer('enabled', { mode: 'boolean' }).notNull(),
contentDigest: text('content_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.taskId, table.revision] }),
foreignKey({
columns: [table.projectId, table.taskId],
foreignColumns: [taskDefinitions.projectId, taskDefinitions.taskId],
name: 'ql3_task_definition_revisions_task_fk',
}).onDelete('restrict'),
check(
'ql3_task_definition_revisions_revision_check',
sql`${table.revision} between 1 and 2147483647`,
),
check(
'ql3_task_definition_revisions_mutation_check',
sql`length(${table.mutationId}) = 36 and substr(${table.mutationId}, 9, 1) = '-' and substr(${table.mutationId}, 14, 1) = '-' and substr(${table.mutationId}, 19, 1) = '-' and substr(${table.mutationId}, 24, 1) = '-' and replace(${table.mutationId}, '-', '') not glob '*[^0-9a-f]*'`,
),
check(
'ql3_task_definition_revisions_name_check',
sql`length(${table.name}) between 1 and 255 and (${table.description} is null or length(${table.description}) between 1 and 4096)`,
),
check(
'ql3_task_definition_revisions_kind_check',
sql`${table.kind} in ('script','command','workflow','agent','tool')`,
),
check(
'ql3_task_definition_revisions_spec_check',
sql`json_valid(${table.specJson}) and json_type(${table.specJson}) = 'object' and length(${table.specJson}) between 1 and 65536`,
),
check(
'ql3_task_definition_revisions_labels_check',
sql`json_valid(${table.labelsJson}) and json_type(${table.labelsJson}) = 'object' and length(${table.labelsJson}) between 2 and 16384`,
),
check(
'ql3_task_definition_revisions_enabled_check',
sql`${table.enabled} in (0, 1)`,
),
check(
'ql3_task_definition_revisions_digest_check',
sql`length(${table.contentDigest}) = 64 and ${table.contentDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_task_definition_revisions_created_check',
sql`${table.createdAtMs} >= 0`,
),
uniqueIndex('ql3_task_definition_revisions_mutation_uidx').on(
table.mutationId,
),
index('ql3_task_definition_revisions_project_kind_idx').on(
table.projectId,
table.kind,
table.enabled,
table.taskId,
table.revision,
),
],
);
export const pluginPackageQuarantineEvents = sqliteTable(
'QingLong3PluginPackageQuarantineEvents',
{
eventDigest: text('event_digest').primaryKey(),
mutationId: text('mutation_id').notNull(),
revocationReceiptDigest: text('revocation_receipt_digest').notNull(),
impactDigest: text('impact_digest').notNull(),
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
lockDigest: text('lock_digest').notNull(),
installState: text('install_state').notNull(),
installVersion: integer('install_version').notNull(),
installRecordDigest: text('install_record_digest').notNull(),
activeLockDigest: text('active_lock_digest'),
proposerType: text('proposer_type').notNull(),
proposerId: text('proposer_id').notNull(),
confirmerType: text('confirmer_type').notNull(),
confirmerId: text('confirmer_id').notNull(),
authorizationMode: text('authorization_mode').notNull(),
reasonCode: text('reason_code').notNull(),
occurredAtMs: integer('occurred_at_ms').notNull(),
eventJson: text('event_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_quarantine_mutation_uidx').on(
table.mutationId,
),
uniqueIndex('ql3_plugin_package_quarantine_target_uidx').on(
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
),
foreignKey({
columns: [
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
table.installRecordDigest,
],
foreignColumns: [
pluginPackageInstalls.projectId,
pluginPackageInstalls.packageName,
pluginPackageInstalls.installationId,
pluginPackageInstalls.lockDigest,
pluginPackageInstalls.recordDigest,
],
name: 'ql3_plugin_package_quarantine_install_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
check(
'ql3_plugin_package_quarantine_identity_check',
sql`length(${table.mutationId}) between 1 and 128 and length(${table.packageName}) between 1 and 63 and length(${table.installationId}) between 1 and 128 and ${table.installState} in ('queued','staged','activating','active') and ${table.installVersion} between 1 and 2147483647`,
),
check(
'ql3_plugin_package_quarantine_state_check',
sql`(${table.installState} = 'active' and ${table.activeLockDigest} = ${table.lockDigest}) or (${table.installState} <> 'active' and (${table.activeLockDigest} is null or ${table.activeLockDigest} <> ${table.lockDigest}))`,
),
check(
'ql3_plugin_package_quarantine_subject_check',
sql`${table.proposerType} in ('user','api_app','mcp_client','agent','system','worker') and ${table.confirmerType} in ('user','api_app','mcp_client','agent','system','worker') and length(${table.proposerId}) between 1 and 255 and length(${table.confirmerId}) between 1 and 255 and ${table.authorizationMode} in ('dual_control','break_glass') and (${table.authorizationMode} = 'break_glass' or ${table.proposerType} <> ${table.confirmerType} or ${table.proposerId} <> ${table.confirmerId}) and ${table.reasonCode} in ('suspected_key_compromise','confirmed_key_compromise')`,
),
check(
'ql3_plugin_package_quarantine_digest_check',
sql`length(${table.eventDigest}) = 64 and ${table.eventDigest} not glob '*[^0-9a-f]*' and length(${table.revocationReceiptDigest}) = 64 and ${table.revocationReceiptDigest} not glob '*[^0-9a-f]*' and length(${table.impactDigest}) = 64 and ${table.impactDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.installRecordDigest}) = 64 and ${table.installRecordDigest} not glob '*[^0-9a-f]*' and (${table.activeLockDigest} is null or (length(${table.activeLockDigest}) = 64 and ${table.activeLockDigest} not glob '*[^0-9a-f]*'))`,
),
check(
'ql3_plugin_package_quarantine_json_check',
sql`length(${table.eventJson}) between 2 and 262144 and json_valid(${table.eventJson}) and json_type(${table.eventJson}) = 'object' and json_extract(${table.eventJson}, '$.schema') = 'qinglong/plugin-package-quarantine-event@v1' and json_extract(${table.eventJson}, '$.mutationId') = ${table.mutationId} and json_extract(${table.eventJson}, '$.revocationReceiptDigest') = ${table.revocationReceiptDigest} and json_extract(${table.eventJson}, '$.impactDigest') = ${table.impactDigest} and json_extract(${table.eventJson}, '$.target.projectId') = ${table.projectId} and json_extract(${table.eventJson}, '$.target.packageName') = ${table.packageName} and json_extract(${table.eventJson}, '$.target.installationId') = ${table.installationId} and json_extract(${table.eventJson}, '$.target.lockDigest') = ${table.lockDigest} and json_extract(${table.eventJson}, '$.target.installState') = ${table.installState} and json_extract(${table.eventJson}, '$.target.installVersion') = ${table.installVersion} and json_extract(${table.eventJson}, '$.target.installRecordDigest') = ${table.installRecordDigest} and ((${table.activeLockDigest} is null and json_type(${table.eventJson}, '$.target.activeLockDigest') = 'null') or json_extract(${table.eventJson}, '$.target.activeLockDigest') = ${table.activeLockDigest}) and json_extract(${table.eventJson}, '$.proposer.type') = ${table.proposerType} and json_extract(${table.eventJson}, '$.proposer.id') = ${table.proposerId} and json_extract(${table.eventJson}, '$.confirmer.type') = ${table.confirmerType} and json_extract(${table.eventJson}, '$.confirmer.id') = ${table.confirmerId} and json_extract(${table.eventJson}, '$.authorizationMode') = ${table.authorizationMode} and json_extract(${table.eventJson}, '$.reasonCode') = ${table.reasonCode} and json_extract(${table.eventJson}, '$.occurredAtMs') = ${table.occurredAtMs} and json_extract(${table.eventJson}, '$.eventDigest') = ${table.eventDigest}`,
),
check(
'ql3_plugin_package_quarantine_time_check',
sql`${table.occurredAtMs} >= 0`,
),
index('ql3_plugin_package_quarantine_lock_idx').on(
table.lockDigest,
table.projectId,
table.packageName,
),
index('ql3_plugin_package_quarantine_project_idx').on(
table.projectId,
table.packageName,
table.occurredAtMs,
table.eventDigest,
),
],
);
export const pluginPackageWithdrawalReceipts = sqliteTable(
'QingLong3PluginPackageWithdrawalReceipts',
{
eventDigest: text('event_digest')
.primaryKey()
.references(() => pluginPackageQuarantineEvents.eventDigest, {
onDelete: 'restrict',
}),
receiptDigest: text('receipt_digest').notNull(),
projectId: text('project_id').notNull(),
capabilityStatus: text('capability_status').notNull(),
taskCount: integer('task_count').notNull(),
previousActiveVectorDigest: text('previous_active_vector_digest'),
currentActiveVectorDigest: text('current_active_vector_digest'),
currentToolSnapshotDigest: text('current_tool_snapshot_digest'),
retainedSourceCount: integer('retained_source_count').notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_withdrawal_receipt_uidx').on(
table.receiptDigest,
),
foreignKey({
columns: [
table.projectId,
table.currentActiveVectorDigest,
table.currentToolSnapshotDigest,
],
foreignColumns: [
projectToolDefinitionSnapshots.projectId,
projectToolDefinitionSnapshots.activeVectorDigest,
projectToolDefinitionSnapshots.snapshotDigest,
],
name: 'ql3_plugin_package_withdrawal_snapshot_fk',
}).onDelete('restrict'),
check(
'ql3_plugin_package_withdrawal_disposition_check',
sql`(${table.capabilityStatus} = 'not_active' and ${table.taskCount} = 0 and ${table.previousActiveVectorDigest} is null and ${table.currentActiveVectorDigest} is null and ${table.currentToolSnapshotDigest} is null and ${table.retainedSourceCount} = 0) or (${table.capabilityStatus} = 'withdrawn' and ${table.taskCount} between 0 and 128 and ${table.previousActiveVectorDigest} is not null and ${table.currentActiveVectorDigest} is not null and ${table.previousActiveVectorDigest} <> ${table.currentActiveVectorDigest} and ${table.currentToolSnapshotDigest} is not null and ${table.retainedSourceCount} between 0 and 128)`,
),
check(
'ql3_plugin_package_withdrawal_digest_check',
sql`length(${table.eventDigest}) = 64 and ${table.eventDigest} not glob '*[^0-9a-f]*' and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*' and (${table.previousActiveVectorDigest} is null or (length(${table.previousActiveVectorDigest}) = 64 and ${table.previousActiveVectorDigest} not glob '*[^0-9a-f]*')) and (${table.currentActiveVectorDigest} is null or (length(${table.currentActiveVectorDigest}) = 64 and ${table.currentActiveVectorDigest} not glob '*[^0-9a-f]*')) and (${table.currentToolSnapshotDigest} is null or (length(${table.currentToolSnapshotDigest}) = 64 and ${table.currentToolSnapshotDigest} not glob '*[^0-9a-f]*'))`,
),
check(
'ql3_plugin_package_withdrawal_json_check',
sql`length(${table.receiptJson}) between 2 and 8388608 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-withdrawal-receipt@v1' and json_extract(${table.receiptJson}, '$.eventDigest') = ${table.eventDigest} and json_extract(${table.receiptJson}, '$.target.projectId') = ${table.projectId} and json_extract(${table.receiptJson}, '$.capability.status') = ${table.capabilityStatus} and json_array_length(json_extract(${table.receiptJson}, '$.capability.taskWithdrawals')) = ${table.taskCount} and ((${table.previousActiveVectorDigest} is null and json_type(${table.receiptJson}, '$.capability.previousActiveVectorDigest') = 'null') or json_extract(${table.receiptJson}, '$.capability.previousActiveVectorDigest') = ${table.previousActiveVectorDigest}) and ((${table.currentActiveVectorDigest} is null and json_type(${table.receiptJson}, '$.capability.currentActiveVectorDigest') = 'null') or json_extract(${table.receiptJson}, '$.capability.currentActiveVectorDigest') = ${table.currentActiveVectorDigest}) and ((${table.currentToolSnapshotDigest} is null and json_type(${table.receiptJson}, '$.capability.currentToolSnapshotDigest') = 'null') or json_extract(${table.receiptJson}, '$.capability.currentToolSnapshotDigest') = ${table.currentToolSnapshotDigest}) and json_extract(${table.receiptJson}, '$.capability.retainedSourceCount') = ${table.retainedSourceCount} and json_extract(${table.receiptJson}, '$.committedAtMs') = ${table.committedAtMs} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
check(
'ql3_plugin_package_withdrawal_time_check',
sql`${table.committedAtMs} >= 0`,
),
index('ql3_plugin_package_withdrawal_snapshot_idx').on(
table.currentToolSnapshotDigest,
table.eventDigest,
),
],
);
export const pluginPackageWithdrawalTasks = sqliteTable(
'QingLong3PluginPackageWithdrawalTasks',
{
eventDigest: text('event_digest')
.notNull()
.references(() => pluginPackageWithdrawalReceipts.eventDigest, {
onDelete: 'restrict',
}),
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
previousRevision: integer('previous_revision').notNull(),
disabledRevision: integer('disabled_revision').notNull(),
previousContentDigest: text('previous_content_digest').notNull(),
disabledContentDigest: text('disabled_content_digest').notNull(),
},
(table) => [
primaryKey({ columns: [table.eventDigest, table.taskId] }),
foreignKey({
columns: [table.projectId, table.taskId, table.previousRevision],
foreignColumns: [
taskDefinitionRevisions.projectId,
taskDefinitionRevisions.taskId,
taskDefinitionRevisions.revision,
],
name: 'ql3_plugin_package_withdrawal_task_previous_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.projectId, table.taskId, table.disabledRevision],
foreignColumns: [
taskDefinitionRevisions.projectId,
taskDefinitionRevisions.taskId,
taskDefinitionRevisions.revision,
],
name: 'ql3_plugin_package_withdrawal_task_disabled_fk',
}).onDelete('restrict'),
check(
'ql3_plugin_package_withdrawal_task_identity_check',
sql`length(${table.taskId}) between 1 and 128 and ${table.previousRevision} between 1 and 2147483646 and ${table.disabledRevision} = ${table.previousRevision} + 1`,
),
check(
'ql3_plugin_package_withdrawal_task_digest_check',
sql`length(${table.eventDigest}) = 64 and ${table.eventDigest} not glob '*[^0-9a-f]*' and length(${table.previousContentDigest}) = 64 and ${table.previousContentDigest} not glob '*[^0-9a-f]*' and length(${table.disabledContentDigest}) = 64 and ${table.disabledContentDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_plugin_package_withdrawal_task_task_idx').on(
table.projectId,
table.taskId,
table.eventDigest,
),
],
);
export const pluginPackageTaskOwnerships = sqliteTable(
'QingLong3PluginPackageTaskOwnerships',
{
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
packageName: text('package_name').notNull(),
claimedGenerationDigest: text('claimed_generation_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.taskId] }),
foreignKey({
columns: [table.projectId, table.taskId],
foreignColumns: [taskDefinitions.projectId, taskDefinitions.taskId],
name: 'ql3_plugin_package_task_ownership_task_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.projectId],
foreignColumns: [localProjects.id],
name: 'ql3_plugin_package_task_ownership_project_fk',
}).onDelete('restrict'),
check(
'ql3_plugin_package_task_ownership_identity_check',
sql`length(${table.taskId}) between 1 and 128 and length(${table.packageName}) between 1 and 63`,
),
check(
'ql3_plugin_package_task_ownership_digest_check',
sql`length(${table.claimedGenerationDigest}) = 64 and ${table.claimedGenerationDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_task_ownership_namespace_check',
sql`${table.taskId} like 'pkg:' || ${table.packageName} || ':%'`,
),
check(
'ql3_plugin_package_task_ownership_time_check',
sql`${table.createdAtMs} >= 0`,
),
index('ql3_plugin_package_task_ownership_package_idx').on(
table.projectId,
table.packageName,
table.taskId,
),
],
);
export const pluginPackageTaskReconciliations = sqliteTable(
'QingLong3PluginPackageTaskReconciliations',
{
generationDigest: text('generation_digest')
.primaryKey()
.references(() => pluginPackageMaterializedRevisions.generationDigest, {
onDelete: 'restrict',
}),
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
generation: integer('generation').notNull(),
materializedRevisionDigest: text('materialized_revision_digest').notNull(),
lockDigest: text('lock_digest').notNull(),
previousLockDigest: text('previous_lock_digest'),
receiptDigest: text('receipt_digest').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
},
(table) => [
check(
'ql3_plugin_package_task_reconciliation_identity_check',
sql`length(${table.packageName}) between 1 and 63 and ${table.generation} between 1 and 2147483647`,
),
check(
'ql3_plugin_package_task_reconciliation_digest_check',
sql`length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.materializedRevisionDigest}) = 64 and ${table.materializedRevisionDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and (${table.previousLockDigest} is null or (length(${table.previousLockDigest}) = 64 and ${table.previousLockDigest} not glob '*[^0-9a-f]*')) and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_task_reconciliation_json_check',
sql`length(${table.receiptJson}) between 2 and 8388608 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-task-reconciliation@v1' and json_extract(${table.receiptJson}, '$.generationDigest') = ${table.generationDigest} and json_extract(${table.receiptJson}, '$.projectId') = ${table.projectId} and json_extract(${table.receiptJson}, '$.packageName') = ${table.packageName} and json_extract(${table.receiptJson}, '$.generation') = ${table.generation} and json_extract(${table.receiptJson}, '$.materializedRevisionDigest') = ${table.materializedRevisionDigest} and json_extract(${table.receiptJson}, '$.lockDigest') = ${table.lockDigest} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
check(
'ql3_plugin_package_task_reconciliation_time_check',
sql`${table.committedAtMs} >= 0`,
),
uniqueIndex('ql3_plugin_package_task_reconciliation_generation_uidx').on(
table.projectId,
table.packageName,
table.generation,
),
uniqueIndex('ql3_plugin_package_task_reconciliation_receipt_uidx').on(
table.generationDigest,
table.receiptDigest,
),
index('ql3_plugin_package_task_reconciliation_lock_idx').on(
table.lockDigest,
table.generationDigest,
),
],
);
export const pluginPackageTaskReconciliationItems = sqliteTable(
'QingLong3PluginPackageTaskReconciliationItems',
{
generationDigest: text('generation_digest')
.notNull()
.references(() => pluginPackageTaskReconciliations.generationDigest, {
onDelete: 'restrict',
}),
taskId: text('task_id').notNull(),
revision: integer('revision').notNull(),
disposition: text('disposition').notNull(),
contentDigest: text('content_digest').notNull(),
},
(table) => [
primaryKey({ columns: [table.generationDigest, table.taskId] }),
check(
'ql3_plugin_package_task_reconciliation_item_identity_check',
sql`length(${table.taskId}) between 1 and 128 and ${table.revision} between 1 and 2147483647`,
),
check(
'ql3_plugin_package_task_reconciliation_item_disposition_check',
sql`${table.disposition} in ('already_disabled','created','disabled','retained','updated')`,
),
check(
'ql3_plugin_package_task_reconciliation_item_digest_check',
sql`length(${table.contentDigest}) = 64 and ${table.contentDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_plugin_package_task_reconciliation_item_task_idx').on(
table.taskId,
table.generationDigest,
),
],
);
export const triggers = sqliteTable(
'QingLong3Triggers',
{
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
triggerId: text('trigger_id').notNull(),
taskId: text('task_id').notNull(),
currentRevision: integer('current_revision').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.triggerId] }),
foreignKey({
columns: [table.projectId, table.taskId],
foreignColumns: [taskDefinitions.projectId, taskDefinitions.taskId],
name: 'ql3_triggers_task_fk',
}).onDelete('restrict'),
check(
'ql3_triggers_id_check',
sql`length(${table.projectId}) between 1 and 128 and length(${table.triggerId}) between 1 and 128 and length(${table.taskId}) between 1 and 128 and ${table.projectId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*' and ${table.triggerId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*' and ${table.taskId} not glob '*[' || char(0) || '-' || char(31) || char(127) || ']*'`,
),
check(
'ql3_triggers_revision_check',
sql`${table.currentRevision} between 1 and 2147483647`,
),
check(
'ql3_triggers_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
uniqueIndex('ql3_triggers_task_uidx').on(
table.projectId,
table.triggerId,
table.taskId,
),
],
);
export const triggerRevisions = sqliteTable(
'QingLong3TriggerRevisions',
{
projectId: text('project_id').notNull(),
triggerId: text('trigger_id').notNull(),
revision: integer('revision').notNull(),
mutationId: text('mutation_id').notNull(),
taskId: text('task_id').notNull(),
taskRevision: integer('task_revision').notNull(),
taskContentDigest: text('task_content_digest').notNull(),
specJson: text('spec_json').notNull(),
enabled: integer('enabled', { mode: 'boolean' }).notNull(),
contentDigest: text('content_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.triggerId, table.revision] }),
foreignKey({
columns: [table.projectId, table.triggerId, table.taskId],
foreignColumns: [triggers.projectId, triggers.triggerId, triggers.taskId],
name: 'ql3_trigger_revisions_trigger_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.projectId, table.taskId, table.taskRevision],
foreignColumns: [
taskDefinitionRevisions.projectId,
taskDefinitionRevisions.taskId,
taskDefinitionRevisions.revision,
],
name: 'ql3_trigger_revisions_task_revision_fk',
}).onDelete('restrict'),
check(
'ql3_trigger_revisions_revision_check',
sql`${table.revision} between 1 and 2147483647 and ${table.taskRevision} between 1 and 2147483647`,
),
check(
'ql3_trigger_revisions_mutation_check',
sql`length(${table.mutationId}) = 36 and substr(${table.mutationId}, 9, 1) = '-' and substr(${table.mutationId}, 14, 1) = '-' and substr(${table.mutationId}, 19, 1) = '-' and substr(${table.mutationId}, 24, 1) = '-' and replace(${table.mutationId}, '-', '') not glob '*[^0-9a-f]*'`,
),
check(
'ql3_trigger_revisions_task_digest_check',
sql`length(${table.taskContentDigest}) = 64 and ${table.taskContentDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_trigger_revisions_spec_check',
sql`json_valid(${table.specJson}) and json_type(${table.specJson}) = 'object' and length(${table.specJson}) between 1 and 16384`,
),
check(
'ql3_trigger_revisions_enabled_check',
sql`${table.enabled} in (0, 1)`,
),
check(
'ql3_trigger_revisions_digest_check',
sql`length(${table.contentDigest}) = 64 and ${table.contentDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_trigger_revisions_created_check',
sql`${table.createdAtMs} >= 0`,
),
uniqueIndex('ql3_trigger_revisions_mutation_uidx').on(table.mutationId),
index('ql3_trigger_revisions_project_enabled_idx').on(
table.projectId,
table.enabled,
table.triggerId,
table.revision,
),
index('ql3_trigger_revisions_task_idx').on(
table.projectId,
table.taskId,
table.taskRevision,
table.triggerId,
table.revision,
),
],
);
export const localTriggerSchedules = sqliteTable(
'QingLong3LocalTriggerSchedules',
{
projectId: text('project_id').notNull(),
triggerId: text('trigger_id').notNull(),
triggerRevision: integer('trigger_revision').notNull(),
nextFireAtMs: integer('next_fire_at_ms'),
lastScheduledAtMs: integer('last_scheduled_at_ms'),
stateVersion: integer('state_version').notNull().default(0),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.triggerId] }),
foreignKey({
columns: [table.projectId, table.triggerId],
foreignColumns: [triggers.projectId, triggers.triggerId],
name: 'ql3_local_trigger_schedules_trigger_fk',
}).onDelete('restrict'),
check(
'ql3_local_trigger_schedules_revision_check',
sql`${table.triggerRevision} between 1 and 2147483647`,
),
check(
'ql3_local_trigger_schedules_state_check',
sql`${table.stateVersion} >= 0`,
),
check(
'ql3_local_trigger_schedules_time_check',
sql`${table.updatedAtMs} >= 0 and (${table.nextFireAtMs} is null or ${table.nextFireAtMs} >= 0) and (${table.lastScheduledAtMs} is null or ${table.lastScheduledAtMs} >= 0)`,
),
index('ql3_local_trigger_schedules_due_idx')
.on(table.nextFireAtMs, table.projectId, table.triggerId)
.where(sql`${table.nextFireAtMs} is not null`),
index('ql3_local_trigger_schedules_initialize_idx')
.on(table.projectId, table.triggerId)
.where(sql`${table.nextFireAtMs} is null`),
],
);
export const localProjectRoleBindings = sqliteTable(
'QingLong3ProjectRoleBindings',
{
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
version: integer('version').notNull(),
state: text('state').notNull(),
role: text('role'),
mutationId: text('mutation_id').notNull(),
changedByType: text('changed_by_type').notNull(),
changedById: text('changed_by_id').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
primaryKey({
columns: [
table.projectId,
table.subjectType,
table.subjectId,
table.version,
],
}),
check(
'ql3_local_bindings_subject_type_check',
sql`${table.subjectType} in ('user','api_app','mcp_client','agent','system','worker')`,
),
check(
'ql3_local_bindings_subject_id_check',
sql`length(${table.subjectId}) between 1 and 255`,
),
check(
'ql3_local_bindings_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_bindings_state_role_check',
sql`(${table.state} = 'active' and ${table.role} in ('owner','admin','operator','viewer')) or (${table.state} = 'revoked' and ${table.role} is null)`,
),
check(
'ql3_local_bindings_mutation_check',
sql`length(${table.mutationId}) between 1 and 64`,
),
check(
'ql3_local_bindings_changed_by_type_check',
sql`${table.changedByType} in ('user','api_app','mcp_client','agent','system','worker')`,
),
check(
'ql3_local_bindings_changed_by_id_check',
sql`length(${table.changedById}) between 1 and 255`,
),
check('ql3_local_bindings_created_check', sql`${table.createdAtMs} >= 0`),
uniqueIndex('ql3_local_bindings_mutation_uidx').on(
table.projectId,
table.subjectType,
table.subjectId,
table.mutationId,
),
index('ql3_local_bindings_current_idx').on(
table.projectId,
table.subjectType,
table.subjectId,
sql`${table.version} desc`,
),
index('ql3_local_bindings_project_idx').on(
table.projectId,
sql`${table.version} desc`,
),
],
);
export const localSecurityAuditEvents = sqliteTable(
'QingLong3SecurityAuditEvents',
{
eventId: text('event_id').primaryKey(),
requestId: text('request_id').notNull(),
operationId: text('operation_id').notNull(),
projectId: text('project_id').references(() => localProjects.id, {
onDelete: 'restrict',
}),
subjectType: text('subject_type'),
subjectId: text('subject_id'),
authenticationId: text('authentication_id'),
outcome: text('outcome').notNull(),
reasonsJson: text('reasons_json', { mode: 'json' })
.$type<readonly string[]>()
.notNull(),
fenceProjectVersion: integer('fence_project_version'),
fenceBindingVersion: integer('fence_binding_version'),
occurredAtMs: integer('occurred_at_ms').notNull(),
},
(table) => [
check('ql3_local_audit_event_check', sql`length(${table.eventId}) = 36`),
check(
'ql3_local_audit_request_check',
sql`length(${table.requestId}) between 1 and 128`,
),
check(
'ql3_local_audit_operation_check',
sql`length(${table.operationId}) between 1 and 128`,
),
check(
'ql3_local_audit_subject_check',
sql`(${table.subjectType} is null and ${table.subjectId} is null and ${table.authenticationId} is null) or (${table.subjectType} in ('user','api_app','mcp_client','agent','system','worker') and length(${table.subjectId}) between 1 and 255 and length(${table.authenticationId}) between 1 and 128)`,
),
check(
'ql3_local_audit_outcome_check',
sql`${table.outcome} in ('authentication_rejected','authentication_unavailable','authorization_unavailable','denied','approval_required','allowed')`,
),
check(
'ql3_local_audit_reasons_check',
sql`json_valid(${table.reasonsJson}) and json_type(${table.reasonsJson}) = 'array' and json_array_length(${table.reasonsJson}) between 1 and 8 and length(${table.reasonsJson}) <= 2048`,
),
check(
'ql3_local_audit_fence_check',
sql`(${table.fenceProjectVersion} is null and ${table.fenceBindingVersion} is null) or (${table.fenceProjectVersion} between 1 and 2147483647 and (${table.fenceBindingVersion} is null or ${table.fenceBindingVersion} between 1 and 2147483647))`,
),
check('ql3_local_audit_time_check', sql`${table.occurredAtMs} >= 0`),
index('ql3_local_audit_project_time_idx').on(
table.projectId,
sql`${table.occurredAtMs} desc`,
sql`${table.eventId} desc`,
),
index('ql3_local_audit_subject_time_idx').on(
table.subjectType,
table.subjectId,
sql`${table.occurredAtMs} desc`,
sql`${table.eventId} desc`,
),
],
);
export const localProjectAdministrationMutations = sqliteTable(
'QingLong3ProjectAdministrationMutations',
{
mutationId: text('mutation_id').primaryKey(),
operation: text('operation').notNull(),
authorityProjectId: text('authority_project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
projectName: text('project_name').notNull(),
projectSlug: text('project_slug').notNull(),
projectStatus: text('project_status').notNull(),
projectVersion: integer('project_version').notNull(),
expectedPreviousVersion: integer('expected_previous_version').notNull(),
changedByType: text('changed_by_type').notNull(),
changedById: text('changed_by_id').notNull(),
initialOwnerBindingVersion: integer('initial_owner_binding_version'),
auditEventId: text('audit_event_id')
.notNull()
.unique()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
projectCreatedAtMs: integer('project_created_at_ms').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_project_admin_mutation_id_check',
sql`length(${table.mutationId}) = 36 and substr(${table.mutationId}, 9, 1) = '-' and substr(${table.mutationId}, 14, 1) = '-' and substr(${table.mutationId}, 19, 1) = '-' and substr(${table.mutationId}, 24, 1) = '-' and replace(${table.mutationId}, '-', '') not glob '*[^0-9a-f]*'`,
),
check(
'ql3_project_admin_operation_check',
sql`${table.operation} in ('create','archive','restore')`,
),
check(
'ql3_project_admin_identity_check',
sql`length(${table.authorityProjectId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.projectName}) between 1 and 255 and length(${table.projectSlug}) between 1 and 128 and ${table.projectSlug} = lower(${table.projectSlug}) and ${table.projectSlug} not glob '*[^a-z0-9-]*' and substr(${table.projectSlug}, 1, 1) not glob '[^a-z0-9]' and substr(${table.projectSlug}, -1, 1) not glob '[^a-z0-9]'`,
),
check(
'ql3_project_admin_transition_check',
sql`${table.projectVersion} = ${table.expectedPreviousVersion} + 1 and ${table.projectVersion} between 1 and 2147483647 and ${table.expectedPreviousVersion} between 0 and 2147483646 and ((${table.operation} = 'create' and ${table.expectedPreviousVersion} = 0 and ${table.projectStatus} = 'active' and ${table.initialOwnerBindingVersion} = 1) or (${table.operation} = 'archive' and ${table.expectedPreviousVersion} > 0 and ${table.projectStatus} = 'archived' and ${table.initialOwnerBindingVersion} is null) or (${table.operation} = 'restore' and ${table.expectedPreviousVersion} > 0 and ${table.projectStatus} = 'active' and ${table.initialOwnerBindingVersion} is null))`,
),
check(
'ql3_project_admin_actor_check',
sql`${table.changedByType} = 'user' and length(${table.changedById}) between 1 and 255`,
),
check(
'ql3_project_admin_audit_check',
sql`${table.auditEventId} = ${table.mutationId}`,
),
check(
'ql3_project_admin_time_check',
sql`${table.projectCreatedAtMs} >= 0 and ${table.createdAtMs} >= ${table.projectCreatedAtMs}`,
),
uniqueIndex('ql3_project_admin_project_version_uidx').on(
table.projectId,
table.projectVersion,
),
index('ql3_project_admin_authority_time_idx').on(
table.authorityProjectId,
sql`${table.createdAtMs} desc`,
sql`${table.mutationId} desc`,
),
],
);
export const localSecurityAuditCompactions = sqliteTable(
'QingLong3SecurityAuditCompactions',
{
mutationId: text('mutation_id').primaryKey(),
requestId: text('request_id').notNull(),
authorityProjectId: text('authority_project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
retentionMs: integer('retention_ms').notNull(),
eligibleBeforeMs: integer('eligible_before_ms').notNull(),
batchLimit: integer('batch_limit').notNull(),
deletedCount: integer('deleted_count').notNull(),
deletedPayloadBytes: integer('deleted_payload_bytes').notNull(),
firstOccurredAtMs: integer('first_occurred_at_ms'),
firstEventId: text('first_event_id'),
lastOccurredAtMs: integer('last_occurred_at_ms'),
lastEventId: text('last_event_id'),
recordsDigest: text('records_digest').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.unique()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_audit_compaction_mutation_check',
sql`length(${table.mutationId}) = 36 and substr(${table.mutationId}, 9, 1) = '-' and substr(${table.mutationId}, 14, 1) = '-' and substr(${table.mutationId}, 19, 1) = '-' and substr(${table.mutationId}, 24, 1) = '-' and replace(${table.mutationId}, '-', '') not glob '*[^0-9a-f]*'`,
),
check(
'ql3_audit_compaction_request_check',
sql`length(${table.requestId}) between 1 and 128`,
),
check(
'ql3_audit_compaction_authority_check',
sql`length(${table.authorityProjectId}) between 1 and 128`,
),
check(
'ql3_audit_compaction_policy_check',
sql`${table.retentionMs} between 2592000000 and 315360000000 and ${table.eligibleBeforeMs} >= 0 and ${table.eligibleBeforeMs} + ${table.retentionMs} <= ${table.createdAtMs} and ${table.batchLimit} between 1 and 512`,
),
check(
'ql3_audit_compaction_result_check',
sql`${table.deletedCount} between 0 and ${table.batchLimit} and ${table.deletedPayloadBytes} between 0 and 16777216 and length(${table.recordsDigest}) = 64 and ${table.recordsDigest} not glob '*[^0-9a-f]*' and ((${table.deletedCount} = 0 and ${table.deletedPayloadBytes} = 0 and ${table.firstOccurredAtMs} is null and ${table.firstEventId} is null and ${table.lastOccurredAtMs} is null and ${table.lastEventId} is null) or (${table.deletedCount} > 0 and ${table.deletedPayloadBytes} > 0 and ${table.firstOccurredAtMs} >= 0 and ${table.lastOccurredAtMs} >= ${table.firstOccurredAtMs} and length(${table.firstEventId}) = 36 and length(${table.lastEventId}) = 36))`,
),
check(
'ql3_audit_compaction_audit_check',
sql`${table.auditEventId} = ${table.mutationId}`,
),
check(
'ql3_audit_compaction_time_check',
sql`${table.createdAtMs} >= 2592000000`,
),
index('ql3_audit_compaction_authority_time_idx').on(
table.authorityProjectId,
sql`${table.createdAtMs} desc`,
sql`${table.mutationId} desc`,
),
],
);
export const toolExecutionTraceAnchors = sqliteTable(
'ToolExecutionTraceAnchors',
{
traceId: text('trace_id').notNull(),
spanId: text('span_id').notNull(),
parentSpanId: text('parent_span_id'),
projectId: text('project_id').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
invocationPlanDigest: text('invocation_plan_digest').notNull(),
bindingDigest: text('binding_digest').notNull(),
adapterDigest: text('adapter_digest').notNull(),
redactionContractDigest: text('redaction_contract_digest').notNull(),
auditContractDigest: text('audit_contract_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
traceDigest: text('trace_digest').notNull(),
traceJson: text('trace_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
primaryKey({
columns: [table.traceId, table.spanId],
name: 'tool_execution_trace_anchors_pkey',
}),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_tool_execution_trace_step_fk',
}).onDelete('cascade'),
check(
'ql3_tool_execution_trace_identity_check',
sql`length(${table.traceId}) = 32 and ${table.traceId} not glob '*[^0-9a-f]*' and length(${table.spanId}) = 16 and ${table.spanId} not glob '*[^0-9a-f]*' and (${table.parentSpanId} is null or (length(${table.parentSpanId}) = 16 and ${table.parentSpanId} not glob '*[^0-9a-f]*' and ${table.parentSpanId} <> ${table.spanId})) and length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128`,
),
check(
'ql3_tool_execution_trace_digest_check',
sql`length(${table.invocationPlanDigest}) = 64 and ${table.invocationPlanDigest} not glob '*[^0-9a-f]*' and length(${table.bindingDigest}) = 64 and ${table.bindingDigest} not glob '*[^0-9a-f]*' and length(${table.adapterDigest}) = 64 and ${table.adapterDigest} not glob '*[^0-9a-f]*' and length(${table.redactionContractDigest}) = 64 and ${table.redactionContractDigest} not glob '*[^0-9a-f]*' and length(${table.auditContractDigest}) = 64 and ${table.auditContractDigest} not glob '*[^0-9a-f]*' and length(${table.traceDigest}) = 64 and ${table.traceDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_execution_trace_time_check',
sql`${table.createdAtMs} >= 0`,
),
check(
'ql3_tool_execution_trace_json_check',
sql`length(cast(${table.traceJson} as blob)) between 2 and 16384 and json_valid(${table.traceJson}) and json_type(${table.traceJson}) = 'object' and json_extract(${table.traceJson}, '$.schema') = 'qinglong/tool-execution-trace-anchor@v1' and json_extract(${table.traceJson}, '$.traceId') = ${table.traceId} and json_extract(${table.traceJson}, '$.spanId') = ${table.spanId} and json_extract(${table.traceJson}, '$.parentSpanId') is ${table.parentSpanId} and json_extract(${table.traceJson}, '$.projectId') = ${table.projectId} and json_extract(${table.traceJson}, '$.runId') = ${table.runId} and json_extract(${table.traceJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.traceJson}, '$.invocationPlanDigest') = ${table.invocationPlanDigest} and json_extract(${table.traceJson}, '$.bindingDigest') = ${table.bindingDigest} and json_extract(${table.traceJson}, '$.adapterDigest') = ${table.adapterDigest} and json_extract(${table.traceJson}, '$.redactionContractDigest') = ${table.redactionContractDigest} and json_extract(${table.traceJson}, '$.auditContractDigest') = ${table.auditContractDigest} and json_extract(${table.traceJson}, '$.createdAtMs') is ${table.createdAtMs} and json_extract(${table.traceJson}, '$.traceDigest') = ${table.traceDigest}`,
),
index('ql3_tool_execution_trace_run_idx').on(
table.runId,
table.createdAtMs,
table.traceId,
table.spanId,
),
index('ql3_tool_execution_trace_step_idx').on(
table.runId,
table.stepRunId,
table.createdAtMs,
table.traceId,
table.spanId,
),
],
);
export const toolExecutionAuditReceipts = sqliteTable(
'ToolExecutionAuditReceipts',
{
eventId: text('event_id')
.primaryKey()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
projectId: text('project_id').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
traceId: text('trace_id').notNull(),
spanId: text('span_id').notNull(),
traceDigest: text('trace_digest').notNull(),
invocationPlanDigest: text('invocation_plan_digest').notNull(),
bindingDigest: text('binding_digest').notNull(),
auditRecordDigest: text('audit_record_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
receiptDigest: text('receipt_digest').notNull(),
auditJson: text('audit_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.traceId, table.spanId],
foreignColumns: [
toolExecutionTraceAnchors.traceId,
toolExecutionTraceAnchors.spanId,
],
name: 'ql3_tool_execution_audit_trace_fk',
}).onDelete('cascade'),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_tool_execution_audit_step_fk',
}).onDelete('cascade'),
check(
'ql3_tool_execution_audit_identity_check',
sql`length(${table.eventId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.traceId}) = 32 and ${table.traceId} not glob '*[^0-9a-f]*' and length(${table.spanId}) = 16 and ${table.spanId} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_execution_audit_digest_check',
sql`length(${table.traceDigest}) = 64 and ${table.traceDigest} not glob '*[^0-9a-f]*' and length(${table.invocationPlanDigest}) = 64 and ${table.invocationPlanDigest} not glob '*[^0-9a-f]*' and length(${table.bindingDigest}) = 64 and ${table.bindingDigest} not glob '*[^0-9a-f]*' and length(${table.auditRecordDigest}) = 64 and ${table.auditRecordDigest} not glob '*[^0-9a-f]*' and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_execution_audit_time_check',
sql`${table.createdAtMs} >= 0`,
),
check(
'ql3_tool_execution_audit_json_check',
sql`length(cast(${table.auditJson} as blob)) between 2 and 8192 and json_valid(${table.auditJson}) and json_type(${table.auditJson}) = 'object' and json_extract(${table.auditJson}, '$.eventId') = ${table.eventId} and json_extract(${table.auditJson}, '$.projectId') = ${table.projectId} and json_extract(${table.auditJson}, '$.operationId') = 'tool.invoke.start' and json_extract(${table.auditJson}, '$.outcome') = 'allowed' and json_type(${table.auditJson}, '$.fence') = 'object' and json_extract(${table.auditJson}, '$.occurredAtMs') is ${table.createdAtMs}`,
),
check(
'ql3_tool_execution_audit_receipt_json_check',
sql`length(cast(${table.receiptJson} as blob)) between 2 and 16384 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/tool-execution-audit-receipt@v1' and json_extract(${table.receiptJson}, '$.eventId') = ${table.eventId} and json_extract(${table.receiptJson}, '$.projectId') = ${table.projectId} and json_extract(${table.receiptJson}, '$.runId') = ${table.runId} and json_extract(${table.receiptJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.receiptJson}, '$.traceId') = ${table.traceId} and json_extract(${table.receiptJson}, '$.spanId') = ${table.spanId} and json_extract(${table.receiptJson}, '$.traceDigest') = ${table.traceDigest} and json_extract(${table.receiptJson}, '$.invocationPlanDigest') = ${table.invocationPlanDigest} and json_extract(${table.receiptJson}, '$.bindingDigest') = ${table.bindingDigest} and json_extract(${table.receiptJson}, '$.auditRecordDigest') = ${table.auditRecordDigest} and json_extract(${table.receiptJson}, '$.createdAtMs') is ${table.createdAtMs} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
uniqueIndex('ql3_tool_execution_audit_trace_uidx').on(
table.traceId,
table.spanId,
),
index('ql3_tool_execution_audit_run_idx').on(
table.runId,
table.createdAtMs,
table.traceId,
table.spanId,
),
index('ql3_tool_execution_audit_step_idx').on(
table.runId,
table.stepRunId,
table.createdAtMs,
table.eventId,
),
],
);
export const toolExecutionStartBarriers = sqliteTable(
'ToolExecutionStartBarriers',
{
startId: text('start_id').primaryKey(),
projectId: text('project_id').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
startedStepRunVersion: integer('started_step_run_version').notNull(),
stepRunMutationId: text('step_run_mutation_id')
.notNull()
.references(() => stepRunMutations.mutationId, {
onDelete: 'restrict',
}),
runEventId: text('run_event_id')
.notNull()
.references(() => runEvents.id, { onDelete: 'restrict' }),
traceId: text('trace_id').notNull(),
spanId: text('span_id').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.references(() => toolExecutionAuditReceipts.eventId, {
onDelete: 'restrict',
}),
commandDigest: text('command_digest').notNull(),
barrierDigest: text('barrier_digest').notNull(),
startedAtMs: integer('started_at_ms').notNull(),
barrierJson: text('barrier_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_tool_start_step_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.traceId, table.spanId],
foreignColumns: [
toolExecutionTraceAnchors.traceId,
toolExecutionTraceAnchors.spanId,
],
name: 'ql3_tool_start_trace_fk',
}).onDelete('restrict'),
check(
'ql3_tool_start_identity_check',
sql`length(${table.startId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.stepRunMutationId}) between 1 and 128 and length(${table.runEventId}) between 1 and 128 and length(${table.traceId}) = 32 and ${table.traceId} not glob '*[^0-9a-f]*' and length(${table.spanId}) = 16 and ${table.spanId} not glob '*[^0-9a-f]*' and length(${table.auditEventId}) between 1 and 128`,
),
check(
'ql3_tool_start_version_time_check',
sql`${table.startedStepRunVersion} between 2 and 2147483647 and ${table.startedAtMs} >= 0`,
),
check(
'ql3_tool_start_digest_check',
sql`length(${table.commandDigest}) = 64 and ${table.commandDigest} not glob '*[^0-9a-f]*' and length(${table.barrierDigest}) = 64 and ${table.barrierDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_start_json_check',
sql`length(cast(${table.barrierJson} as blob)) between 2 and 16384 and json_valid(${table.barrierJson}) and json_type(${table.barrierJson}) = 'object' and json_extract(${table.barrierJson}, '$.schema') = 'qinglong/tool-execution-start-barrier@v1' and json_extract(${table.barrierJson}, '$.startId') = ${table.startId} and json_extract(${table.barrierJson}, '$.projectId') = ${table.projectId} and json_extract(${table.barrierJson}, '$.runId') = ${table.runId} and json_extract(${table.barrierJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.barrierJson}, '$.startedStepRunVersion') is ${table.startedStepRunVersion} and json_extract(${table.barrierJson}, '$.stepRunMutationId') = ${table.stepRunMutationId} and json_extract(${table.barrierJson}, '$.runEventId') = ${table.runEventId} and json_extract(${table.barrierJson}, '$.traceId') = ${table.traceId} and json_extract(${table.barrierJson}, '$.spanId') = ${table.spanId} and json_extract(${table.barrierJson}, '$.auditEventId') = ${table.auditEventId} and json_extract(${table.barrierJson}, '$.commandDigest') = ${table.commandDigest} and json_extract(${table.barrierJson}, '$.barrierDigest') = ${table.barrierDigest} and json_extract(${table.barrierJson}, '$.startedAtMs') is ${table.startedAtMs}`,
),
uniqueIndex('ql3_tool_start_step_version_uidx').on(
table.runId,
table.stepRunId,
table.startedStepRunVersion,
),
uniqueIndex('ql3_tool_start_mutation_uidx').on(table.stepRunMutationId),
uniqueIndex('ql3_tool_start_event_uidx').on(table.runEventId),
uniqueIndex('ql3_tool_start_trace_uidx').on(table.traceId, table.spanId),
uniqueIndex('ql3_tool_start_audit_uidx').on(table.auditEventId),
index('ql3_tool_start_run_time_idx').on(
table.runId,
table.startedAtMs,
table.startId,
),
],
);
export const toolInvocationInputArtifacts = sqliteTable(
'ToolInvocationInputArtifacts',
{
artifactId: text('artifact_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
actionRef: text('action_ref').notNull(),
inputDigest: text('input_digest').notNull(),
invocationActionDigest: text('invocation_action_digest').notNull(),
artifactDigest: text('artifact_digest').notNull(),
keyId: text('key_id').notNull(),
algorithm: text('algorithm').notNull(),
plaintextBytes: integer('plaintext_bytes').notNull(),
sealedAtMs: integer('sealed_at_ms').notNull(),
artifactJson: text('artifact_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
check(
'ql3_tool_input_artifact_identity_check',
sql`length(${table.artifactId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.actionRef}) between 1 and 255 and length(${table.keyId}) between 1 and 128 and ${table.algorithm} = 'aes-256-gcm'`,
),
check(
'ql3_tool_input_artifact_digest_check',
sql`length(${table.inputDigest}) = 64 and ${table.inputDigest} not glob '*[^0-9a-f]*' and length(${table.invocationActionDigest}) = 64 and ${table.invocationActionDigest} not glob '*[^0-9a-f]*' and length(${table.artifactDigest}) = 64 and ${table.artifactDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_input_artifact_budget_check',
sql`${table.plaintextBytes} between 0 and 65536 and ${table.sealedAtMs} >= 0`,
),
check(
'ql3_tool_input_artifact_json_check',
sql`length(cast(${table.artifactJson} as blob)) between 2 and 98304 and json_valid(${table.artifactJson}) and json_type(${table.artifactJson}) = 'object' and json_extract(${table.artifactJson}, '$.schema') = 'qinglong/tool-invocation-input-artifact@v1' and json_extract(${table.artifactJson}, '$.artifactId') = ${table.artifactId} and json_extract(${table.artifactJson}, '$.projectId') = ${table.projectId} and json_extract(${table.artifactJson}, '$.actionRef') = ${table.actionRef} and json_extract(${table.artifactJson}, '$.inputDigest') = ${table.inputDigest} and json_extract(${table.artifactJson}, '$.invocationActionDigest') = ${table.invocationActionDigest} and json_extract(${table.artifactJson}, '$.artifactDigest') = ${table.artifactDigest} and json_extract(${table.artifactJson}, '$.keyId') = ${table.keyId} and json_extract(${table.artifactJson}, '$.algorithm') = ${table.algorithm} and json_extract(${table.artifactJson}, '$.plaintextBytes') is ${table.plaintextBytes} and json_extract(${table.artifactJson}, '$.sealedAtMs') is ${table.sealedAtMs}`,
),
uniqueIndex('ql3_tool_input_artifact_action_uidx').on(
table.projectId,
table.actionRef,
),
uniqueIndex('ql3_tool_input_artifact_start_binding_uidx').on(
table.artifactId,
table.artifactDigest,
table.projectId,
table.actionRef,
table.inputDigest,
),
index('ql3_tool_input_artifact_project_time_idx').on(
table.projectId,
table.sealedAtMs,
table.artifactId,
),
],
);
export const toolInvocationPreviewArtifacts = sqliteTable(
'ToolInvocationPreviewArtifacts',
{
artifactId: text('artifact_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
actionRef: text('action_ref').notNull(),
actionDigest: text('action_digest').notNull(),
previewDigest: text('preview_digest').notNull(),
redactionContractDigest: text('redaction_contract_digest').notNull(),
artifactDigest: text('artifact_digest').notNull(),
byteLength: integer('byte_length').notNull(),
sealedAtMs: integer('sealed_at_ms').notNull(),
artifactJson: text('artifact_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
check(
'ql3_tool_preview_artifact_identity_check',
sql`length(${table.artifactId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.actionRef}) between 1 and 255`,
),
check(
'ql3_tool_preview_artifact_digest_check',
sql`length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and length(${table.previewDigest}) = 64 and ${table.previewDigest} not glob '*[^0-9a-f]*' and length(${table.redactionContractDigest}) = 64 and ${table.redactionContractDigest} not glob '*[^0-9a-f]*' and length(${table.artifactDigest}) = 64 and ${table.artifactDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_preview_artifact_budget_check',
sql`${table.byteLength} between 2 and 8192 and ${table.sealedAtMs} >= 0`,
),
check(
'ql3_tool_preview_artifact_json_check',
sql`length(cast(${table.artifactJson} as blob)) between 2 and 16384 and json_valid(${table.artifactJson}) and json_type(${table.artifactJson}) = 'object' and json_extract(${table.artifactJson}, '$.schema') = 'qinglong/tool-invocation-preview-artifact@v1' and json_extract(${table.artifactJson}, '$.artifactId') = ${table.artifactId} and json_extract(${table.artifactJson}, '$.projectId') = ${table.projectId} and json_extract(${table.artifactJson}, '$.actionRef') = ${table.actionRef} and json_extract(${table.artifactJson}, '$.actionDigest') = ${table.actionDigest} and json_extract(${table.artifactJson}, '$.previewDigest') = ${table.previewDigest} and json_extract(${table.artifactJson}, '$.redactionContractDigest') = ${table.redactionContractDigest} and json_extract(${table.artifactJson}, '$.artifactDigest') = ${table.artifactDigest} and json_extract(${table.artifactJson}, '$.byteLength') is ${table.byteLength} and json_extract(${table.artifactJson}, '$.sealedAtMs') is ${table.sealedAtMs}`,
),
uniqueIndex('ql3_tool_preview_artifact_action_uidx').on(
table.projectId,
table.actionRef,
),
uniqueIndex('ql3_tool_preview_artifact_action_digest_uidx').on(
table.actionDigest,
),
uniqueIndex('ql3_tool_preview_artifact_start_binding_uidx').on(
table.artifactId,
table.artifactDigest,
table.projectId,
table.actionRef,
table.actionDigest,
table.previewDigest,
table.redactionContractDigest,
),
index('ql3_tool_preview_artifact_project_time_idx').on(
table.projectId,
table.sealedAtMs,
table.artifactId,
),
],
);
export const toolExecutionStartArtifactBindings = sqliteTable(
'ToolExecutionStartArtifactBindings',
{
startId: text('start_id')
.primaryKey()
.references(() => toolExecutionStartBarriers.startId, {
onDelete: 'restrict',
}),
projectId: text('project_id').notNull(),
actionRef: text('action_ref').notNull(),
inputArtifactId: text('input_artifact_id').notNull(),
inputArtifactDigest: text('input_artifact_digest').notNull(),
inputDigest: text('input_digest').notNull(),
previewArtifactId: text('preview_artifact_id').notNull(),
previewArtifactDigest: text('preview_artifact_digest').notNull(),
actionDigest: text('action_digest').notNull(),
previewDigest: text('preview_digest').notNull(),
redactionContractDigest: text('redaction_contract_digest').notNull(),
boundAtMs: integer('bound_at_ms').notNull(),
},
(table) => [
foreignKey({
columns: [
table.inputArtifactId,
table.inputArtifactDigest,
table.projectId,
table.actionRef,
table.inputDigest,
],
foreignColumns: [
toolInvocationInputArtifacts.artifactId,
toolInvocationInputArtifacts.artifactDigest,
toolInvocationInputArtifacts.projectId,
toolInvocationInputArtifacts.actionRef,
toolInvocationInputArtifacts.inputDigest,
],
name: 'ql3_tool_start_input_artifact_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.previewArtifactId,
table.previewArtifactDigest,
table.projectId,
table.actionRef,
table.actionDigest,
table.previewDigest,
table.redactionContractDigest,
],
foreignColumns: [
toolInvocationPreviewArtifacts.artifactId,
toolInvocationPreviewArtifacts.artifactDigest,
toolInvocationPreviewArtifacts.projectId,
toolInvocationPreviewArtifacts.actionRef,
toolInvocationPreviewArtifacts.actionDigest,
toolInvocationPreviewArtifacts.previewDigest,
toolInvocationPreviewArtifacts.redactionContractDigest,
],
name: 'ql3_tool_start_preview_artifact_fk',
}).onDelete('restrict'),
check(
'ql3_tool_start_artifact_identity_check',
sql`length(${table.startId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.actionRef}) between 1 and 255 and length(${table.inputArtifactId}) between 1 and 128 and length(${table.previewArtifactId}) between 1 and 128`,
),
check(
'ql3_tool_start_artifact_digest_check',
sql`length(${table.inputArtifactDigest}) = 64 and ${table.inputArtifactDigest} not glob '*[^0-9a-f]*' and length(${table.inputDigest}) = 64 and ${table.inputDigest} not glob '*[^0-9a-f]*' and length(${table.previewArtifactDigest}) = 64 and ${table.previewArtifactDigest} not glob '*[^0-9a-f]*' and length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and length(${table.previewDigest}) = 64 and ${table.previewDigest} not glob '*[^0-9a-f]*' and length(${table.redactionContractDigest}) = 64 and ${table.redactionContractDigest} not glob '*[^0-9a-f]*'`,
),
check('ql3_tool_start_artifact_time_check', sql`${table.boundAtMs} >= 0`),
index('ql3_tool_start_artifact_input_idx').on(
table.inputArtifactId,
table.startId,
),
index('ql3_tool_start_artifact_preview_idx').on(
table.previewArtifactId,
table.startId,
),
],
);
export const toolExecutionCompletions = sqliteTable(
'ToolExecutionCompletions',
{
startId: text('start_id').primaryKey(),
artifactId: text('artifact_id').notNull(),
projectId: text('project_id').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
startedStepRunVersion: integer('started_step_run_version').notNull(),
completedStepRunVersion: integer('completed_step_run_version').notNull(),
barrierDigest: text('barrier_digest').notNull(),
adapterDigest: text('adapter_digest').notNull(),
outputDigest: text('output_digest').notNull(),
executionResultDigest: text('execution_result_digest').notNull(),
artifactDigest: text('artifact_digest').notNull(),
keyId: text('key_id').notNull(),
algorithm: text('algorithm').notNull(),
plaintextBytes: integer('plaintext_bytes').notNull(),
stepRunMutationId: text('step_run_mutation_id').notNull(),
stepRunMutationDigest: text('step_run_mutation_digest').notNull(),
completedStepRunDigest: text('completed_step_run_digest').notNull(),
runEventId: text('run_event_id').notNull(),
completedAtMs: integer('completed_at_ms').notNull(),
completionDigest: text('completion_digest').notNull(),
artifactJson: text('artifact_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
completionJson: text('completion_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.startId],
foreignColumns: [toolExecutionStartBarriers.startId],
name: 'ql3_tool_completion_start_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_tool_completion_step_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.stepRunMutationId],
foreignColumns: [stepRunMutations.mutationId],
name: 'ql3_tool_completion_mutation_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.runEventId],
foreignColumns: [runEvents.id],
name: 'ql3_tool_completion_event_fk',
}).onDelete('restrict'),
check(
'ql3_tool_completion_identity_check',
sql`length(${table.startId}) between 1 and 128 and length(${table.artifactId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.keyId}) between 1 and 128 and length(${table.stepRunMutationId}) between 1 and 128 and length(${table.runEventId}) between 1 and 128`,
),
check(
'ql3_tool_completion_version_check',
sql`${table.startedStepRunVersion} between 2 and 2147483646 and ${table.completedStepRunVersion} = ${table.startedStepRunVersion} + 1`,
),
check(
'ql3_tool_completion_digest_check',
sql`length(${table.barrierDigest}) = 64 and ${table.barrierDigest} not glob '*[^0-9a-f]*' and length(${table.adapterDigest}) = 64 and ${table.adapterDigest} not glob '*[^0-9a-f]*' and length(${table.outputDigest}) = 64 and ${table.outputDigest} not glob '*[^0-9a-f]*' and length(${table.executionResultDigest}) = 64 and ${table.executionResultDigest} not glob '*[^0-9a-f]*' and length(${table.artifactDigest}) = 64 and ${table.artifactDigest} not glob '*[^0-9a-f]*' and length(${table.stepRunMutationDigest}) = 64 and ${table.stepRunMutationDigest} not glob '*[^0-9a-f]*' and length(${table.completedStepRunDigest}) = 64 and ${table.completedStepRunDigest} not glob '*[^0-9a-f]*' and length(${table.completionDigest}) = 64 and ${table.completionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_completion_budget_check',
sql`${table.algorithm} = 'aes-256-gcm' and ${table.plaintextBytes} between 0 and 262144 and ${table.completedAtMs} >= 0 and length(cast(${table.artifactJson} as blob)) between 2 and 393216 and length(cast(${table.completionJson} as blob)) between 2 and 24576`,
),
check(
'ql3_tool_completion_json_check',
sql`json_valid(${table.artifactJson}) and json_type(${table.artifactJson}) = 'object' and json_extract(${table.artifactJson}, '$.schema') = 'qinglong/tool-execution-result-artifact@v1' and json_extract(${table.artifactJson}, '$.artifactId') = ${table.artifactId} and json_extract(${table.artifactJson}, '$.projectId') = ${table.projectId} and json_extract(${table.artifactJson}, '$.startId') = ${table.startId} and json_extract(${table.artifactJson}, '$.runId') = ${table.runId} and json_extract(${table.artifactJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.artifactJson}, '$.barrierDigest') = ${table.barrierDigest} and json_extract(${table.artifactJson}, '$.adapterDigest') = ${table.adapterDigest} and json_extract(${table.artifactJson}, '$.outputDigest') = ${table.outputDigest} and json_extract(${table.artifactJson}, '$.executionResultDigest') = ${table.executionResultDigest} and json_extract(${table.artifactJson}, '$.artifactDigest') = ${table.artifactDigest} and json_extract(${table.artifactJson}, '$.keyId') = ${table.keyId} and json_extract(${table.artifactJson}, '$.algorithm') = ${table.algorithm} and json_extract(${table.artifactJson}, '$.plaintextBytes') = ${table.plaintextBytes} and json_extract(${table.artifactJson}, '$.sealedAtMs') = ${table.completedAtMs} and json_valid(${table.completionJson}) and json_type(${table.completionJson}) = 'object' and json_extract(${table.completionJson}, '$.schema') = 'qinglong/tool-execution-completion@v1' and json_extract(${table.completionJson}, '$.startId') = ${table.startId} and json_extract(${table.completionJson}, '$.projectId') = ${table.projectId} and json_extract(${table.completionJson}, '$.runId') = ${table.runId} and json_extract(${table.completionJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.completionJson}, '$.startedStepRunVersion') = ${table.startedStepRunVersion} and json_extract(${table.completionJson}, '$.completedStepRunVersion') = ${table.completedStepRunVersion} and json_extract(${table.completionJson}, '$.barrierDigest') = ${table.barrierDigest} and json_extract(${table.completionJson}, '$.adapterDigest') = ${table.adapterDigest} and json_extract(${table.completionJson}, '$.resultArtifact.artifactId') = ${table.artifactId} and json_extract(${table.completionJson}, '$.resultArtifact.artifactDigest') = ${table.artifactDigest} and json_extract(${table.completionJson}, '$.resultArtifact.outputDigest') = ${table.outputDigest} and json_extract(${table.completionJson}, '$.resultArtifact.executionResultDigest') = ${table.executionResultDigest} and json_extract(${table.completionJson}, '$.stepRunMutationId') = ${table.stepRunMutationId} and json_extract(${table.completionJson}, '$.stepRunMutationDigest') = ${table.stepRunMutationDigest} and json_extract(${table.completionJson}, '$.completedStepRunDigest') = ${table.completedStepRunDigest} and json_extract(${table.completionJson}, '$.runEventId') = ${table.runEventId} and json_extract(${table.completionJson}, '$.completedAtMs') = ${table.completedAtMs} and json_extract(${table.completionJson}, '$.completionDigest') = ${table.completionDigest}`,
),
uniqueIndex('ql3_tool_completion_artifact_uidx').on(table.artifactId),
uniqueIndex('ql3_tool_completion_mutation_uidx').on(
table.stepRunMutationId,
),
uniqueIndex('ql3_tool_completion_event_uidx').on(table.runEventId),
uniqueIndex('ql3_tool_completion_step_version_uidx').on(
table.runId,
table.stepRunId,
table.completedStepRunVersion,
),
index('ql3_tool_completion_project_time_idx').on(
table.projectId,
table.completedAtMs,
table.startId,
),
],
);
export const toolExecutionFailureCompletions = sqliteTable(
'ToolExecutionFailureCompletions',
{
startId: text('start_id').primaryKey(),
projectId: text('project_id').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
startedStepRunVersion: integer('started_step_run_version').notNull(),
completedStepRunVersion: integer('completed_step_run_version').notNull(),
barrierDigest: text('barrier_digest').notNull(),
adapterDigest: text('adapter_digest').notNull(),
outcome: text('outcome').notNull(),
resultCode: text('result_code').notNull(),
errorSummary: text('error_summary').notNull(),
stepRunMutationId: text('step_run_mutation_id').notNull(),
stepRunMutationDigest: text('step_run_mutation_digest').notNull(),
completedStepRunDigest: text('completed_step_run_digest').notNull(),
runEventId: text('run_event_id').notNull(),
completedAtMs: integer('completed_at_ms').notNull(),
completionDigest: text('completion_digest').notNull(),
completionJson: text('completion_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.startId],
foreignColumns: [toolExecutionStartBarriers.startId],
name: 'ql3_tool_failure_completion_start_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_tool_failure_completion_step_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.stepRunMutationId],
foreignColumns: [stepRunMutations.mutationId],
name: 'ql3_tool_failure_completion_mutation_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.runEventId],
foreignColumns: [runEvents.id],
name: 'ql3_tool_failure_completion_event_fk',
}).onDelete('restrict'),
check(
'ql3_tool_failure_completion_identity_check',
sql`length(${table.startId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.stepRunMutationId}) between 1 and 128 and length(${table.runEventId}) between 1 and 128`,
),
check(
'ql3_tool_failure_completion_version_check',
sql`${table.startedStepRunVersion} between 2 and 2147483646 and ${table.completedStepRunVersion} = ${table.startedStepRunVersion} + 1`,
),
check(
'ql3_tool_failure_completion_digest_check',
sql`length(${table.barrierDigest}) = 64 and ${table.barrierDigest} not glob '*[^0-9a-f]*' and length(${table.adapterDigest}) = 64 and ${table.adapterDigest} not glob '*[^0-9a-f]*' and length(${table.stepRunMutationDigest}) = 64 and ${table.stepRunMutationDigest} not glob '*[^0-9a-f]*' and length(${table.completedStepRunDigest}) = 64 and ${table.completedStepRunDigest} not glob '*[^0-9a-f]*' and length(${table.completionDigest}) = 64 and ${table.completionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_failure_completion_fact_check',
sql`(${table.outcome} = 'failed' and ${table.resultCode} = 'tool_adapter_failed' and ${table.errorSummary} = 'Trusted Tool execution failed') or (${table.outcome} = 'timed_out' and ${table.resultCode} = 'tool_deadline_exceeded' and ${table.errorSummary} = 'Trusted Tool execution deadline exceeded')`,
),
check(
'ql3_tool_failure_completion_budget_check',
sql`${table.completedAtMs} >= 0 and length(cast(${table.completionJson} as blob)) between 2 and 24576`,
),
check(
'ql3_tool_failure_completion_json_check',
sql`json_valid(${table.completionJson}) and json_type(${table.completionJson}) = 'object' and json_extract(${table.completionJson}, '$.schema') = 'qinglong/tool-execution-failure-completion@v1' and json_extract(${table.completionJson}, '$.startId') = ${table.startId} and json_extract(${table.completionJson}, '$.projectId') = ${table.projectId} and json_extract(${table.completionJson}, '$.runId') = ${table.runId} and json_extract(${table.completionJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.completionJson}, '$.startedStepRunVersion') = ${table.startedStepRunVersion} and json_extract(${table.completionJson}, '$.completedStepRunVersion') = ${table.completedStepRunVersion} and json_extract(${table.completionJson}, '$.barrierDigest') = ${table.barrierDigest} and json_extract(${table.completionJson}, '$.adapterDigest') = ${table.adapterDigest} and json_extract(${table.completionJson}, '$.outcome') = ${table.outcome} and json_extract(${table.completionJson}, '$.resultCode') = ${table.resultCode} and json_extract(${table.completionJson}, '$.errorSummary') = ${table.errorSummary} and json_extract(${table.completionJson}, '$.stepRunMutationId') = ${table.stepRunMutationId} and json_extract(${table.completionJson}, '$.stepRunMutationDigest') = ${table.stepRunMutationDigest} and json_extract(${table.completionJson}, '$.completedStepRunDigest') = ${table.completedStepRunDigest} and json_extract(${table.completionJson}, '$.runEventId') = ${table.runEventId} and json_extract(${table.completionJson}, '$.completedAtMs') = ${table.completedAtMs} and json_extract(${table.completionJson}, '$.completionDigest') = ${table.completionDigest}`,
),
uniqueIndex('ql3_tool_failure_completion_mutation_uidx').on(
table.stepRunMutationId,
),
uniqueIndex('ql3_tool_failure_completion_event_uidx').on(table.runEventId),
uniqueIndex('ql3_tool_failure_completion_step_version_uidx').on(
table.runId,
table.stepRunId,
table.completedStepRunVersion,
),
index('ql3_tool_failure_completion_project_time_idx').on(
table.projectId,
table.completedAtMs,
table.startId,
),
],
);
export const toolResultKeyCatalogGenerations = sqliteTable(
'ToolResultKeyCatalogGenerations',
{
authority: text('authority').notNull(),
generation: integer('generation').notNull(),
previousGeneration: integer('previous_generation'),
previousCatalogDigest: text('previous_catalog_digest'),
activeKeyId: text('active_key_id'),
mutationKind: text('mutation_kind').notNull(),
mutationId: text('mutation_id').notNull(),
catalogDigest: text('catalog_digest').notNull(),
commandDigest: text('command_digest').notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
catalogJson: text('catalog_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
primaryKey({ columns: [table.authority, table.generation] }),
foreignKey({
columns: [
table.authority,
table.previousGeneration,
table.previousCatalogDigest,
],
foreignColumns: [table.authority, table.generation, table.catalogDigest],
name: 'ql3_tool_result_key_catalog_previous_fk',
}).onDelete('restrict'),
check(
'ql3_tool_result_key_catalog_authority_check',
sql`${table.authority} = 'trusted-tool-results'`,
),
check(
'ql3_tool_result_key_catalog_generation_check',
sql`${table.generation} between 1 and 2147483647 and ((${table.generation} = 1 and ${table.previousGeneration} is null and ${table.previousCatalogDigest} is null and ${table.mutationKind} = 'bootstrap') or (${table.generation} > 1 and ${table.previousGeneration} = ${table.generation} - 1 and ${table.previousCatalogDigest} is not null and ${table.mutationKind} in ('rotate', 'retire', 'mark_lost', 'restore')))`,
),
check(
'ql3_tool_result_key_catalog_identity_check',
sql`${table.activeKeyId} is null or length(${table.activeKeyId}) between 1 and 128 and ${table.activeKeyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.activeKeyId}, 1, 1) glob '[A-Za-z0-9]'`,
),
check(
'ql3_tool_result_key_catalog_digest_check',
sql`(${table.previousCatalogDigest} is null or length(${table.previousCatalogDigest}) = 64 and ${table.previousCatalogDigest} not glob '*[^0-9a-f]*') and length(${table.catalogDigest}) = 64 and ${table.catalogDigest} not glob '*[^0-9a-f]*' and length(${table.commandDigest}) = 64 and ${table.commandDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_result_key_catalog_budget_check',
sql`${table.committedAtMs} >= 0 and length(${table.mutationId}) between 1 and 128 and length(cast(${table.catalogJson} as blob)) between 2 and 65536`,
),
check(
'ql3_tool_result_key_catalog_json_check',
sql`json_valid(${table.catalogJson}) and json_type(${table.catalogJson}) = 'object' and json_extract(${table.catalogJson}, '$.schema') = 'qinglong/tool-result-key-catalog@v1' and json_extract(${table.catalogJson}, '$.generation') = ${table.generation} and ((${table.previousCatalogDigest} is null and json_type(${table.catalogJson}, '$.previousCatalogDigest') = 'null') or json_extract(${table.catalogJson}, '$.previousCatalogDigest') = ${table.previousCatalogDigest}) and ((${table.activeKeyId} is null and json_type(${table.catalogJson}, '$.activeKeyId') = 'null') or json_extract(${table.catalogJson}, '$.activeKeyId') = ${table.activeKeyId}) and json_extract(${table.catalogJson}, '$.mutationKind') = ${table.mutationKind} and json_extract(${table.catalogJson}, '$.mutationId') = ${table.mutationId} and json_extract(${table.catalogJson}, '$.catalogDigest') = ${table.catalogDigest} and json_extract(${table.catalogJson}, '$.committedAtMs') = ${table.committedAtMs} and json_type(${table.catalogJson}, '$.keys') = 'array' and json_array_length(json_extract(${table.catalogJson}, '$.keys')) between 1 and 64`,
),
index('ql3_tool_result_key_catalog_current_idx').on(
table.authority,
sql`${table.generation} desc`,
),
],
);
export const toolExecutionResultKeyBindings = sqliteTable(
'ToolExecutionResultKeyBindings',
{
startId: text('start_id').primaryKey(),
artifactId: text('artifact_id').notNull(),
artifactDigest: text('artifact_digest').notNull(),
catalogAuthority: text('catalog_authority').notNull(),
catalogGeneration: integer('catalog_generation').notNull(),
catalogDigest: text('catalog_digest').notNull(),
keyId: text('key_id').notNull(),
materialProof: text('material_proof').notNull(),
bindingDigest: text('binding_digest').notNull(),
},
(table) => [
foreignKey({
columns: [table.startId],
foreignColumns: [toolExecutionCompletions.startId],
name: 'ql3_tool_result_key_binding_completion_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.artifactId],
foreignColumns: [toolExecutionCompletions.artifactId],
name: 'ql3_tool_result_key_binding_artifact_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.catalogAuthority,
table.catalogGeneration,
table.catalogDigest,
],
foreignColumns: [
toolResultKeyCatalogGenerations.authority,
toolResultKeyCatalogGenerations.generation,
toolResultKeyCatalogGenerations.catalogDigest,
],
name: 'ql3_tool_result_key_binding_catalog_fk',
}).onDelete('restrict'),
check(
'ql3_tool_result_key_binding_authority_check',
sql`${table.catalogAuthority} = 'trusted-tool-results'`,
),
check(
'ql3_tool_result_key_binding_identity_check',
sql`length(${table.startId}) between 1 and 128 and length(${table.artifactId}) between 1 and 128 and length(${table.keyId}) between 1 and 128 and ${table.keyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.keyId}, 1, 1) glob '[A-Za-z0-9]' and ${table.catalogGeneration} between 1 and 2147483647`,
),
check(
'ql3_tool_result_key_binding_digest_check',
sql`length(${table.artifactDigest}) = 64 and ${table.artifactDigest} not glob '*[^0-9a-f]*' and length(${table.catalogDigest}) = 64 and ${table.catalogDigest} not glob '*[^0-9a-f]*' and length(${table.materialProof}) = 64 and ${table.materialProof} not glob '*[^0-9a-f]*' and length(${table.bindingDigest}) = 64 and ${table.bindingDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_tool_result_key_binding_catalog_idx').on(
table.catalogGeneration,
table.keyId,
table.startId,
),
],
);
export const toolExecutionResultRekeyOverlays = sqliteTable(
'ToolExecutionResultRekeyOverlays',
{
overlayId: text('overlay_id').primaryKey(),
artifactId: text('artifact_id').notNull(),
sourceBindingDigest: text('source_binding_digest').notNull(),
revision: integer('revision').notNull(),
previousOverlayDigest: text('previous_overlay_digest'),
fromKeyId: text('from_key_id').notNull(),
targetCatalogAuthority: text('target_catalog_authority').notNull(),
targetCatalogGeneration: integer('target_catalog_generation').notNull(),
targetCatalogDigest: text('target_catalog_digest').notNull(),
targetKeyId: text('target_key_id').notNull(),
targetMaterialProof: text('target_material_proof').notNull(),
mutationId: text('mutation_id').notNull().unique(),
commandDigest: text('command_digest').notNull(),
overlayDigest: text('overlay_digest').notNull().unique(),
rekeyedAtMs: integer('rekeyed_at_ms').notNull(),
overlayJson: text('overlay_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.artifactId],
foreignColumns: [toolExecutionResultKeyBindings.artifactId],
name: 'ql3_tool_result_rekey_artifact_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.sourceBindingDigest],
foreignColumns: [toolExecutionResultKeyBindings.bindingDigest],
name: 'ql3_tool_result_rekey_binding_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.previousOverlayDigest],
foreignColumns: [table.overlayDigest],
name: 'ql3_tool_result_rekey_previous_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.targetCatalogAuthority,
table.targetCatalogGeneration,
table.targetCatalogDigest,
],
foreignColumns: [
toolResultKeyCatalogGenerations.authority,
toolResultKeyCatalogGenerations.generation,
toolResultKeyCatalogGenerations.catalogDigest,
],
name: 'ql3_tool_result_rekey_catalog_fk',
}).onDelete('restrict'),
check(
'ql3_tool_result_rekey_revision_check',
sql`${table.revision} between 1 and 2147483647 and ((${table.revision} = 1 and ${table.previousOverlayDigest} is null) or (${table.revision} > 1 and ${table.previousOverlayDigest} is not null))`,
),
check(
'ql3_tool_result_rekey_authority_check',
sql`${table.targetCatalogAuthority} = 'trusted-tool-results'`,
),
check(
'ql3_tool_result_rekey_identity_check',
sql`length(${table.overlayId}) between 1 and 128 and length(${table.artifactId}) between 1 and 128 and length(${table.mutationId}) between 1 and 128 and length(${table.fromKeyId}) between 1 and 128 and ${table.fromKeyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.fromKeyId}, 1, 1) glob '[A-Za-z0-9]' and length(${table.targetKeyId}) between 1 and 128 and ${table.targetKeyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.targetKeyId}, 1, 1) glob '[A-Za-z0-9]' and ${table.fromKeyId} <> ${table.targetKeyId} and ${table.targetCatalogGeneration} between 1 and 2147483647`,
),
check(
'ql3_tool_result_rekey_digest_check',
sql`(${table.previousOverlayDigest} is null or length(${table.previousOverlayDigest}) = 64 and ${table.previousOverlayDigest} not glob '*[^0-9a-f]*') and length(${table.sourceBindingDigest}) = 64 and ${table.sourceBindingDigest} not glob '*[^0-9a-f]*' and length(${table.targetCatalogDigest}) = 64 and ${table.targetCatalogDigest} not glob '*[^0-9a-f]*' and length(${table.targetMaterialProof}) = 64 and ${table.targetMaterialProof} not glob '*[^0-9a-f]*' and length(${table.commandDigest}) = 64 and ${table.commandDigest} not glob '*[^0-9a-f]*' and length(${table.overlayDigest}) = 64 and ${table.overlayDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_result_rekey_budget_check',
sql`${table.rekeyedAtMs} >= 0 and length(cast(${table.overlayJson} as blob)) between 2 and 393216`,
),
check(
'ql3_tool_result_rekey_json_check',
sql`json_valid(${table.overlayJson}) and json_type(${table.overlayJson}) = 'object' and json_extract(${table.overlayJson}, '$.schema') = 'qinglong/tool-execution-result-rekey-overlay@v1' and json_extract(${table.overlayJson}, '$.overlayId') = ${table.overlayId} and json_extract(${table.overlayJson}, '$.sourceArtifact.artifactId') = ${table.artifactId} and json_extract(${table.overlayJson}, '$.sourceBindingDigest') = ${table.sourceBindingDigest} and json_extract(${table.overlayJson}, '$.revision') = ${table.revision} and ((${table.previousOverlayDigest} is null and json_type(${table.overlayJson}, '$.previousOverlayDigest') = 'null') or json_extract(${table.overlayJson}, '$.previousOverlayDigest') = ${table.previousOverlayDigest}) and json_extract(${table.overlayJson}, '$.fromKeyId') = ${table.fromKeyId} and json_extract(${table.overlayJson}, '$.targetCatalogFence.generation') = ${table.targetCatalogGeneration} and json_extract(${table.overlayJson}, '$.targetCatalogFence.catalogDigest') = ${table.targetCatalogDigest} and json_extract(${table.overlayJson}, '$.targetCatalogFence.keyId') = ${table.targetKeyId} and json_extract(${table.overlayJson}, '$.targetCatalogFence.materialProof') = ${table.targetMaterialProof} and json_extract(${table.overlayJson}, '$.rekeyedAtMs') = ${table.rekeyedAtMs} and json_extract(${table.overlayJson}, '$.overlayDigest') = ${table.overlayDigest}`,
),
index('ql3_tool_result_rekey_artifact_idx').on(
table.artifactId,
sql`${table.revision} desc`,
),
index('ql3_tool_result_rekey_target_idx').on(
table.targetKeyId,
table.artifactId,
sql`${table.revision} desc`,
),
],
);
export const toolExecutionResultRekeyHeads = sqliteTable(
'ToolExecutionResultRekeyHeads',
{
artifactId: text('artifact_id').primaryKey(),
revision: integer('revision').notNull(),
overlayId: text('overlay_id').notNull().unique(),
overlayDigest: text('overlay_digest').notNull().unique(),
targetCatalogGeneration: integer('target_catalog_generation').notNull(),
targetCatalogDigest: text('target_catalog_digest').notNull(),
targetKeyId: text('target_key_id').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
foreignKey({
columns: [table.artifactId, table.revision, table.overlayDigest],
foreignColumns: [
toolExecutionResultRekeyOverlays.artifactId,
toolExecutionResultRekeyOverlays.revision,
toolExecutionResultRekeyOverlays.overlayDigest,
],
name: 'ql3_tool_result_rekey_head_overlay_fk',
}).onDelete('restrict'),
check(
'ql3_tool_result_rekey_head_identity_check',
sql`${table.revision} between 1 and 2147483647 and length(${table.artifactId}) between 1 and 128 and length(${table.overlayId}) between 1 and 128 and length(${table.targetKeyId}) between 1 and 128 and ${table.targetKeyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.targetKeyId}, 1, 1) glob '[A-Za-z0-9]' and ${table.targetCatalogGeneration} between 1 and 2147483647 and ${table.updatedAtMs} >= 0`,
),
check(
'ql3_tool_result_rekey_head_digest_check',
sql`length(${table.overlayDigest}) = 64 and ${table.overlayDigest} not glob '*[^0-9a-f]*' and length(${table.targetCatalogDigest}) = 64 and ${table.targetCatalogDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_tool_result_rekey_head_target_idx').on(
table.targetKeyId,
table.artifactId,
),
],
);
export const toolResultKeyRetirementReceipts = sqliteTable(
'ToolResultKeyRetirementReceipts',
{
receiptDigest: text('receipt_digest').primaryKey(),
catalogAuthority: text('catalog_authority').notNull(),
catalogGeneration: integer('catalog_generation').notNull(),
catalogDigest: text('catalog_digest').notNull(),
keyId: text('key_id').notNull(),
materialProof: text('material_proof').notNull(),
mutationId: text('mutation_id').notNull().unique(),
commandDigest: text('command_digest').notNull(),
bindingCount: integer('binding_count').notNull(),
overlayHeadCount: integer('overlay_head_count').notNull(),
uncoveredBindingCount: integer('uncovered_binding_count').notNull(),
uncoveredOverlayHeadCount: integer(
'uncovered_overlay_head_count',
).notNull(),
coverageDigest: text('coverage_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [
table.catalogAuthority,
table.catalogGeneration,
table.catalogDigest,
],
foreignColumns: [
toolResultKeyCatalogGenerations.authority,
toolResultKeyCatalogGenerations.generation,
toolResultKeyCatalogGenerations.catalogDigest,
],
name: 'ql3_tool_result_key_retirement_catalog_fk',
}).onDelete('restrict'),
check(
'ql3_tool_result_key_retirement_authority_check',
sql`${table.catalogAuthority} = 'trusted-tool-results'`,
),
check(
'ql3_tool_result_key_retirement_identity_check',
sql`${table.catalogGeneration} between 1 and 2147483647 and length(${table.keyId}) between 1 and 128 and ${table.keyId} not glob '*[^A-Za-z0-9._-]*' and substr(${table.keyId}, 1, 1) glob '[A-Za-z0-9]' and length(${table.mutationId}) between 1 and 128`,
),
check(
'ql3_tool_result_key_retirement_count_check',
sql`${table.bindingCount} between 0 and 2147483647 and ${table.overlayHeadCount} between 0 and 2147483647 and ${table.uncoveredBindingCount} = 0 and ${table.uncoveredOverlayHeadCount} = 0 and ${table.createdAtMs} >= 0`,
),
check(
'ql3_tool_result_key_retirement_digest_check',
sql`length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*' and length(${table.catalogDigest}) = 64 and ${table.catalogDigest} not glob '*[^0-9a-f]*' and length(${table.materialProof}) = 64 and ${table.materialProof} not glob '*[^0-9a-f]*' and length(${table.commandDigest}) = 64 and ${table.commandDigest} not glob '*[^0-9a-f]*' and length(${table.coverageDigest}) = 64 and ${table.coverageDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_tool_result_key_retirement_json_check',
sql`json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/tool-result-key-retirement-receipt@v1' and json_extract(${table.receiptJson}, '$.catalogGeneration') = ${table.catalogGeneration} and json_extract(${table.receiptJson}, '$.catalogDigest') = ${table.catalogDigest} and json_extract(${table.receiptJson}, '$.keyId') = ${table.keyId} and json_extract(${table.receiptJson}, '$.materialProof') = ${table.materialProof} and json_extract(${table.receiptJson}, '$.mutationId') = ${table.mutationId} and json_extract(${table.receiptJson}, '$.bindingCount') = ${table.bindingCount} and json_extract(${table.receiptJson}, '$.overlayHeadCount') = ${table.overlayHeadCount} and json_extract(${table.receiptJson}, '$.uncoveredBindingCount') = 0 and json_extract(${table.receiptJson}, '$.uncoveredOverlayHeadCount') = 0 and json_extract(${table.receiptJson}, '$.coverageDigest') = ${table.coverageDigest} and json_extract(${table.receiptJson}, '$.createdAtMs') = ${table.createdAtMs} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
index('ql3_tool_result_key_retirement_catalog_idx').on(
table.catalogGeneration,
table.keyId,
),
],
);
export const pluginPackageAdmissionReceipts = sqliteTable(
'QingLong3PluginPackageAdmissionReceipts',
{
dispatchId: text('dispatch_id')
.primaryKey()
.references(() => approvedActionDispatches.dispatchId, {
onDelete: 'restrict',
}),
dispatchDigest: text('dispatch_digest').notNull(),
approvalRequestId: text('approval_request_id')
.notNull()
.references(() => approvalRequests.requestId, { onDelete: 'restrict' }),
actionRef: text('action_ref').notNull(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
packageName: text('package_name').notNull(),
installationId: text('installation_id')
.notNull()
.references(() => pluginPackageInstalls.installationId, {
onDelete: 'restrict',
}),
lockDigest: text('lock_digest').notNull(),
recordDigest: text('record_digest').notNull(),
mutationId: text('mutation_id').notNull(),
mutationDigest: text('mutation_digest').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
admittedAtMs: integer('admitted_at_ms').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
receiptDigest: text('receipt_digest').notNull(),
},
(table) => [
check(
'ql3_plugin_package_admission_identity_check',
sql`length(${table.dispatchId}) between 1 and 128 and length(${table.approvalRequestId}) between 1 and 128 and length(${table.actionRef}) between 1 and 255 and length(${table.projectId}) between 1 and 128 and length(${table.packageName}) between 1 and 64 and length(${table.installationId}) between 1 and 128 and length(${table.mutationId}) between 1 and 128 and length(${table.auditEventId}) = 36`,
),
check(
'ql3_plugin_package_admission_digest_check',
sql`length(${table.dispatchDigest}) = 64 and ${table.dispatchDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.recordDigest}) = 64 and ${table.recordDigest} not glob '*[^0-9a-f]*' and length(${table.mutationDigest}) = 64 and ${table.mutationDigest} not glob '*[^0-9a-f]*' and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_admission_json_check',
sql`length(${table.receiptJson}) between 2 and 65536 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-admission-receipt@v1' and json_extract(${table.receiptJson}, '$.dispatchId') = ${table.dispatchId} and json_extract(${table.receiptJson}, '$.dispatchDigest') = ${table.dispatchDigest} and json_extract(${table.receiptJson}, '$.approvalRequestId') = ${table.approvalRequestId} and json_extract(${table.receiptJson}, '$.actionRef') = ${table.actionRef} and json_extract(${table.receiptJson}, '$.projectId') = ${table.projectId} and json_extract(${table.receiptJson}, '$.packageName') = ${table.packageName} and json_extract(${table.receiptJson}, '$.installationId') = ${table.installationId} and json_extract(${table.receiptJson}, '$.lockDigest') = ${table.lockDigest} and json_extract(${table.receiptJson}, '$.recordDigest') = ${table.recordDigest} and json_extract(${table.receiptJson}, '$.mutationId') = ${table.mutationId} and json_extract(${table.receiptJson}, '$.mutationDigest') = ${table.mutationDigest} and json_extract(${table.receiptJson}, '$.auditEventId') = ${table.auditEventId} and json_extract(${table.receiptJson}, '$.admittedAtMs') = ${table.admittedAtMs} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
check(
'ql3_plugin_package_admission_time_check',
sql`${table.admittedAtMs} >= 0`,
),
uniqueIndex('ql3_plugin_package_admission_install_uidx').on(
table.installationId,
),
uniqueIndex('ql3_plugin_package_admission_audit_uidx').on(
table.auditEventId,
),
index('ql3_plugin_package_admission_project_idx').on(
table.projectId,
table.admittedAtMs,
table.dispatchId,
),
],
);
export const legacyAdoptions = sqliteTable(
'QingLong3LegacyAdoptions',
{
mutationId: text('mutation_id').primaryKey(),
decisionId: text('decision_id').notNull(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
profile: text('profile').notNull(),
planDigest: text('plan_digest').notNull(),
inventoryDigest: text('inventory_digest').notNull(),
decisionDigest: text('decision_digest').notNull(),
receiptDigest: text('receipt_digest').notNull(),
authorizationFileDigest: text('authorization_file_digest').notNull(),
publicationDigest: text('publication_digest').notNull(),
rowCount: integer('row_count').notNull(),
adoptedTaskCount: integer('adopted_task_count').notNull(),
adoptedTriggerCount: integer('adopted_trigger_count').notNull(),
skippedCount: integer('skipped_count').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_legacy_adoptions_mutation_check',
sql`length(${table.mutationId}) = 36 and ${table.auditEventId} = ${table.mutationId}`,
),
check(
'ql3_legacy_adoptions_decision_check',
sql`length(${table.decisionId}) = 36 and substr(${table.decisionId}, 15, 1) = '7' and replace(${table.decisionId}, '-', '') not glob '*[^0-9a-f]*'`,
),
check(
'ql3_legacy_adoptions_profile_check',
sql`${table.profile} in ('edge', 'standalone')`,
),
check(
'ql3_legacy_adoptions_digest_check',
sql`length(${table.planDigest}) = 64 and ${table.planDigest} not glob '*[^0-9a-f]*' and length(${table.inventoryDigest}) = 64 and ${table.inventoryDigest} not glob '*[^0-9a-f]*' and length(${table.decisionDigest}) = 64 and ${table.decisionDigest} not glob '*[^0-9a-f]*' and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*' and length(${table.authorizationFileDigest}) = 64 and ${table.authorizationFileDigest} not glob '*[^0-9a-f]*' and length(${table.publicationDigest}) = 64 and ${table.publicationDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_legacy_adoptions_count_check',
sql`${table.rowCount} between 0 and 100000 and ${table.adoptedTaskCount} between 0 and ${table.rowCount} and ${table.skippedCount} between 0 and ${table.rowCount} and ${table.adoptedTaskCount} + ${table.skippedCount} = ${table.rowCount} and ${table.adoptedTriggerCount} between 0 and 500000`,
),
check('ql3_legacy_adoptions_created_check', sql`${table.createdAtMs} >= 0`),
uniqueIndex('ql3_legacy_adoptions_decision_uidx').on(table.decisionId),
index('ql3_legacy_adoptions_project_time_idx').on(
table.projectId,
sql`${table.createdAtMs} desc`,
sql`${table.mutationId} desc`,
),
],
);
export const localIdentitySubjects = sqliteTable(
'QingLong3IdentitySubjects',
{
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
status: text('status').notNull(),
version: integer('version').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.subjectType, table.subjectId] }),
check(
'ql3_local_identity_type_check',
sql`${table.subjectType} in ('user','api_app','mcp_client','agent','system','worker')`,
),
check(
'ql3_local_identity_id_check',
sql`length(${table.subjectId}) between 1 and 255`,
),
check(
'ql3_local_identity_status_check',
sql`${table.status} in ('active','disabled')`,
),
check(
'ql3_local_identity_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_identity_time_check',
sql`${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
),
index('ql3_local_identity_status_idx').on(
table.status,
table.subjectType,
table.subjectId,
),
],
);
export const localApiCredentials = sqliteTable(
'QingLong3ApiCredentials',
{
credentialId: text('credential_id').notNull(),
version: integer('version').notNull(),
state: text('state').notNull(),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
secretDigest: text('secret_digest').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
notBeforeAtMs: integer('not_before_at_ms').notNull(),
expiresAtMs: integer('expires_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.credentialId, table.version] }),
check(
'ql3_local_credentials_id_check',
sql`length(${table.credentialId}) between 1 and 64 and ${table.credentialId} not glob '*[^A-Za-z0-9._:-]*'`,
),
check(
'ql3_local_credentials_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_credentials_state_check',
sql`${table.state} in ('active','revoked')`,
),
check(
'ql3_local_credentials_subject_type_check',
sql`${table.subjectType} in ('user','api_app','mcp_client','agent')`,
),
check(
'ql3_local_credentials_subject_id_check',
sql`length(${table.subjectId}) between 1 and 255`,
),
check(
'ql3_local_credentials_digest_check',
sql`length(${table.secretDigest}) = 64 and ${table.secretDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_credentials_lifetime_check',
sql`${table.createdAtMs} >= 0 and ${table.notBeforeAtMs} >= ${table.createdAtMs} and ${table.expiresAtMs} > ${table.notBeforeAtMs}`,
),
foreignKey({
columns: [table.subjectType, table.subjectId],
foreignColumns: [
localIdentitySubjects.subjectType,
localIdentitySubjects.subjectId,
],
name: 'ql3_local_credentials_subject_fk',
}).onDelete('restrict'),
index('ql3_local_credentials_current_idx').on(
table.credentialId,
sql`${table.version} desc`,
),
index('ql3_local_credentials_subject_idx').on(
table.subjectType,
table.subjectId,
table.credentialId,
sql`${table.version} desc`,
),
],
);
export const localApiCredentialPepperBindings = sqliteTable(
'QingLong3ApiCredentialPepperBindings',
{
credentialId: text('credential_id').notNull(),
credentialVersion: integer('credential_version').notNull(),
pepperKeyId: text('pepper_key_id')
.notNull()
.references(() => localOwnerPepperKeys.pepperKeyId, {
onDelete: 'restrict',
}),
},
(table) => [
primaryKey({
columns: [table.credentialId, table.credentialVersion],
}),
check(
'ql3_local_credential_pepper_key_id_check',
sql`length(${table.pepperKeyId}) between 1 and 64 and ${table.pepperKeyId} not glob '*[^A-Za-z0-9._:-]*'`,
),
foreignKey({
columns: [table.credentialId, table.credentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_credential_pepper_binding_credential_fk',
}).onDelete('restrict'),
index('ql3_local_credential_pepper_key_idx').on(
table.pepperKeyId,
table.credentialId,
table.credentialVersion,
),
uniqueIndex('ql3_local_credential_pepper_binding_triple_uidx').on(
table.credentialId,
table.credentialVersion,
table.pepperKeyId,
),
],
);
export const localOwnerPepperKeys = sqliteTable(
'QingLong3LocalOwnerPepperKeys',
{
pepperKeyId: text('pepper_key_id').primaryKey(),
materialDigest: text('material_digest'),
backupDigest: text('backup_digest'),
state: text('state').notNull(),
version: integer('version').notNull(),
registerMutationId: text('register_mutation_id'),
activateMutationId: text('activate_mutation_id'),
retireMutationId: text('retire_mutation_id'),
registeredAtMs: integer('registered_at_ms').notNull(),
activatedAtMs: integer('activated_at_ms'),
retiredAtMs: integer('retired_at_ms'),
},
(table) => [
check(
'ql3_local_owner_pepper_key_id_check',
sql`length(${table.pepperKeyId}) between 1 and 64 and ${table.pepperKeyId} not glob '*[^A-Za-z0-9._:-]*'`,
),
check(
'ql3_local_owner_pepper_digest_check',
sql`(${table.materialDigest} is null and ${table.backupDigest} is null) or (length(${table.materialDigest}) = 64 and ${table.materialDigest} not glob '*[^0-9a-f]*' and length(${table.backupDigest}) = 64 and ${table.backupDigest} not glob '*[^0-9a-f]*')`,
),
check(
'ql3_local_owner_pepper_state_check',
sql`${table.state} in ('recovery_required','staged','active','retired')`,
),
check(
'ql3_local_owner_pepper_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_owner_pepper_mutation_check',
sql`(${table.registerMutationId} is null or length(${table.registerMutationId}) = 36) and (${table.activateMutationId} is null or length(${table.activateMutationId}) = 36) and (${table.retireMutationId} is null or length(${table.retireMutationId}) = 36)`,
),
check(
'ql3_local_owner_pepper_time_check',
sql`${table.registeredAtMs} >= 0 and (${table.activatedAtMs} is null or ${table.activatedAtMs} >= ${table.registeredAtMs}) and (${table.retiredAtMs} is null or ${table.retiredAtMs} >= ${table.activatedAtMs})`,
),
check(
'ql3_local_owner_pepper_shape_check',
sql`(${table.state} = 'recovery_required' and ${table.materialDigest} is null and ${table.backupDigest} is null and ${table.registerMutationId} is null and ${table.activateMutationId} is null and ${table.retireMutationId} is null and ${table.activatedAtMs} is null and ${table.retiredAtMs} is null) or (${table.state} = 'staged' and ${table.materialDigest} is not null and ${table.backupDigest} is not null and ${table.registerMutationId} is not null and ${table.activateMutationId} is null and ${table.retireMutationId} is null and ${table.activatedAtMs} is null and ${table.retiredAtMs} is null) or (${table.state} = 'active' and ${table.materialDigest} is not null and ${table.backupDigest} is not null and ${table.registerMutationId} is not null and ${table.activateMutationId} is not null and ${table.retireMutationId} is null and ${table.activatedAtMs} is not null and ${table.retiredAtMs} is null) or (${table.state} = 'retired' and ${table.materialDigest} is not null and ${table.backupDigest} is not null and ${table.registerMutationId} is not null and ${table.activateMutationId} is not null and ${table.retireMutationId} is not null and ${table.activatedAtMs} is not null and ${table.retiredAtMs} is not null)`,
),
uniqueIndex('ql3_local_owner_pepper_register_mutation_uidx')
.on(table.registerMutationId)
.where(sql`${table.registerMutationId} is not null`),
uniqueIndex('ql3_local_owner_pepper_single_active_uidx')
.on(table.state)
.where(sql`${table.state} = 'active'`),
index('ql3_local_owner_pepper_state_idx').on(
table.state,
table.pepperKeyId,
),
],
);
export const localOwnerPepperActivations = sqliteTable(
'QingLong3LocalOwnerPepperActivations',
{
generation: integer('generation').primaryKey(),
mutationId: text('mutation_id').notNull(),
expectedGeneration: integer('expected_generation').notNull(),
previousPepperKeyId: text('previous_pepper_key_id').references(
() => localOwnerPepperKeys.pepperKeyId,
{ onDelete: 'restrict' },
),
activePepperKeyId: text('active_pepper_key_id')
.notNull()
.references(() => localOwnerPepperKeys.pepperKeyId, {
onDelete: 'restrict',
}),
materialDigest: text('material_digest').notNull(),
backupDigest: text('backup_digest').notNull(),
activatedAtMs: integer('activated_at_ms').notNull(),
},
(table) => [
check(
'ql3_local_owner_pepper_activation_generation_check',
sql`${table.generation} between 1 and 2147483647 and ${table.expectedGeneration} = ${table.generation} - 1`,
),
check(
'ql3_local_owner_pepper_activation_mutation_check',
sql`length(${table.mutationId}) = 36`,
),
check(
'ql3_local_owner_pepper_activation_key_check',
sql`length(${table.activePepperKeyId}) between 1 and 64 and ${table.activePepperKeyId} not glob '*[^A-Za-z0-9._:-]*' and (${table.previousPepperKeyId} is null or (length(${table.previousPepperKeyId}) between 1 and 64 and ${table.previousPepperKeyId} not glob '*[^A-Za-z0-9._:-]*' and ${table.previousPepperKeyId} <> ${table.activePepperKeyId}))`,
),
check(
'ql3_local_owner_pepper_activation_digest_check',
sql`length(${table.materialDigest}) = 64 and ${table.materialDigest} not glob '*[^0-9a-f]*' and length(${table.backupDigest}) = 64 and ${table.backupDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_owner_pepper_activation_time_check',
sql`${table.activatedAtMs} >= 0`,
),
uniqueIndex('ql3_local_owner_pepper_activation_mutation_uidx').on(
table.mutationId,
),
index('ql3_local_owner_pepper_activation_key_idx').on(
table.activePepperKeyId,
sql`${table.generation} desc`,
),
],
);
export const localIdentityProvisionings = sqliteTable(
'QingLong3LocalIdentityProvisionings',
{
slot: integer('slot').primaryKey(),
mutationId: text('mutation_id').notNull(),
requestId: text('request_id').notNull(),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
credentialId: text('credential_id').notNull(),
credentialVersion: integer('credential_version').notNull(),
issuerAuthenticationId: text('issuer_authentication_id').notNull(),
issuerAuthenticatedAtMs: integer('issuer_authenticated_at_ms').notNull(),
issuerExpiresAtMs: integer('issuer_expires_at_ms').notNull(),
auditEventId: text('audit_event_id').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check('ql3_local_provisioning_singleton_check', sql`${table.slot} = 1`),
check(
'ql3_local_provisioning_mutation_check',
sql`length(${table.mutationId}) = 36`,
),
check(
'ql3_local_provisioning_request_check',
sql`length(${table.requestId}) between 1 and 128`,
),
check(
'ql3_local_provisioning_subject_check',
sql`${table.subjectType} = 'user' and length(${table.subjectId}) between 1 and 255`,
),
check(
'ql3_local_provisioning_credential_check',
sql`length(${table.credentialId}) between 1 and 64 and ${table.credentialVersion} = 1`,
),
check(
'ql3_local_provisioning_issuer_check',
sql`length(${table.issuerAuthenticationId}) between 1 and 128 and ${table.issuerAuthenticatedAtMs} <= ${table.createdAtMs} and ${table.issuerExpiresAtMs} > ${table.createdAtMs}`,
),
check(
'ql3_local_provisioning_audit_check',
sql`${table.auditEventId} = ${table.mutationId}`,
),
check('ql3_local_provisioning_time_check', sql`${table.createdAtMs} >= 0`),
foreignKey({
columns: [table.subjectType, table.subjectId],
foreignColumns: [
localIdentitySubjects.subjectType,
localIdentitySubjects.subjectId,
],
name: 'ql3_local_provisioning_subject_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.credentialId, table.credentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_provisioning_credential_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.auditEventId],
foreignColumns: [localSecurityAuditEvents.eventId],
name: 'ql3_local_provisioning_audit_fk',
}).onDelete('restrict'),
uniqueIndex('ql3_local_provisioning_mutation_uidx').on(table.mutationId),
uniqueIndex('ql3_local_provisioning_subject_uidx').on(
table.subjectType,
table.subjectId,
),
uniqueIndex('ql3_local_provisioning_credential_uidx').on(
table.credentialId,
table.credentialVersion,
),
],
);
export const localOwnerBootstrapChallenges = sqliteTable(
'QingLong3LocalOwnerBootstrapChallenges',
{
projectId: text('project_id').notNull(),
version: integer('version').notNull(),
issueMutationId: text('issue_mutation_id').notNull(),
issueRequestId: text('issue_request_id').notNull(),
challengeId: text('challenge_id').notNull(),
tokenDigest: text('token_digest').notNull(),
issuerAuthenticationId: text('issuer_authentication_id').notNull(),
issuerAuthenticatedAtMs: integer('issuer_authenticated_at_ms').notNull(),
issuerExpiresAtMs: integer('issuer_expires_at_ms').notNull(),
issuedAtMs: integer('issued_at_ms').notNull(),
expiresAtMs: integer('expires_at_ms').notNull(),
issueAuditEventId: text('issue_audit_event_id').notNull(),
consumedAtMs: integer('consumed_at_ms'),
claimMutationId: text('claim_mutation_id'),
claimRequestId: text('claim_request_id'),
claimedSubjectType: text('claimed_subject_type'),
claimedSubjectId: text('claimed_subject_id'),
credentialId: text('credential_id'),
credentialVersion: integer('credential_version'),
claimAuthenticationId: text('claim_authentication_id'),
claimAuthenticatedAtMs: integer('claim_authenticated_at_ms'),
claimExpiresAtMs: integer('claim_expires_at_ms'),
claimAssurance: text('claim_assurance'),
claimAuditEventId: text('claim_audit_event_id'),
},
(table) => [
primaryKey({ columns: [table.projectId, table.version] }),
check(
'ql3_local_owner_challenge_version_check',
sql`${table.version} between 1 and 2147483647`,
),
check(
'ql3_local_owner_challenge_issue_identity_check',
sql`length(${table.issueMutationId}) = 36 and length(${table.issueRequestId}) between 1 and 128 and ${table.issueAuditEventId} = ${table.issueMutationId}`,
),
check(
'ql3_local_owner_challenge_id_check',
sql`length(${table.challengeId}) = 22 and ${table.challengeId} not glob '*[^A-Za-z0-9_-]*'`,
),
check(
'ql3_local_owner_challenge_digest_check',
sql`length(${table.tokenDigest}) = 64 and ${table.tokenDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_owner_challenge_issuer_check',
sql`length(${table.issuerAuthenticationId}) between 1 and 128 and ${table.issuerAuthenticatedAtMs} <= ${table.issuedAtMs} and ${table.issuerExpiresAtMs} > ${table.issuedAtMs}`,
),
check(
'ql3_local_owner_challenge_lifetime_check',
sql`${table.issuedAtMs} >= 0 and ${table.expiresAtMs} > ${table.issuedAtMs} and ${table.expiresAtMs} - ${table.issuedAtMs} between 60000 and 1800000`,
),
check(
'ql3_local_owner_challenge_claim_shape_check',
sql`(${table.consumedAtMs} is null and ${table.claimMutationId} is null and ${table.claimRequestId} is null and ${table.claimedSubjectType} is null and ${table.claimedSubjectId} is null and ${table.credentialId} is null and ${table.credentialVersion} is null and ${table.claimAuthenticationId} is null and ${table.claimAuthenticatedAtMs} is null and ${table.claimExpiresAtMs} is null and ${table.claimAssurance} is null and ${table.claimAuditEventId} is null) or (${table.consumedAtMs} >= ${table.issuedAtMs} and ${table.consumedAtMs} < ${table.expiresAtMs} and length(${table.claimMutationId}) = 36 and length(${table.claimRequestId}) between 1 and 128 and ${table.claimedSubjectType} = 'user' and length(${table.claimedSubjectId}) between 1 and 255 and length(${table.credentialId}) between 1 and 64 and ${table.credentialVersion} between 1 and 2147483647 and length(${table.claimAuthenticationId}) between 1 and 128 and ${table.claimAuthenticatedAtMs} <= ${table.consumedAtMs} and ${table.claimExpiresAtMs} > ${table.consumedAtMs} and ${table.claimAssurance} = 'single_factor' and ${table.claimAuditEventId} = ${table.claimMutationId})`,
),
foreignKey({
columns: [table.projectId],
foreignColumns: [localProjects.id],
name: 'ql3_local_owner_challenge_project_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.issueAuditEventId],
foreignColumns: [localSecurityAuditEvents.eventId],
name: 'ql3_local_owner_challenge_issue_audit_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.claimedSubjectType, table.claimedSubjectId],
foreignColumns: [
localIdentitySubjects.subjectType,
localIdentitySubjects.subjectId,
],
name: 'ql3_local_owner_challenge_subject_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.credentialId, table.credentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_owner_challenge_credential_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.claimAuditEventId],
foreignColumns: [localSecurityAuditEvents.eventId],
name: 'ql3_local_owner_challenge_claim_audit_fk',
}).onDelete('restrict'),
uniqueIndex('ql3_local_owner_challenge_issue_mutation_uidx').on(
table.issueMutationId,
),
uniqueIndex('ql3_local_owner_challenge_id_uidx').on(table.challengeId),
uniqueIndex('ql3_local_owner_challenge_claim_mutation_uidx')
.on(table.claimMutationId)
.where(sql`${table.claimMutationId} is not null`),
index('ql3_local_owner_challenge_current_idx').on(
table.projectId,
sql`${table.version} desc`,
),
index('ql3_local_owner_challenge_expiry_idx')
.on(table.projectId, table.expiresAtMs, sql`${table.version} desc`)
.where(sql`${table.consumedAtMs} is null`),
],
);
export const localOwnerDeliveryAcknowledgements = sqliteTable(
'QingLong3LocalOwnerDeliveryAcknowledgements',
{
mutationId: text('mutation_id').primaryKey(),
kind: text('kind').notNull(),
requestId: text('request_id').notNull(),
projectId: text('project_id'),
subjectId: text('subject_id'),
credentialId: text('credential_id'),
challengeId: text('challenge_id'),
factDigest: text('fact_digest').notNull(),
deliveryDigest: text('delivery_digest').notNull(),
ttlMs: integer('ttl_ms').notNull(),
acknowledgedAtMs: integer('acknowledged_at_ms').notNull(),
provisioningMutationId: text('provisioning_mutation_id'),
challengeMutationId: text('challenge_mutation_id'),
},
(table) => [
check(
'ql3_local_owner_delivery_ack_mutation_check',
sql`length(${table.mutationId}) = 36`,
),
check(
'ql3_local_owner_delivery_ack_request_check',
sql`length(${table.requestId}) between 1 and 128`,
),
check(
'ql3_local_owner_delivery_ack_digest_check',
sql`length(${table.factDigest}) = 64 and ${table.factDigest} not glob '*[^0-9a-f]*' and length(${table.deliveryDigest}) = 64 and ${table.deliveryDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_owner_delivery_ack_time_check',
sql`${table.ttlMs} > 0 and ${table.acknowledgedAtMs} >= 0`,
),
check(
'ql3_local_owner_delivery_ack_shape_check',
sql`(${table.kind} = 'credential' and ${table.projectId} is null and length(${table.subjectId}) = 26 and ${table.subjectId} glob 'usr_*' and ${table.subjectId} not glob '*[^A-Za-z0-9_-]*' and length(${table.credentialId}) = 26 and ${table.credentialId} glob 'own_*' and ${table.credentialId} not glob '*[^A-Za-z0-9_-]*' and ${table.challengeId} is null and ${table.provisioningMutationId} = ${table.mutationId} and ${table.challengeMutationId} is null and ${table.ttlMs} between 600000 and 604800000) or (${table.kind} = 'challenge' and length(${table.projectId}) between 1 and 128 and ${table.subjectId} is null and ${table.credentialId} is null and length(${table.challengeId}) = 22 and ${table.challengeId} not glob '*[^A-Za-z0-9_-]*' and ${table.provisioningMutationId} is null and ${table.challengeMutationId} = ${table.mutationId} and ${table.ttlMs} between 60000 and 1800000)`,
),
foreignKey({
columns: [table.provisioningMutationId],
foreignColumns: [localIdentityProvisionings.mutationId],
name: 'ql3_local_owner_delivery_ack_provisioning_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.challengeMutationId],
foreignColumns: [localOwnerBootstrapChallenges.issueMutationId],
name: 'ql3_local_owner_delivery_ack_challenge_fk',
}).onDelete('restrict'),
],
);
export const localOwnerDeliveryAcknowledgementGc = sqliteTable(
'QingLong3LocalOwnerDeliveryAcknowledgementGc',
{
gcMutationId: text('gc_mutation_id').primaryKey(),
gcRequestId: text('gc_request_id').notNull(),
acknowledgementMutationId: text('acknowledgement_mutation_id').notNull(),
acknowledgementKind: text('acknowledgement_kind').notNull(),
deliveryDigest: text('delivery_digest').notNull(),
acknowledgedAtMs: integer('acknowledged_at_ms').notNull(),
acknowledgementSemanticDigest: text(
'acknowledgement_semantic_digest',
).notNull(),
bridgeClearEvidenceDigest: text('bridge_clear_evidence_digest').notNull(),
retentionPolicyVersion: integer('retention_policy_version').notNull(),
replayRetentionMs: integer('replay_retention_ms').notNull(),
auditRetentionMs: integer('audit_retention_ms').notNull(),
retentionPolicyDigest: text('retention_policy_digest').notNull(),
retentionEligibleAtMs: integer('retention_eligible_at_ms').notNull(),
compactedAtMs: integer('compacted_at_ms').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
provisioningMutationId: text('provisioning_mutation_id').references(
() => localIdentityProvisionings.mutationId,
{ onDelete: 'restrict' },
),
challengeMutationId: text('challenge_mutation_id').references(
() => localOwnerBootstrapChallenges.issueMutationId,
{ onDelete: 'restrict' },
),
},
(table) => [
check(
'ql3_local_owner_delivery_ack_gc_mutation_check',
sql`length(${table.gcMutationId}) = 36 and ${table.auditEventId} = ${table.gcMutationId} and length(${table.acknowledgementMutationId}) = 36 and ${table.acknowledgementMutationId} <> ${table.gcMutationId}`,
),
check(
'ql3_local_owner_delivery_ack_gc_request_check',
sql`length(${table.gcRequestId}) between 1 and 128`,
),
check(
'ql3_local_owner_delivery_ack_gc_digest_check',
sql`length(${table.deliveryDigest}) = 64 and ${table.deliveryDigest} not glob '*[^0-9a-f]*' and length(${table.acknowledgementSemanticDigest}) = 64 and ${table.acknowledgementSemanticDigest} not glob '*[^0-9a-f]*' and length(${table.bridgeClearEvidenceDigest}) = 64 and ${table.bridgeClearEvidenceDigest} not glob '*[^0-9a-f]*' and length(${table.retentionPolicyDigest}) = 64 and ${table.retentionPolicyDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_local_owner_delivery_ack_gc_retention_check',
sql`${table.retentionPolicyVersion} = 1 and ${table.replayRetentionMs} between 2592000000 and 315360000000 and ${table.auditRetentionMs} between 2592000000 and 315360000000`,
),
check(
'ql3_local_owner_delivery_ack_gc_time_check',
sql`${table.acknowledgedAtMs} >= 0 and ${table.retentionEligibleAtMs} <= ${table.compactedAtMs} and ${table.compactedAtMs} >= ${table.acknowledgedAtMs}`,
),
check(
'ql3_local_owner_delivery_ack_gc_shape_check',
sql`(${table.acknowledgementKind} = 'credential' and ${table.provisioningMutationId} = ${table.acknowledgementMutationId} and ${table.challengeMutationId} is null) or (${table.acknowledgementKind} = 'challenge' and ${table.provisioningMutationId} is null and ${table.challengeMutationId} = ${table.acknowledgementMutationId})`,
),
uniqueIndex('ql3_local_owner_delivery_ack_gc_ack_uidx').on(
table.acknowledgementMutationId,
),
index('ql3_local_owner_delivery_ack_gc_compacted_idx').on(
table.acknowledgementKind,
table.compactedAtMs,
table.acknowledgementMutationId,
),
],
);
export const localOwnerCredentialRecoveries = sqliteTable(
'QingLong3LocalOwnerCredentialRecoveries',
{
issueMutationId: text('issue_mutation_id').primaryKey(),
issueRequestId: text('issue_request_id').notNull(),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
previousCredentialId: text('previous_credential_id').notNull(),
previousCredentialVersion: integer('previous_credential_version').notNull(),
replacementCredentialId: text('replacement_credential_id').notNull(),
replacementCredentialVersion: integer(
'replacement_credential_version',
).notNull(),
state: text('state').notNull(),
issuedAtMs: integer('issued_at_ms').notNull(),
issueAuditEventId: text('issue_audit_event_id').notNull(),
deliveryDigest: text('delivery_digest'),
acknowledgedAtMs: integer('acknowledged_at_ms'),
completeMutationId: text('complete_mutation_id'),
completeRequestId: text('complete_request_id'),
revokedCredentialVersion: integer('revoked_credential_version'),
completedAtMs: integer('completed_at_ms'),
completeAuditEventId: text('complete_audit_event_id'),
},
(table) => [
check(
'ql3_local_owner_recovery_mutation_check',
sql`length(${table.issueMutationId}) = 36 and ${table.issueAuditEventId} = ${table.issueMutationId} and (${table.completeMutationId} is null or (length(${table.completeMutationId}) = 36 and ${table.completeAuditEventId} = ${table.completeMutationId} and ${table.completeMutationId} <> ${table.issueMutationId}))`,
),
check(
'ql3_local_owner_recovery_request_check',
sql`length(${table.issueRequestId}) between 1 and 128 and (${table.completeRequestId} is null or length(${table.completeRequestId}) between 1 and 128)`,
),
check(
'ql3_local_owner_recovery_identity_check',
sql`${table.subjectType} = 'user' and length(${table.subjectId}) = 26 and ${table.subjectId} glob 'usr_*' and ${table.subjectId} not glob '*[^A-Za-z0-9_-]*'`,
),
check(
'ql3_local_owner_recovery_credential_check',
sql`length(${table.previousCredentialId}) between 1 and 64 and ${table.previousCredentialId} not glob '*[^A-Za-z0-9._:-]*' and ${table.previousCredentialVersion} between 1 and 2147483646 and length(${table.replacementCredentialId}) between 1 and 64 and ${table.replacementCredentialId} not glob '*[^A-Za-z0-9._:-]*' and ${table.replacementCredentialId} <> ${table.previousCredentialId} and ${table.replacementCredentialVersion} = 1 and (${table.revokedCredentialVersion} is null or ${table.revokedCredentialVersion} = ${table.previousCredentialVersion} + 1)`,
),
check(
'ql3_local_owner_recovery_digest_check',
sql`${table.deliveryDigest} is null or (length(${table.deliveryDigest}) = 64 and ${table.deliveryDigest} not glob '*[^0-9a-f]*')`,
),
check(
'ql3_local_owner_recovery_time_check',
sql`${table.issuedAtMs} >= 0 and (${table.acknowledgedAtMs} is null or ${table.acknowledgedAtMs} >= ${table.issuedAtMs}) and (${table.completedAtMs} is null or ${table.completedAtMs} >= ${table.acknowledgedAtMs})`,
),
check(
'ql3_local_owner_recovery_shape_check',
sql`(${table.state} = 'issued' and ${table.deliveryDigest} is null and ${table.acknowledgedAtMs} is null and ${table.completeMutationId} is null and ${table.completeRequestId} is null and ${table.revokedCredentialVersion} is null and ${table.completedAtMs} is null and ${table.completeAuditEventId} is null) or (${table.state} = 'acknowledged' and ${table.deliveryDigest} is not null and ${table.acknowledgedAtMs} is not null and ${table.completeMutationId} is null and ${table.completeRequestId} is null and ${table.revokedCredentialVersion} is null and ${table.completedAtMs} is null and ${table.completeAuditEventId} is null) or (${table.state} = 'completed' and ${table.deliveryDigest} is not null and ${table.acknowledgedAtMs} is not null and ${table.completeMutationId} is not null and ${table.completeRequestId} is not null and ${table.revokedCredentialVersion} is not null and ${table.completedAtMs} is not null and ${table.completeAuditEventId} is not null)`,
),
foreignKey({
columns: [table.subjectType, table.subjectId],
foreignColumns: [
localIdentitySubjects.subjectType,
localIdentitySubjects.subjectId,
],
name: 'ql3_local_owner_recovery_subject_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.previousCredentialId, table.previousCredentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_owner_recovery_previous_fk',
}).onDelete('restrict'),
foreignKey({
columns: [
table.replacementCredentialId,
table.replacementCredentialVersion,
],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_owner_recovery_replacement_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.previousCredentialId, table.revokedCredentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_local_owner_recovery_revoked_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.issueAuditEventId],
foreignColumns: [localSecurityAuditEvents.eventId],
name: 'ql3_local_owner_recovery_issue_audit_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.completeAuditEventId],
foreignColumns: [localSecurityAuditEvents.eventId],
name: 'ql3_local_owner_recovery_complete_audit_fk',
}).onDelete('restrict'),
uniqueIndex('ql3_local_owner_recovery_open_subject_uidx')
.on(table.subjectId)
.where(sql`${table.state} <> 'completed'`),
uniqueIndex('ql3_local_owner_recovery_replacement_uidx').on(
table.replacementCredentialId,
table.replacementCredentialVersion,
),
uniqueIndex('ql3_local_owner_recovery_complete_mutation_uidx')
.on(table.completeMutationId)
.where(sql`${table.completeMutationId} is not null`),
index('ql3_local_owner_recovery_previous_idx').on(
table.previousCredentialId,
table.previousCredentialVersion,
table.state,
),
],
);
export const localOwnerPepperMaterialGc = sqliteTable(
'QingLong3LocalOwnerPepperMaterialGc',
{
prepareMutationId: text('prepare_mutation_id').primaryKey(),
prepareRequestId: text('prepare_request_id').notNull(),
pepperKeyId: text('pepper_key_id')
.notNull()
.references(() => localOwnerPepperKeys.pepperKeyId, {
onDelete: 'restrict',
}),
materialDigest: text('material_digest').notNull(),
backupMaterialDigest: text('backup_material_digest').notNull(),
activePepperKeyId: text('active_pepper_key_id')
.notNull()
.references(() => localOwnerPepperKeys.pepperKeyId, {
onDelete: 'restrict',
}),
activeGeneration: integer('active_generation')
.notNull()
.references(() => localOwnerPepperActivations.generation, {
onDelete: 'restrict',
}),
activeMaterialDigest: text('active_material_digest').notNull(),
retentionPolicyVersion: integer('retention_policy_version').notNull(),
acknowledgementRetentionMs: integer(
'acknowledgement_retention_ms',
).notNull(),
auditRetentionMs: integer('audit_retention_ms').notNull(),
backupRetentionMs: integer('backup_retention_ms').notNull(),
retentionPolicyDigest: text('retention_policy_digest').notNull(),
referencesInspectedAtMs: integer('references_inspected_at_ms').notNull(),
retentionEligibleAtMs: integer('retention_eligible_at_ms').notNull(),
preparedAtMs: integer('prepared_at_ms').notNull(),
prepareAuditEventId: text('prepare_audit_event_id')
.notNull()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
state: text('state').notNull(),
completeMutationId: text('complete_mutation_id'),
completeRequestId: text('complete_request_id'),
destructionProofDigest: text('destruction_proof_digest'),
completedAtMs: integer('completed_at_ms'),
completeAuditEventId: text('complete_audit_event_id').references(
() => localSecurityAuditEvents.eventId,
{ onDelete: 'restrict' },
),
},
(table) => [
check(
'ql3_local_owner_pepper_gc_mutation_check',
sql`length(${table.prepareMutationId}) = 36 and ${table.prepareAuditEventId} = ${table.prepareMutationId} and (${table.completeMutationId} is null or (length(${table.completeMutationId}) = 36 and ${table.completeMutationId} <> ${table.prepareMutationId} and ${table.completeAuditEventId} = ${table.completeMutationId}))`,
),
check(
'ql3_local_owner_pepper_gc_request_check',
sql`length(${table.prepareRequestId}) between 1 and 128 and (${table.completeRequestId} is null or length(${table.completeRequestId}) between 1 and 128)`,
),
check(
'ql3_local_owner_pepper_gc_key_check',
sql`length(${table.pepperKeyId}) between 1 and 64 and ${table.pepperKeyId} not glob '*[^A-Za-z0-9._:-]*' and length(${table.activePepperKeyId}) between 1 and 64 and ${table.activePepperKeyId} not glob '*[^A-Za-z0-9._:-]*' and ${table.pepperKeyId} <> ${table.activePepperKeyId} and ${table.activeGeneration} between 1 and 2147483647`,
),
check(
'ql3_local_owner_pepper_gc_digest_check',
sql`length(${table.materialDigest}) = 64 and ${table.materialDigest} not glob '*[^0-9a-f]*' and length(${table.backupMaterialDigest}) = 64 and ${table.backupMaterialDigest} not glob '*[^0-9a-f]*' and length(${table.activeMaterialDigest}) = 64 and ${table.activeMaterialDigest} not glob '*[^0-9a-f]*' and length(${table.retentionPolicyDigest}) = 64 and ${table.retentionPolicyDigest} not glob '*[^0-9a-f]*' and (${table.destructionProofDigest} is null or (length(${table.destructionProofDigest}) = 64 and ${table.destructionProofDigest} not glob '*[^0-9a-f]*'))`,
),
check(
'ql3_local_owner_pepper_gc_retention_check',
sql`${table.retentionPolicyVersion} = 1 and ${table.acknowledgementRetentionMs} between 604800000 and 315360000000 and ${table.auditRetentionMs} between 2592000000 and 315360000000 and ${table.backupRetentionMs} between 2592000000 and 315360000000`,
),
check(
'ql3_local_owner_pepper_gc_time_check',
sql`${table.referencesInspectedAtMs} = ${table.preparedAtMs} and ${table.retentionEligibleAtMs} <= ${table.preparedAtMs} and ${table.preparedAtMs} >= 0 and (${table.completedAtMs} is null or ${table.completedAtMs} >= ${table.preparedAtMs})`,
),
check(
'ql3_local_owner_pepper_gc_shape_check',
sql`(${table.state} = 'prepared' and ${table.completeMutationId} is null and ${table.completeRequestId} is null and ${table.destructionProofDigest} is null and ${table.completedAtMs} is null and ${table.completeAuditEventId} is null) or (${table.state} = 'completed' and ${table.completeMutationId} is not null and ${table.completeRequestId} is not null and ${table.destructionProofDigest} is not null and ${table.completedAtMs} is not null and ${table.completeAuditEventId} is not null)`,
),
uniqueIndex('ql3_local_owner_pepper_gc_key_uidx').on(table.pepperKeyId),
uniqueIndex('ql3_local_owner_pepper_gc_open_uidx')
.on(table.state)
.where(sql`${table.state} = 'prepared'`),
uniqueIndex('ql3_local_owner_pepper_gc_complete_mutation_uidx')
.on(table.completeMutationId)
.where(sql`${table.completeMutationId} is not null`),
index('ql3_local_owner_pepper_gc_state_idx').on(
table.state,
table.retentionEligibleAtMs,
table.pepperKeyId,
),
],
);
export const localIdentityAdministrationMutations = sqliteTable(
'QingLong3IdentityAdministrationMutations',
{
mutationId: text('mutation_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
operation: text('operation').notNull(),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
subjectVersion: integer('subject_version').notNull(),
expectedPreviousVersion: integer('expected_previous_version').notNull(),
status: text('status').notNull(),
changedByType: text('changed_by_type').notNull(),
changedById: text('changed_by_id').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.unique()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
identityCreatedAtMs: integer('identity_created_at_ms').notNull(),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_identity_admin_mutation_id_check',
sql`length(${table.mutationId}) = 36`,
),
check(
'ql3_identity_admin_operation_check',
sql`${table.operation} in ('register','enable','disable')`,
),
check(
'ql3_identity_admin_subject_check',
sql`${table.subjectType} in ('user','api_app','mcp_client','agent') and length(${table.subjectId}) between 1 and 255`,
),
check(
'ql3_identity_admin_transition_check',
sql`${table.subjectVersion} = ${table.expectedPreviousVersion} + 1 and ${table.subjectVersion} between 1 and 2147483647 and ${table.expectedPreviousVersion} between 0 and 2147483646 and ((${table.operation} = 'register' and ${table.expectedPreviousVersion} = 0 and ${table.status} = 'active') or (${table.operation} = 'enable' and ${table.expectedPreviousVersion} > 0 and ${table.status} = 'active') or (${table.operation} = 'disable' and ${table.expectedPreviousVersion} > 0 and ${table.status} = 'disabled'))`,
),
check(
'ql3_identity_admin_actor_check',
sql`${table.changedByType} = 'user' and length(${table.changedById}) between 1 and 255`,
),
check(
'ql3_identity_admin_audit_check',
sql`${table.auditEventId} = ${table.mutationId}`,
),
check(
'ql3_identity_admin_time_check',
sql`${table.identityCreatedAtMs} >= 0 and ${table.createdAtMs} >= ${table.identityCreatedAtMs}`,
),
foreignKey({
columns: [table.subjectType, table.subjectId],
foreignColumns: [
localIdentitySubjects.subjectType,
localIdentitySubjects.subjectId,
],
name: 'ql3_identity_admin_subject_fk',
}).onDelete('restrict'),
index('ql3_identity_admin_subject_idx').on(
table.subjectType,
table.subjectId,
sql`${table.subjectVersion} desc`,
),
],
);
export const localApiCredentialAdministrationMutations = sqliteTable(
'QingLong3ApiCredentialAdministrationMutations',
{
mutationId: text('mutation_id').primaryKey(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
operation: text('operation').notNull(),
credentialId: text('credential_id').notNull(),
credentialVersion: integer('credential_version').notNull(),
expectedPreviousVersion: integer('expected_previous_version').notNull(),
subjectType: text('subject_type').notNull(),
subjectId: text('subject_id').notNull(),
subjectStatus: text('subject_status').notNull(),
state: text('state').notNull(),
pepperKeyId: text('pepper_key_id').notNull(),
secretDigest: text('secret_digest').notNull(),
notBeforeAtMs: integer('not_before_at_ms').notNull(),
expiresAtMs: integer('expires_at_ms').notNull(),
deliveryDigest: text('delivery_digest'),
changedByType: text('changed_by_type').notNull(),
changedById: text('changed_by_id').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.unique()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
createdAtMs: integer('created_at_ms').notNull(),
},
(table) => [
check(
'ql3_credential_admin_mutation_id_check',
sql`length(${table.mutationId}) = 36`,
),
check(
'ql3_credential_admin_operation_check',
sql`${table.operation} in ('issue','rotate','revoke')`,
),
check(
'ql3_credential_admin_identity_check',
sql`length(${table.credentialId}) between 1 and 64 and ${table.credentialId} not glob '*[^A-Za-z0-9._:-]*' and ${table.subjectType} in ('user','api_app','mcp_client','agent') and length(${table.subjectId}) between 1 and 255 and ${table.subjectStatus} in ('active','disabled')`,
),
check(
'ql3_credential_admin_transition_check',
sql`${table.credentialVersion} = ${table.expectedPreviousVersion} + 1 and ${table.credentialVersion} between 1 and 2147483647 and ${table.expectedPreviousVersion} between 0 and 2147483646 and ((${table.operation} = 'issue' and ${table.expectedPreviousVersion} = 0 and ${table.state} = 'active') or (${table.operation} = 'rotate' and ${table.expectedPreviousVersion} > 0 and ${table.state} = 'active') or (${table.operation} = 'revoke' and ${table.expectedPreviousVersion} > 0 and ${table.state} = 'revoked'))`,
),
check(
'ql3_credential_admin_digest_check',
sql`length(${table.pepperKeyId}) between 1 and 64 and ${table.pepperKeyId} not glob '*[^A-Za-z0-9._:-]*' and length(${table.secretDigest}) = 64 and ${table.secretDigest} not glob '*[^0-9a-f]*' and ((${table.operation} in ('issue','rotate') and length(${table.deliveryDigest}) = 64 and ${table.deliveryDigest} not glob '*[^0-9a-f]*') or (${table.operation} = 'revoke' and ${table.deliveryDigest} is null))`,
),
check(
'ql3_credential_admin_lifetime_check',
sql`${table.createdAtMs} >= 0 and ${table.notBeforeAtMs} >= ${table.createdAtMs} and ${table.expiresAtMs} > ${table.notBeforeAtMs}`,
),
check(
'ql3_credential_admin_actor_check',
sql`${table.changedByType} = 'user' and length(${table.changedById}) between 1 and 255`,
),
check(
'ql3_credential_admin_audit_check',
sql`${table.auditEventId} = ${table.mutationId}`,
),
foreignKey({
columns: [table.credentialId, table.credentialVersion],
foreignColumns: [
localApiCredentials.credentialId,
localApiCredentials.version,
],
name: 'ql3_credential_admin_credential_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.credentialId, table.credentialVersion, table.pepperKeyId],
foreignColumns: [
localApiCredentialPepperBindings.credentialId,
localApiCredentialPepperBindings.credentialVersion,
localApiCredentialPepperBindings.pepperKeyId,
],
name: 'ql3_credential_admin_pepper_binding_fk',
}).onDelete('restrict'),
index('ql3_credential_admin_credential_idx').on(
table.credentialId,
sql`${table.credentialVersion} desc`,
),
index('ql3_credential_admin_subject_idx').on(
table.subjectType,
table.subjectId,
sql`${table.createdAtMs} desc`,
),
],
);
export const localApiCredentialDeliveryAcknowledgements = sqliteTable(
'QingLong3ApiCredentialDeliveryAcknowledgements',
{
credentialMutationId: text('credential_mutation_id')
.primaryKey()
.references(() => localApiCredentialAdministrationMutations.mutationId, {
onDelete: 'restrict',
}),
acknowledgementMutationId: text('acknowledgement_mutation_id')
.notNull()
.unique(),
projectId: text('project_id')
.notNull()
.references(() => localProjects.id, { onDelete: 'restrict' }),
deliveryDigest: text('delivery_digest').notNull(),
acknowledgedByType: text('acknowledged_by_type').notNull(),
acknowledgedById: text('acknowledged_by_id').notNull(),
auditEventId: text('audit_event_id')
.notNull()
.unique()
.references(() => localSecurityAuditEvents.eventId, {
onDelete: 'restrict',
}),
acknowledgedAtMs: integer('acknowledged_at_ms').notNull(),
},
(table) => [
check(
'ql3_credential_delivery_ack_identity_check',
sql`length(${table.credentialMutationId}) = 36 and length(${table.acknowledgementMutationId}) = 36 and ${table.credentialMutationId} <> ${table.acknowledgementMutationId}`,
),
check(
'ql3_credential_delivery_ack_digest_check',
sql`length(${table.deliveryDigest}) = 64 and ${table.deliveryDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_credential_delivery_ack_actor_check',
sql`${table.acknowledgedByType} = 'user' and length(${table.acknowledgedById}) between 1 and 255`,
),
check(
'ql3_credential_delivery_ack_audit_check',
sql`${table.auditEventId} = ${table.acknowledgementMutationId}`,
),
check(
'ql3_credential_delivery_ack_time_check',
sql`${table.acknowledgedAtMs} >= 0`,
),
index('ql3_credential_delivery_ack_project_idx').on(
table.projectId,
sql`${table.acknowledgedAtMs} desc`,
),
],
);
export const pluginPackageLifecycleEvents = sqliteTable(
'QingLong3PluginPackageLifecycleEvents',
{
eventDigest: text('event_digest').primaryKey(),
mutationId: text('mutation_id').notNull(),
dispatchId: text('dispatch_id').notNull(),
approvedActionType: text('approved_action_type').notNull(),
action: text('action').notNull(),
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
lockDigest: text('lock_digest').notNull(),
installVersion: integer('install_version').notNull(),
installRecordDigest: text('install_record_digest').notNull(),
expectedVersion: integer('expected_version').notNull(),
expectedDisposition: text('expected_disposition').notNull(),
expectedEventDigest: text('expected_event_digest').references(
(): AnySQLiteColumn => pluginPackageLifecycleEvents.eventDigest,
{ onDelete: 'restrict' },
),
generationDigest: text('generation_digest').notNull(),
materializedRevisionDigest: text('materialized_revision_digest').notNull(),
currentToolSnapshotDigest: text('current_tool_snapshot_digest').notNull(),
referenceGraphDigest: text('reference_graph_digest').notNull(),
impactDigest: text('impact_digest').notNull(),
actionDigest: text('action_digest').notNull(),
requestedByType: text('requested_by_type').notNull(),
requestedById: text('requested_by_id').notNull(),
approvedByType: text('approved_by_type').notNull(),
approvedById: text('approved_by_id').notNull(),
authorizationMode: text('authorization_mode').notNull(),
occurredAtMs: integer('occurred_at_ms').notNull(),
eventJson: text('event_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_lifecycle_mutation_uidx').on(
table.mutationId,
),
uniqueIndex('ql3_plugin_package_lifecycle_dispatch_uidx').on(
table.dispatchId,
),
uniqueIndex('ql3_plugin_package_lifecycle_target_version_uidx').on(
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
table.expectedVersion,
),
foreignKey({
columns: [
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
table.installRecordDigest,
],
foreignColumns: [
pluginPackageInstalls.projectId,
pluginPackageInstalls.packageName,
pluginPackageInstalls.installationId,
pluginPackageInstalls.lockDigest,
pluginPackageInstalls.recordDigest,
],
name: 'ql3_plugin_package_lifecycle_install_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
columns: [
table.dispatchId,
table.projectId,
table.approvedActionType,
table.actionDigest,
table.impactDigest,
],
foreignColumns: [
approvedActionDispatches.dispatchId,
approvedActionDispatches.projectId,
approvedActionDispatches.actionType,
approvedActionDispatches.actionDigest,
approvedActionDispatches.previewDigest,
],
name: 'ql3_plugin_package_lifecycle_dispatch_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
check(
'ql3_plugin_package_lifecycle_identity_check',
sql`length(${table.mutationId}) between 1 and 128 and length(${table.dispatchId}) between 1 and 128 and length(${table.packageName}) between 1 and 63 and length(${table.installationId}) between 1 and 128 and ${table.installVersion} between 1 and 2147483647 and ${table.action} in ('disable','enable','uninstall') and ${table.approvedActionType} = 'plugin_package.lifecycle.' || ${table.action}`,
),
check(
'ql3_plugin_package_lifecycle_expectation_check',
sql`(${table.action} = 'disable' and ${table.expectedDisposition} = 'active') or (${table.action} in ('enable','uninstall') and ${table.expectedDisposition} = 'disabled')`,
),
check(
'ql3_plugin_package_lifecycle_origin_check',
sql`(${table.expectedVersion} = 0 and ${table.expectedDisposition} = 'active' and ${table.expectedEventDigest} is null) or (${table.expectedVersion} between 1 and 2147483646 and ${table.expectedEventDigest} is not null)`,
),
check(
'ql3_plugin_package_lifecycle_subject_check',
sql`${table.requestedByType} = 'user' and ${table.approvedByType} = 'user' and length(${table.requestedById}) between 1 and 255 and length(${table.approvedById}) between 1 and 255 and ${table.authorizationMode} in ('human_confirmation','separation_of_duty') and ((${table.authorizationMode} = 'human_confirmation' and ${table.requestedById} = ${table.approvedById}) or (${table.authorizationMode} = 'separation_of_duty' and ${table.requestedById} <> ${table.approvedById}))`,
),
check(
'ql3_plugin_package_lifecycle_digest_check',
sql`length(${table.eventDigest}) = 64 and ${table.eventDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.installRecordDigest}) = 64 and ${table.installRecordDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.materializedRevisionDigest}) = 64 and ${table.materializedRevisionDigest} not glob '*[^0-9a-f]*' and length(${table.currentToolSnapshotDigest}) = 64 and ${table.currentToolSnapshotDigest} not glob '*[^0-9a-f]*' and length(${table.referenceGraphDigest}) = 64 and ${table.referenceGraphDigest} not glob '*[^0-9a-f]*' and length(${table.impactDigest}) = 64 and ${table.impactDigest} not glob '*[^0-9a-f]*' and length(${table.actionDigest}) = 64 and ${table.actionDigest} not glob '*[^0-9a-f]*' and (${table.expectedEventDigest} is null or (length(${table.expectedEventDigest}) = 64 and ${table.expectedEventDigest} not glob '*[^0-9a-f]*'))`,
),
check(
'ql3_plugin_package_lifecycle_json_check',
sql`length(${table.eventJson}) between 2 and 524288 and json_valid(${table.eventJson}) and json_type(${table.eventJson}) = 'object' and json_extract(${table.eventJson}, '$.schema') = 'qinglong/plugin-package-lifecycle-event@v1' and json_extract(${table.eventJson}, '$.mutationId') = ${table.mutationId} and json_extract(${table.eventJson}, '$.dispatchId') = ${table.dispatchId} and json_extract(${table.eventJson}, '$.impact.schema') = 'qinglong/plugin-package-lifecycle-impact@v1' and json_extract(${table.eventJson}, '$.impact.action') = ${table.action} and json_extract(${table.eventJson}, '$.impact.target.projectId') = ${table.projectId} and json_extract(${table.eventJson}, '$.impact.target.packageName') = ${table.packageName} and json_extract(${table.eventJson}, '$.impact.target.installationId') = ${table.installationId} and json_extract(${table.eventJson}, '$.impact.target.lockDigest') = ${table.lockDigest} and json_extract(${table.eventJson}, '$.impact.target.installVersion') = ${table.installVersion} and json_extract(${table.eventJson}, '$.impact.target.installRecordDigest') = ${table.installRecordDigest} and json_extract(${table.eventJson}, '$.impact.expected.version') = ${table.expectedVersion} and json_extract(${table.eventJson}, '$.impact.expected.disposition') = ${table.expectedDisposition} and ((${table.expectedEventDigest} is null and json_type(${table.eventJson}, '$.impact.expected.eventDigest') = 'null') or json_extract(${table.eventJson}, '$.impact.expected.eventDigest') = ${table.expectedEventDigest}) and json_extract(${table.eventJson}, '$.impact.generationDigest') = ${table.generationDigest} and json_extract(${table.eventJson}, '$.impact.materializedRevisionDigest') = ${table.materializedRevisionDigest} and json_extract(${table.eventJson}, '$.impact.currentToolSnapshotDigest') = ${table.currentToolSnapshotDigest} and json_extract(${table.eventJson}, '$.impact.referenceGraphDigest') = ${table.referenceGraphDigest} and json_extract(${table.eventJson}, '$.impact.impactDigest') = ${table.impactDigest} and json_extract(${table.eventJson}, '$.actionDigest') = ${table.actionDigest} and json_extract(${table.eventJson}, '$.requestedBy.type') = ${table.requestedByType} and json_extract(${table.eventJson}, '$.requestedBy.id') = ${table.requestedById} and json_extract(${table.eventJson}, '$.approvedBy.type') = ${table.approvedByType} and json_extract(${table.eventJson}, '$.approvedBy.id') = ${table.approvedById} and json_extract(${table.eventJson}, '$.authorizationMode') = ${table.authorizationMode} and json_extract(${table.eventJson}, '$.occurredAtMs') = ${table.occurredAtMs} and json_extract(${table.eventJson}, '$.eventDigest') = ${table.eventDigest}`,
),
check(
'ql3_plugin_package_lifecycle_time_check',
sql`${table.occurredAtMs} >= 0`,
),
index('ql3_plugin_package_lifecycle_project_idx').on(
table.projectId,
table.packageName,
table.occurredAtMs,
table.eventDigest,
),
],
);
export const pluginPackageLifecycleHeads = sqliteTable(
'QingLong3PluginPackageLifecycleHeads',
{
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
lockDigest: text('lock_digest').notNull(),
installRecordDigest: text('install_record_digest').notNull(),
version: integer('version').notNull(),
disposition: text('disposition').notNull(),
eventDigest: text('event_digest')
.notNull()
.references(() => pluginPackageLifecycleEvents.eventDigest, {
onDelete: 'restrict',
}),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.packageName] }),
uniqueIndex('ql3_plugin_package_lifecycle_head_event_uidx').on(
table.eventDigest,
),
foreignKey({
columns: [
table.projectId,
table.packageName,
table.installationId,
table.lockDigest,
table.installRecordDigest,
],
foreignColumns: [
pluginPackageInstalls.projectId,
pluginPackageInstalls.packageName,
pluginPackageInstalls.installationId,
pluginPackageInstalls.lockDigest,
pluginPackageInstalls.recordDigest,
],
name: 'ql3_plugin_package_lifecycle_head_install_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
check(
'ql3_plugin_package_lifecycle_head_state_check',
sql`${table.version} between 1 and 2147483647 and ${table.disposition} in ('active','disabled','uninstalled') and ${table.updatedAtMs} >= 0`,
),
check(
'ql3_plugin_package_lifecycle_head_digest_check',
sql`length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.installRecordDigest}) = 64 and ${table.installRecordDigest} not glob '*[^0-9a-f]*' and length(${table.eventDigest}) = 64 and ${table.eventDigest} not glob '*[^0-9a-f]*'`,
),
],
);
export const pluginPackageLifecycleReceipts = sqliteTable(
'QingLong3PluginPackageLifecycleReceipts',
{
eventDigest: text('event_digest')
.primaryKey()
.references(() => pluginPackageLifecycleEvents.eventDigest, {
onDelete: 'restrict',
}),
receiptDigest: text('receipt_digest').notNull(),
projectId: text('project_id').notNull(),
action: text('action').notNull(),
capabilityStatus: text('capability_status').notNull(),
taskCount: integer('task_count').notNull(),
previousActiveVectorDigest: text('previous_active_vector_digest').notNull(),
currentActiveVectorDigest: text('current_active_vector_digest').notNull(),
currentToolSnapshotDigest: text('current_tool_snapshot_digest').notNull(),
retainedSourceCount: integer('retained_source_count').notNull(),
committedAtMs: integer('committed_at_ms').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_lifecycle_receipt_uidx').on(
table.receiptDigest,
),
foreignKey({
columns: [
table.projectId,
table.currentActiveVectorDigest,
table.currentToolSnapshotDigest,
],
foreignColumns: [
projectToolDefinitionSnapshots.projectId,
projectToolDefinitionSnapshots.activeVectorDigest,
projectToolDefinitionSnapshots.snapshotDigest,
],
name: 'ql3_plugin_package_lifecycle_receipt_snapshot_fk',
}).onDelete('restrict'),
check(
'ql3_plugin_package_lifecycle_receipt_state_check',
sql`(${table.action} = 'disable' and ${table.capabilityStatus} = 'withdrawn' and ${table.previousActiveVectorDigest} <> ${table.currentActiveVectorDigest}) or (${table.action} = 'enable' and ${table.capabilityStatus} = 'restored' and ${table.previousActiveVectorDigest} <> ${table.currentActiveVectorDigest}) or (${table.action} = 'uninstall' and ${table.capabilityStatus} = 'retired' and ${table.taskCount} = 0 and ${table.previousActiveVectorDigest} = ${table.currentActiveVectorDigest})`,
),
check(
'ql3_plugin_package_lifecycle_receipt_bounds_check',
sql`${table.taskCount} between 0 and 128 and ${table.retainedSourceCount} between 0 and 128 and ${table.committedAtMs} >= 0`,
),
check(
'ql3_plugin_package_lifecycle_receipt_digest_check',
sql`length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*' and length(${table.previousActiveVectorDigest}) = 64 and ${table.previousActiveVectorDigest} not glob '*[^0-9a-f]*' and length(${table.currentActiveVectorDigest}) = 64 and ${table.currentActiveVectorDigest} not glob '*[^0-9a-f]*' and length(${table.currentToolSnapshotDigest}) = 64 and ${table.currentToolSnapshotDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_lifecycle_receipt_json_check',
sql`length(${table.receiptJson}) between 2 and 524288 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-lifecycle-receipt@v1' and json_extract(${table.receiptJson}, '$.eventDigest') = ${table.eventDigest} and json_extract(${table.receiptJson}, '$.action') = ${table.action} and json_extract(${table.receiptJson}, '$.target.projectId') = ${table.projectId} and json_extract(${table.receiptJson}, '$.capability.status') = ${table.capabilityStatus} and json_array_length(json_extract(${table.receiptJson}, '$.capability.taskTransitions')) = ${table.taskCount} and json_extract(${table.receiptJson}, '$.capability.previousActiveVectorDigest') = ${table.previousActiveVectorDigest} and json_extract(${table.receiptJson}, '$.capability.currentActiveVectorDigest') = ${table.currentActiveVectorDigest} and json_extract(${table.receiptJson}, '$.capability.currentToolSnapshotDigest') = ${table.currentToolSnapshotDigest} and json_extract(${table.receiptJson}, '$.capability.retainedSourceCount') = ${table.retainedSourceCount} and json_extract(${table.receiptJson}, '$.committedAtMs') = ${table.committedAtMs} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest}`,
),
index('ql3_plugin_package_lifecycle_receipt_snapshot_idx').on(
table.projectId,
table.currentActiveVectorDigest,
table.eventDigest,
),
],
);
export const pluginPackageLifecycleTasks = sqliteTable(
'QingLong3PluginPackageLifecycleTasks',
{
eventDigest: text('event_digest')
.notNull()
.references(() => pluginPackageLifecycleReceipts.eventDigest, {
onDelete: 'restrict',
}),
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
previousRevision: integer('previous_revision').notNull(),
currentRevision: integer('current_revision').notNull(),
previousContentDigest: text('previous_content_digest').notNull(),
currentContentDigest: text('current_content_digest').notNull(),
previousEnabled: integer('previous_enabled').notNull(),
currentEnabled: integer('current_enabled').notNull(),
},
(table) => [
primaryKey({ columns: [table.eventDigest, table.taskId] }),
foreignKey({
columns: [table.projectId, table.taskId, table.previousRevision],
foreignColumns: [
taskDefinitionRevisions.projectId,
taskDefinitionRevisions.taskId,
taskDefinitionRevisions.revision,
],
name: 'ql3_plugin_package_lifecycle_task_previous_fk',
}).onDelete('restrict'),
foreignKey({
columns: [table.projectId, table.taskId, table.currentRevision],
foreignColumns: [
taskDefinitionRevisions.projectId,
taskDefinitionRevisions.taskId,
taskDefinitionRevisions.revision,
],
name: 'ql3_plugin_package_lifecycle_task_current_fk',
}).onDelete('restrict'),
check(
'ql3_plugin_package_lifecycle_task_transition_check',
sql`${table.currentRevision} = ${table.previousRevision} + 1 and ${table.previousEnabled} in (0, 1) and ${table.currentEnabled} in (0, 1) and ${table.previousEnabled} <> ${table.currentEnabled}`,
),
check(
'ql3_plugin_package_lifecycle_task_digest_check',
sql`length(${table.previousContentDigest}) = 64 and ${table.previousContentDigest} not glob '*[^0-9a-f]*' and length(${table.currentContentDigest}) = 64 and ${table.currentContentDigest} not glob '*[^0-9a-f]*'`,
),
index('ql3_plugin_package_lifecycle_task_idx').on(
table.projectId,
table.taskId,
table.eventDigest,
),
],
);
export const pluginPackageAutomationPublications = sqliteTable(
'QingLong3PluginPackageAutomationPublications',
{
publicationDigest: text('publication_digest').primaryKey(),
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
lockDigest: text('lock_digest').notNull(),
generation: integer('generation').notNull(),
generationDigest: text('generation_digest')
.notNull()
.references(() => pluginPackageMaterializedRevisions.generationDigest, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
materializedRevisionDigest: text('materialized_revision_digest').notNull(),
state: text('state').notNull(),
version: integer('version').notNull(),
previousPublicationDigest: text('previous_publication_digest').references(
(): AnySQLiteColumn =>
pluginPackageAutomationPublications.publicationDigest,
{ onDelete: 'restrict', onUpdate: 'restrict' },
),
lifecycleEventDigest: text('lifecycle_event_digest').references(
() => pluginPackageLifecycleEvents.eventDigest,
{ onDelete: 'restrict', onUpdate: 'restrict' },
),
publishedAtMs: integer('published_at_ms').notNull(),
publicationJson: text('publication_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_automation_publication_version_uidx').on(
table.projectId,
table.packageName,
table.version,
),
uniqueIndex('ql3_plugin_package_automation_publication_previous_uidx')
.on(table.previousPublicationDigest)
.where(sql`${table.previousPublicationDigest} is not null`),
index('ql3_plugin_package_automation_publication_generation_idx').on(
table.generationDigest,
table.publicationDigest,
),
check(
'ql3_plugin_package_automation_publication_identity_check',
sql`length(${table.projectId}) between 1 and 128 and length(${table.packageName}) between 1 and 63 and length(${table.installationId}) between 1 and 128 and ${table.generation} between 1 and 2147483647 and ${table.state} in ('active','withdrawn','absent') and ${table.version} between 1 and 2147483647 and ${table.publishedAtMs} >= 0 and ((${table.version} = 1 and ${table.state} in ('active','absent') and ${table.previousPublicationDigest} is null and ${table.lifecycleEventDigest} is null) or (${table.version} > 1 and ${table.previousPublicationDigest} is not null)) and (${table.state} <> 'withdrawn' or ${table.lifecycleEventDigest} is not null)`,
),
check(
'ql3_plugin_package_automation_publication_digest_check',
sql`length(${table.publicationDigest}) = 64 and ${table.publicationDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.materializedRevisionDigest}) = 64 and ${table.materializedRevisionDigest} not glob '*[^0-9a-f]*' and (${table.previousPublicationDigest} is null or (length(${table.previousPublicationDigest}) = 64 and ${table.previousPublicationDigest} not glob '*[^0-9a-f]*')) and (${table.lifecycleEventDigest} is null or (length(${table.lifecycleEventDigest}) = 64 and ${table.lifecycleEventDigest} not glob '*[^0-9a-f]*'))`,
),
check(
'ql3_plugin_package_automation_publication_json_check',
sql`length(cast(${table.publicationJson} as blob)) between 2 and 12582912 and json_valid(${table.publicationJson}) and json_type(${table.publicationJson}) = 'object' and json_extract(${table.publicationJson}, '$.schema') = 'qinglong/plugin-package-automation-publication@v1' and json_extract(${table.publicationJson}, '$.target.projectId') = ${table.projectId} and json_extract(${table.publicationJson}, '$.target.packageName') = ${table.packageName} and json_extract(${table.publicationJson}, '$.target.installationId') = ${table.installationId} and json_extract(${table.publicationJson}, '$.target.lockDigest') = ${table.lockDigest} and json_extract(${table.publicationJson}, '$.target.generation') = ${table.generation} and json_extract(${table.publicationJson}, '$.target.generationDigest') = ${table.generationDigest} and json_extract(${table.publicationJson}, '$.target.materializedRevisionDigest') = ${table.materializedRevisionDigest} and json_extract(${table.publicationJson}, '$.state') = ${table.state} and json_extract(${table.publicationJson}, '$.version') = ${table.version} and ((${table.previousPublicationDigest} is null and json_type(${table.publicationJson}, '$.previousPublicationDigest') = 'null') or json_extract(${table.publicationJson}, '$.previousPublicationDigest') = ${table.previousPublicationDigest}) and ((${table.lifecycleEventDigest} is null and json_type(${table.publicationJson}, '$.lifecycleEventDigest') = 'null') or json_extract(${table.publicationJson}, '$.lifecycleEventDigest') = ${table.lifecycleEventDigest}) and json_extract(${table.publicationJson}, '$.publishedAtMs') = ${table.publishedAtMs} and json_extract(${table.publicationJson}, '$.publicationDigest') = ${table.publicationDigest} and json_type(${table.publicationJson}, '$.definitions.workflows') = 'array' and json_type(${table.publicationJson}, '$.definitions.prompts') = 'array' and ((${table.state} = 'absent' and json_array_length(json_extract(${table.publicationJson}, '$.definitions.workflows')) + json_array_length(json_extract(${table.publicationJson}, '$.definitions.prompts')) = 0) or (${table.state} <> 'absent' and json_array_length(json_extract(${table.publicationJson}, '$.definitions.workflows')) + json_array_length(json_extract(${table.publicationJson}, '$.definitions.prompts')) > 0))`,
),
],
);
export const pluginPackageAutomationPublicationHeads = sqliteTable(
'QingLong3PluginPackageAutomationPublicationHeads',
{
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
publicationDigest: text('publication_digest')
.notNull()
.references(() => pluginPackageAutomationPublications.publicationDigest, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
generationDigest: text('generation_digest').notNull(),
state: text('state').notNull(),
version: integer('version').notNull(),
updatedAtMs: integer('updated_at_ms').notNull(),
},
(table) => [
primaryKey({ columns: [table.projectId, table.packageName] }),
uniqueIndex(
'ql3_plugin_package_automation_publication_head_digest_uidx',
).on(table.publicationDigest),
check(
'ql3_plugin_package_automation_publication_head_state_check',
sql`length(${table.projectId}) between 1 and 128 and length(${table.packageName}) between 1 and 63 and length(${table.publicationDigest}) = 64 and ${table.publicationDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and ${table.state} in ('active','withdrawn','absent') and ${table.version} between 1 and 2147483647 and ${table.updatedAtMs} >= 0`,
),
],
);
export const pluginPackageWorkflowAdmissions = sqliteTable(
'QingLong3PluginPackageWorkflowAdmissions',
{
planDigest: text('plan_digest').primaryKey(),
planId: text('plan_id').notNull(),
runId: text('run_id')
.notNull()
.references(() => runs.id, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
projectId: text('project_id').notNull(),
packageName: text('package_name').notNull(),
installationId: text('installation_id').notNull(),
lockDigest: text('lock_digest').notNull(),
generation: integer('generation').notNull(),
generationDigest: text('generation_digest').notNull(),
materializedRevisionDigest: text('materialized_revision_digest').notNull(),
publicationDigest: text('publication_digest')
.notNull()
.references(() => pluginPackageAutomationPublications.publicationDigest, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
workflowId: text('workflow_id').notNull(),
workflowDefinitionDigest: text('workflow_definition_digest').notNull(),
stepCount: integer('step_count').notNull(),
admittedAtMs: integer('admitted_at_ms').notNull(),
finalRunVersion: integer('final_run_version').notNull(),
finalRunEventSequence: integer('final_run_event_sequence').notNull(),
receiptDigest: text('receipt_digest').notNull(),
planJson: text('plan_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
uniqueIndex('ql3_plugin_package_workflow_admission_plan_uidx').on(
table.planId,
),
uniqueIndex('ql3_plugin_package_workflow_admission_run_uidx').on(
table.runId,
),
uniqueIndex('ql3_plugin_package_workflow_admission_receipt_uidx').on(
table.receiptDigest,
),
uniqueIndex('ql3_plugin_package_workflow_admission_plan_run_uidx').on(
table.planDigest,
table.runId,
),
index('ql3_plugin_package_workflow_admission_target_idx').on(
table.projectId,
table.packageName,
table.admittedAtMs,
table.planDigest,
),
index('ql3_plugin_package_workflow_admission_workflow_history_idx').on(
table.projectId,
table.packageName,
table.workflowId,
table.admittedAtMs,
table.runId,
),
check(
'ql3_plugin_package_workflow_admission_identity_check',
sql`length(${table.planId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.packageName}) between 1 and 63 and length(${table.installationId}) between 1 and 128 and length(${table.workflowId}) between 1 and 63 and ${table.generation} between 1 and 2147483647 and ${table.stepCount} between 1 and 128 and ${table.admittedAtMs} >= 0 and ${table.finalRunVersion} = ${table.stepCount} + 1 and ${table.finalRunEventSequence} = ${table.stepCount} + 1`,
),
check(
'ql3_plugin_package_workflow_admission_digest_check',
sql`length(${table.planDigest}) = 64 and ${table.planDigest} not glob '*[^0-9a-f]*' and length(${table.lockDigest}) = 64 and ${table.lockDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.materializedRevisionDigest}) = 64 and ${table.materializedRevisionDigest} not glob '*[^0-9a-f]*' and length(${table.publicationDigest}) = 64 and ${table.publicationDigest} not glob '*[^0-9a-f]*' and length(${table.workflowDefinitionDigest}) = 64 and ${table.workflowDefinitionDigest} not glob '*[^0-9a-f]*' and length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_workflow_admission_plan_json_check',
sql`length(cast(${table.planJson} as blob)) between 2 and 262144 and json_valid(${table.planJson}) and json_type(${table.planJson}) = 'object' and json_extract(${table.planJson}, '$.schema') = 'qinglong/plugin-package-workflow-execution-plan@v1' and json_extract(${table.planJson}, '$.planId') = ${table.planId} and json_extract(${table.planJson}, '$.planDigest') = ${table.planDigest} and json_extract(${table.planJson}, '$.runId') = ${table.runId} and json_extract(${table.planJson}, '$.target.projectId') = ${table.projectId} and json_extract(${table.planJson}, '$.target.packageName') = ${table.packageName} and json_extract(${table.planJson}, '$.target.installationId') = ${table.installationId} and json_extract(${table.planJson}, '$.target.lockDigest') = ${table.lockDigest} and json_extract(${table.planJson}, '$.target.generation') = ${table.generation} and json_extract(${table.planJson}, '$.target.generationDigest') = ${table.generationDigest} and json_extract(${table.planJson}, '$.target.materializedRevisionDigest') = ${table.materializedRevisionDigest} and json_extract(${table.planJson}, '$.target.publicationDigest') = ${table.publicationDigest} and json_extract(${table.planJson}, '$.target.workflowId') = ${table.workflowId} and json_extract(${table.planJson}, '$.target.workflowDefinitionDigest') = ${table.workflowDefinitionDigest} and json_extract(${table.planJson}, '$.plannedAtMs') = ${table.admittedAtMs} and json_array_length(json_extract(${table.planJson}, '$.steps')) = ${table.stepCount}`,
),
check(
'ql3_plugin_package_workflow_admission_receipt_json_check',
sql`length(cast(${table.receiptJson} as blob)) between 2 and 262144 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-workflow-admission-receipt@v1' and json_extract(${table.receiptJson}, '$.planId') = ${table.planId} and json_extract(${table.receiptJson}, '$.planDigest') = ${table.planDigest} and json_extract(${table.receiptJson}, '$.runId') = ${table.runId} and json_extract(${table.receiptJson}, '$.publicationDigest') = ${table.publicationDigest} and json_extract(${table.receiptJson}, '$.workflowId') = ${table.workflowId} and json_extract(${table.receiptJson}, '$.admittedAtMs') = ${table.admittedAtMs} and json_extract(${table.receiptJson}, '$.finalRunVersion') = ${table.finalRunVersion} and json_extract(${table.receiptJson}, '$.finalRunEventSequence') = ${table.finalRunEventSequence} and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest} and json_array_length(json_extract(${table.receiptJson}, '$.steps')) = ${table.stepCount}`,
),
],
);
export const pluginPackageWorkflowAdmissionSteps = sqliteTable(
'QingLong3PluginPackageWorkflowAdmissionSteps',
{
planDigest: text('plan_digest').notNull(),
runId: text('run_id').notNull(),
stepKey: text('step_key').notNull(),
stepRunId: text('step_run_id').notNull(),
taskId: text('task_id').notNull(),
taskDefinitionRef: text('task_definition_ref').notNull(),
taskDefinitionDigest: text('task_definition_digest').notNull(),
needsJson: text('needs_json', { mode: 'json' })
.$type<readonly string[]>()
.notNull(),
initialStatus: text('initial_status').notNull(),
mutationId: text('mutation_id')
.notNull()
.references(() => stepRunMutations.mutationId, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
eventId: text('event_id')
.notNull()
.references(() => runEvents.id, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
},
(table) => [
primaryKey({ columns: [table.planDigest, table.stepKey] }),
foreignKey({
columns: [table.planDigest, table.runId],
foreignColumns: [
pluginPackageWorkflowAdmissions.planDigest,
pluginPackageWorkflowAdmissions.runId,
],
name: 'ql3_plugin_package_workflow_admission_step_admission_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_plugin_package_workflow_admission_step_run_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
uniqueIndex('ql3_plugin_package_workflow_admission_step_run_uidx').on(
table.stepRunId,
),
uniqueIndex('ql3_plugin_package_workflow_admission_step_mutation_uidx').on(
table.mutationId,
),
uniqueIndex('ql3_plugin_package_workflow_admission_step_event_uidx').on(
table.eventId,
),
index('ql3_plugin_package_workflow_admission_step_task_idx').on(
table.taskId,
table.taskDefinitionDigest,
table.planDigest,
),
check(
'ql3_plugin_package_workflow_admission_step_identity_check',
sql`length(${table.runId}) between 1 and 128 and length(${table.stepKey}) between 1 and 63 and length(${table.stepRunId}) between 1 and 128 and length(${table.taskId}) between 1 and 63 and length(cast(${table.taskDefinitionRef} as blob)) between 1 and 512 and length(${table.mutationId}) between 1 and 128 and length(${table.eventId}) between 1 and 128 and ${table.initialStatus} in ('pending','ready')`,
),
check(
'ql3_plugin_package_workflow_admission_step_digest_check',
sql`length(${table.planDigest}) = 64 and ${table.planDigest} not glob '*[^0-9a-f]*' and length(${table.taskDefinitionDigest}) = 64 and ${table.taskDefinitionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_workflow_admission_step_needs_check',
sql`length(cast(${table.needsJson} as blob)) between 2 and 8192 and json_valid(${table.needsJson}) and json_type(${table.needsJson}) = 'array' and json_array_length(${table.needsJson}) between 0 and 127`,
),
],
);
export const pluginPackageWorkflowTaskAttemptAdmissions = sqliteTable(
'QingLong3PluginPackageWorkflowTaskAttemptAdmissions',
{
receiptDigest: text('receipt_digest').primaryKey(),
attemptId: text('attempt_id')
.notNull()
.references(() => runAttempts.id, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
planDigest: text('plan_digest').notNull(),
runId: text('run_id').notNull(),
stepRunId: text('step_run_id').notNull(),
stepRunVersion: integer('step_run_version').notNull(),
stepRunDigest: text('step_run_digest').notNull(),
generationDigest: text('generation_digest').notNull(),
resourceTaskId: text('resource_task_id').notNull(),
taskReconciliationReceiptDigest: text(
'task_reconciliation_receipt_digest',
).notNull(),
projectId: text('project_id').notNull(),
taskId: text('task_id').notNull(),
taskRevision: text('task_revision').notNull(),
taskDefinitionDigest: text('task_definition_digest').notNull(),
executorType: text('executor_type').notNull(),
executionDigest: text('execution_digest').notNull(),
attemptNumber: integer('attempt_number').notNull(),
eventId: text('event_id')
.notNull()
.references(() => runEvents.id, {
onDelete: 'restrict',
onUpdate: 'restrict',
}),
runVersion: integer('run_version').notNull(),
runEventSequence: integer('run_event_sequence').notNull(),
admittedAtMs: integer('admitted_at_ms').notNull(),
receiptJson: text('receipt_json', { mode: 'json' })
.$type<Record<string, unknown>>()
.notNull(),
},
(table) => [
foreignKey({
columns: [table.planDigest, table.runId],
foreignColumns: [
pluginPackageWorkflowAdmissions.planDigest,
pluginPackageWorkflowAdmissions.runId,
],
name: 'ql3_plugin_package_workflow_task_attempt_admission_plan_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
columns: [table.runId, table.stepRunId],
foreignColumns: [stepRuns.runId, stepRuns.id],
name: 'ql3_plugin_package_workflow_task_attempt_admission_step_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
columns: [table.generationDigest, table.taskReconciliationReceiptDigest],
foreignColumns: [
pluginPackageTaskReconciliations.generationDigest,
pluginPackageTaskReconciliations.receiptDigest,
],
name: 'ql3_plugin_package_workflow_task_attempt_admission_reconciliation_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
columns: [table.projectId, table.taskId, table.taskRevision],
foreignColumns: [
localTaskExecutionRevisions.projectId,
localTaskExecutionRevisions.taskId,
localTaskExecutionRevisions.taskRevision,
],
name: 'ql3_plugin_package_workflow_task_attempt_admission_execution_fk',
})
.onDelete('restrict')
.onUpdate('restrict'),
uniqueIndex(
'ql3_plugin_package_workflow_task_attempt_admission_attempt_uidx',
).on(table.attemptId),
uniqueIndex(
'ql3_plugin_package_workflow_task_attempt_admission_event_uidx',
).on(table.eventId),
uniqueIndex(
'ql3_plugin_package_workflow_task_attempt_admission_epoch_uidx',
).on(table.runId, table.stepRunId, table.stepRunVersion),
uniqueIndex(
'ql3_plugin_package_workflow_task_attempt_admission_number_uidx',
).on(table.runId, table.attemptNumber),
index(
'ql3_plugin_package_workflow_task_attempt_admission_candidate_idx',
).on(table.runId, table.stepRunId, table.admittedAtMs),
check(
'ql3_plugin_package_workflow_task_attempt_admission_identity_check',
sql`length(${table.attemptId}) between 1 and 128 and length(${table.runId}) between 1 and 128 and length(${table.stepRunId}) between 1 and 128 and length(${table.resourceTaskId}) between 1 and 128 and length(${table.projectId}) between 1 and 128 and length(${table.taskId}) between 1 and 128 and length(${table.taskRevision}) between 1 and 128 and length(${table.eventId}) between 1 and 128 and ${table.executorType} = 'local_process'`,
),
check(
'ql3_plugin_package_workflow_task_attempt_admission_counter_check',
sql`${table.stepRunVersion} between 1 and 2147483647 and ${table.attemptNumber} between 1 and 8192 and ${table.runVersion} between 1 and 2147483647 and ${table.runEventSequence} = ${table.runVersion} and ${table.admittedAtMs} >= 0`,
),
check(
'ql3_plugin_package_workflow_task_attempt_admission_digest_check',
sql`length(${table.receiptDigest}) = 64 and ${table.receiptDigest} not glob '*[^0-9a-f]*' and length(${table.planDigest}) = 64 and ${table.planDigest} not glob '*[^0-9a-f]*' and length(${table.stepRunDigest}) = 64 and ${table.stepRunDigest} not glob '*[^0-9a-f]*' and length(${table.generationDigest}) = 64 and ${table.generationDigest} not glob '*[^0-9a-f]*' and length(${table.taskReconciliationReceiptDigest}) = 64 and ${table.taskReconciliationReceiptDigest} not glob '*[^0-9a-f]*' and length(${table.taskDefinitionDigest}) = 64 and ${table.taskDefinitionDigest} not glob '*[^0-9a-f]*' and length(${table.executionDigest}) = 64 and ${table.executionDigest} not glob '*[^0-9a-f]*'`,
),
check(
'ql3_plugin_package_workflow_task_attempt_admission_json_check',
sql`length(cast(${table.receiptJson} as blob)) between 2 and 16384 and json_valid(${table.receiptJson}) and json_type(${table.receiptJson}) = 'object' and json_extract(${table.receiptJson}, '$.schema') = 'qinglong/plugin-package-workflow-task-attempt-admission@v1' and json_extract(${table.receiptJson}, '$.receiptDigest') = ${table.receiptDigest} and json_extract(${table.receiptJson}, '$.attemptId') = ${table.attemptId} and json_extract(${table.receiptJson}, '$.planDigest') = ${table.planDigest} and json_extract(${table.receiptJson}, '$.runId') = ${table.runId} and json_extract(${table.receiptJson}, '$.stepRunId') = ${table.stepRunId} and json_extract(${table.receiptJson}, '$.stepRunVersion') = ${table.stepRunVersion} and json_extract(${table.receiptJson}, '$.stepRunDigest') = ${table.stepRunDigest} and json_extract(${table.receiptJson}, '$.resourceTaskId') = ${table.resourceTaskId} and json_extract(${table.receiptJson}, '$.taskReconciliationReceiptDigest') = ${table.taskReconciliationReceiptDigest} and json_extract(${table.receiptJson}, '$.taskId') = ${table.taskId} and json_extract(${table.receiptJson}, '$.taskRevision') = ${table.taskRevision} and json_extract(${table.receiptJson}, '$.taskDefinitionDigest') = ${table.taskDefinitionDigest} and json_extract(${table.receiptJson}, '$.executorType') = ${table.executorType} and json_extract(${table.receiptJson}, '$.executionDigest') = ${table.executionDigest} and json_extract(${table.receiptJson}, '$.attemptNumber') = ${table.attemptNumber} and json_extract(${table.receiptJson}, '$.eventId') = ${table.eventId} and json_extract(${table.receiptJson}, '$.runVersion') = ${table.runVersion} and json_extract(${table.receiptJson}, '$.runEventSequence') = ${table.runEventSequence} and json_extract(${table.receiptJson}, '$.admittedAtMs') = ${table.admittedAtMs}`,
),
],
);
export const localSqliteSchema = Object.freeze({
localSchemaMigrations,
localSchemaCapabilities,
runs,
stepRuns,
runAttempts,
runEvents,
stepRunMutations,
runRetryPolicies,
localCompletionReceiptJournal,
runAttemptLogArtifactTombstones,
runAttemptLogRetentionState,
localExecutionContextRecipes,
localTaskExecutionRevisions,
localSecretEnvelopes,
localProjects,
approvalRequests,
approvedActionDispatches,
approvedActionExecutions,
pluginPackageInstallProposals,
pluginPackageInstalls,
pluginPackageInstallHeads,
pluginPackageInstallMutations,
pluginPackageMaterializedRevisions,
pluginPackageQuarantineEvents,
pluginPackageWithdrawalReceipts,
pluginPackageWithdrawalTasks,
pluginPackageLifecycleEvents,
pluginPackageLifecycleHeads,
pluginPackageLifecycleReceipts,
pluginPackageLifecycleTasks,
pluginPackageAutomationPublications,
pluginPackageAutomationPublicationHeads,
pluginPackageWorkflowAdmissions,
pluginPackageWorkflowAdmissionSteps,
pluginPackageWorkflowTaskAttemptAdmissions,
projectToolDefinitionSnapshots,
projectToolDefinitionSnapshotSources,
pluginPackageTaskOwnerships,
pluginPackageTaskReconciliations,
pluginPackageTaskReconciliationItems,
pluginPackageAdmissionReceipts,
taskDefinitions,
taskDefinitionRevisions,
triggers,
triggerRevisions,
localTriggerSchedules,
localProjectRoleBindings,
localSecurityAuditEvents,
localProjectAdministrationMutations,
localSecurityAuditCompactions,
toolExecutionTraceAnchors,
toolExecutionAuditReceipts,
toolExecutionStartBarriers,
toolInvocationInputArtifacts,
toolInvocationPreviewArtifacts,
toolExecutionStartArtifactBindings,
toolExecutionCompletions,
toolExecutionFailureCompletions,
toolResultKeyCatalogGenerations,
toolExecutionResultKeyBindings,
toolExecutionResultRekeyOverlays,
toolExecutionResultRekeyHeads,
toolResultKeyRetirementReceipts,
legacyAdoptions,
localIdentitySubjects,
localApiCredentials,
localApiCredentialPepperBindings,
localIdentityAdministrationMutations,
localApiCredentialAdministrationMutations,
localApiCredentialDeliveryAcknowledgements,
localOwnerPepperKeys,
localOwnerPepperActivations,
localIdentityProvisionings,
localOwnerBootstrapChallenges,
localOwnerDeliveryAcknowledgements,
localOwnerDeliveryAcknowledgementGc,
localOwnerCredentialRecoveries,
localOwnerPepperMaterialGc,
});