openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,005 lines • 149 kB
JavaScript
import { d as asPositiveSafeInteger, f as asSafeIntegerInRange, s as asFiniteNumber } from "./number-coercion-CuVhdCBs.mjs";
import "./src-CdRlGRnW.mjs";
import { r as asNullableRecord } from "./record-coerce-TIsquoVR.mjs";
import { n as safeParseJsonRecord } from "./json-coercion-pfKbZnCJ.mjs";
import { a as getNodeSqliteKysely, c as prepareSqliteQuerySync, l as prepareSqliteQueryTakeFirstSync, n as createSqliteQueryCache, o as iterateSqliteQuerySync, r as executeSqliteQuerySync } from "./kysely-sync-K7ftY-g4.mjs";
import { t as SqliteSchemaMismatchError } from "./sqlite-schema-issues-Cd5MmsG2.mjs";
import { a as tableHasColumn, i as tableExists, n as ensureColumn } from "./openclaw-state-db-schema-helpers-BlZefrPy.mjs";
import { a as runSqliteImmediateTransactionSync } from "./sqlite-transaction-DsZ3X6nD.mjs";
import { t as coerceRequiredSqliteNumber } from "./sqlite-number-DZt1m75H.mjs";
import { t as buildApprovalResolutionRef } from "./approval-resolution-ref-DRRWcLCw.mjs";
//#region src/state/openclaw-state-db-additive-columns.ts
const lazyColumns = [
[
"user_profile_identities",
"authorization_id",
"TEXT"
],
[
"user_profile_identities",
"authorization_basis_json",
"TEXT"
],
[
"claw_installs",
"bootstrap_content_digest",
"TEXT"
],
[
"claw_installs",
"bootstrap_source_path",
"TEXT"
],
[
"worker_environments",
"desktop_json",
"TEXT"
],
[
"worker_environments",
"bootstrap_install_kind",
"TEXT"
],
[
"worker_environments",
"preparation_purpose",
"TEXT"
],
[
"claw_package_refs",
"extension_adapter_identity",
"TEXT"
],
[
"claw_package_refs",
"extension_detected_format",
"TEXT"
],
[
"claw_package_refs",
"extension_format",
"TEXT"
],
[
"claw_package_refs",
"extension_id",
"TEXT"
],
[
"claw_package_refs",
"extension_mapped_json",
"TEXT"
],
[
"claw_package_refs",
"extension_unavailable_json",
"TEXT"
],
[
"worker_environments",
"shared_host",
"INTEGER"
],
[
"worker_environments",
"node_setup_id",
"TEXT"
],
[
"worker_environments",
"node_device_id",
"TEXT"
],
[
"worker_session_placements",
"terminal_reason",
"TEXT"
],
[
"worker_session_placements",
"terminal_at_ms",
"INTEGER"
],
[
"worker_workspace_pending_results",
"repository_workspace_id",
"TEXT",
true
],
[
"worker_session_placement_moves",
"abandon_source",
"INTEGER",
true
],
[
"worker_session_placement_moves",
"target_machine_class",
"TEXT",
true
],
[
"worker_session_placement_moves",
"target_os",
"TEXT",
true
],
[
"worktrees",
"run_end_cleanup_json",
"TEXT"
],
[
"worktrees",
"gc_protection_json",
"TEXT"
],
[
"device_bootstrap_tokens",
"setup_id",
"TEXT",
true
],
[
"session_groups",
"cwd",
"TEXT",
true
],
[
"session_groups",
"worktree",
"INTEGER",
true
],
[
"secret_store_entries",
"allowed_hosts",
"TEXT"
],
[
"web_push_subscriptions",
"device_id",
"TEXT",
true
],
[
"web_push_subscriptions",
"user_profile_id",
"TEXT",
true
],
[
"web_push_subscriptions",
"preferences_json",
"TEXT",
true
],
[
"task_runs",
"execution_owner_host",
"TEXT",
true
],
[
"task_runs",
"execution_owner_pid",
"INTEGER",
true
],
[
"task_runs",
"execution_owner_start_identity",
"INTEGER",
true
],
[
"session_watch_cursors",
"watcher_store_path",
"TEXT",
true
],
[
"subagent_runs",
"requester_store_path",
"TEXT",
true
],
[
"subagent_runs",
"controller_store_path",
"TEXT",
true
],
[
"cron_jobs",
"grant_definition_revision",
"TEXT"
],
[
"cron_jobs",
"grant_definition_generation",
"INTEGER"
],
[
"cron_jobs",
"grant_definition_updated_at",
"INTEGER"
]
];
function lazyColumnDefinitions(firstUseOnly) {
return lazyColumns.filter((definition) => firstUseOnly === void 0 || Boolean(definition[3]) === firstUseOnly).map(([tableName, columnName, dataType]) => ({
columnName,
dataType,
tableName
}));
}
const CLAW_LAZY_ADDITIVE_STATE_COLUMN_DEFINITIONS = lazyColumnDefinitions();
const CLAW_STARTUP_ADDITIVE_STATE_COLUMN_DEFINITIONS = lazyColumnDefinitions(false);
const CLAW_FIRST_USE_ADDITIVE_STATE_COLUMN_DEFINITIONS = lazyColumnDefinitions(true);
const ORDERED_STARTUP_ADDITIVE_STATE_COLUMNS = {
packageUpdatedAt: [["claw_package_refs", "updated_at_ms INTEGER NOT NULL DEFAULT 0"]],
packageIntegrity: [["claw_package_refs", "package_integrity TEXT NOT NULL DEFAULT 'sha256:0000000000000000000000000000000000000000000000000000000000000000'"]],
diagnosticSequence: [["diagnostic_events", "sequence INTEGER NOT NULL DEFAULT 0"]],
cronRunLogs: [
["worktrees", "provisioned_paths_json TEXT"],
["apns_registrations", "relay_origin TEXT"],
["device_pairing_pending", "refreshed_at_ms INTEGER"],
["device_pairing_pending", "browser_origin TEXT"],
["device_pairing_paired", "approved_via TEXT"],
["device_pairing_paired", "browser_origin TEXT"],
["device_pairing_paired", "operator_label TEXT"],
["device_pairing_paired", "node_surface_json TEXT"],
["device_pairing_paired", "pending_node_surface_json TEXT"],
["cron_run_logs", "status TEXT"],
["cron_run_logs", "error TEXT"],
["cron_run_logs", "summary TEXT"],
["cron_run_logs", "diagnostics_summary TEXT"],
["cron_run_logs", "delivery_status TEXT"],
["cron_run_logs", "delivery_error TEXT"],
["cron_run_logs", "delivered INTEGER"],
["cron_run_logs", "session_id TEXT"],
["cron_run_logs", "session_key TEXT"],
["cron_run_logs", "run_id TEXT"],
["cron_run_logs", "run_at_ms INTEGER"],
["cron_run_logs", "duration_ms INTEGER"],
["cron_run_logs", "next_run_at_ms INTEGER"],
["cron_run_logs", "model TEXT"],
["cron_run_logs", "provider TEXT"],
["cron_run_logs", "total_tokens INTEGER"],
["cron_run_logs", "entry_json TEXT NOT NULL DEFAULT '{}'"],
["cron_run_logs", "created_at INTEGER NOT NULL DEFAULT 0"]
],
acpReplay: [["acp_replay_events", "estimated_bytes INTEGER NOT NULL DEFAULT 0"], ["acp_replay_sessions", "estimated_bytes INTEGER NOT NULL DEFAULT 0"]],
cronJobs: [
["cron_jobs", "description TEXT"],
["cron_jobs", "declaration_key TEXT"],
["cron_jobs", "owner_agent_id TEXT"],
["cron_jobs", "name TEXT NOT NULL DEFAULT ''"],
["cron_jobs", "enabled INTEGER NOT NULL DEFAULT 1"],
["cron_jobs", "agent_id TEXT"],
["cron_jobs", "payload_kind TEXT NOT NULL DEFAULT 'message'"],
["cron_jobs", "state_json TEXT NOT NULL DEFAULT '{}'"],
["cron_jobs", "runtime_updated_at_ms INTEGER"],
["cron_jobs", "schedule_identity TEXT"],
["cron_jobs", "sort_order INTEGER NOT NULL DEFAULT 0"]
],
deliveryQueue: [
["sandbox_registry_entries", "session_key TEXT"],
["sandbox_registry_entries", "backend_id TEXT"],
["sandbox_registry_entries", "runtime_label TEXT"],
["sandbox_registry_entries", "image TEXT"],
["sandbox_registry_entries", "created_at_ms INTEGER"],
["sandbox_registry_entries", "last_used_at_ms INTEGER"],
["sandbox_registry_entries", "config_label_kind TEXT"],
["sandbox_registry_entries", "config_hash TEXT"],
["sandbox_registry_entries", "cdp_port INTEGER"],
["sandbox_registry_entries", "no_vnc_port INTEGER"],
["delivery_queue_entries", "entry_kind TEXT"],
["delivery_queue_entries", "session_key TEXT"],
["delivery_queue_entries", "channel TEXT"],
["delivery_queue_entries", "target TEXT"],
["delivery_queue_entries", "account_id TEXT"],
["delivery_queue_entries", "retry_count INTEGER NOT NULL DEFAULT 0"],
["delivery_queue_entries", "last_attempt_at INTEGER"],
["delivery_queue_entries", "last_error TEXT"],
["delivery_queue_entries", "recovery_state TEXT"],
["delivery_queue_entries", "platform_send_started_at INTEGER"]
],
originalMediaRoot: [["managed_outgoing_image_records", "original_media_root TEXT NOT NULL DEFAULT ''"]],
beforeTaskAttribution: [
["managed_outgoing_image_records", "agent_id TEXT"],
["managed_outgoing_image_records", "cleanup_pending INTEGER NOT NULL DEFAULT 0 CHECK (cleanup_pending IN (0, 1))"],
["current_conversation_bindings", "conversation_kind TEXT NOT NULL DEFAULT 'channel'"],
["device_bootstrap_tokens", "pending_profile_json TEXT"],
["gateway_restart_handoff", "restart_trace_started_at INTEGER"],
["gateway_restart_handoff", "restart_trace_last_at INTEGER"],
["gateway_restart_intent", "reason TEXT"],
["gateway_restart_sentinel", "delivery_channel TEXT"],
["gateway_restart_sentinel", "delivery_to TEXT"],
["gateway_restart_sentinel", "delivery_account_id TEXT"],
["gateway_restart_sentinel", "message TEXT"],
["gateway_restart_sentinel", "continuation_json TEXT"],
["gateway_restart_sentinel", "doctor_hint TEXT"],
["gateway_restart_sentinel", "stats_json TEXT"],
["gateway_boot_lifecycle", "startup_reason TEXT"],
["official_external_plugin_catalog_snapshots", "trust_mode TEXT"],
["official_external_plugin_catalog_snapshots", "trust_key_id TEXT"],
["official_external_plugin_catalog_snapshots", "trust_signature_count INTEGER"],
["official_external_plugin_catalog_snapshots", "trust_threshold INTEGER"],
["official_external_plugin_catalog_snapshots", "trust_verified_at TEXT"]
],
taskRequester: [["task_runs", "requester_agent_id TEXT"]],
taskRunDetails: [
["task_runs", "tool_use_count INTEGER"],
["task_runs", "last_tool_name TEXT"],
["task_runs", "detail_json TEXT"]
],
workerEnvironments: [
["worker_environments", "bootstrap_bundle_hash TEXT"],
["worker_environments", "bootstrap_openclaw_version TEXT"],
["worker_environments", "bootstrap_protocol_features_json TEXT"],
["worker_environments", "bootstrap_install_kind TEXT"],
["worker_environments", "owner_epoch INTEGER NOT NULL DEFAULT 0 CHECK (owner_epoch >= 0)"],
["worker_environments", "ssh_host_key TEXT"],
["worker_workspace_pending_results", "staged_result_ref TEXT"],
["worker_environments", "teardown_terminal_state TEXT CHECK (teardown_terminal_state IN ('destroyed', 'failed'))"]
]
};
//#endregion
//#region src/acp/event-ledger-bytes.ts
/** Retained UTF-8 text footprint, including the existing fixed allowance per row. */
function estimateAcpSessionRowBytes(params) {
return Buffer.byteLength(params.sessionId, "utf8") + Buffer.byteLength(params.sessionKey, "utf8") + Buffer.byteLength(params.cwd, "utf8") + 32;
}
function estimateAcpEventRowBytes(params) {
return Buffer.byteLength(params.sessionId, "utf8") + Buffer.byteLength(params.sessionKey, "utf8") + Buffer.byteLength(params.runId ?? "", "utf8") + Buffer.byteLength(params.updateJson, "utf8") + 32;
}
//#endregion
//#region src/infra/delivery-queue-sqlite-bound.ts
const COMPLETED_TOMBSTONE_RETENTION_MS = 2592e6;
const BOUNDED_DELIVERY_RECEIPTS_SQL = `
SELECT * FROM (
SELECT rowid receipt_rowid, queue_name, id, enqueued_at,
json_extract(entry_json, '$.completionRetention.idPrefix') id_prefix,
json_extract(entry_json, '$.completionRetention.maxAgeMs') max_age_ms,
json_extract(entry_json, '$.completionRetention.maxEntries') max_entries
FROM delivery_queue_entries WHERE status IN ('completed', 'failed')
AND recovery_state = 'completed_bounded' AND json_valid(entry_json)
AND json_type(entry_json, '$.completionRetention') = 'object'
)
WHERE typeof(id_prefix) = 'text' AND id_prefix <> ''
AND substr(id, 1, length(id_prefix)) = id_prefix
AND typeof(max_age_ms) = 'integer' AND max_age_ms BETWEEN 1 AND 9007199254740991
AND typeof(max_entries) = 'integer' AND max_entries BETWEEN 1 AND 9007199254740991`;
const deliveryQueueRowColumns = [
"id",
"entry_json",
"enqueued_at",
"retry_count",
"last_attempt_at",
"last_error",
"platform_send_started_at",
"recovery_state"
];
/** Prunes bounded receipts globally or for one exact producer namespace. */
function pruneDeliveryQueueTombstones(db, now, prefix) {
const result = db.prepare(`WITH policies AS (
${BOUNDED_DELIVERY_RECEIPTS_SQL}
${prefix ? "AND queue_name = @queueName AND id_prefix = @idPrefix" : ""}
), ranked AS (
SELECT *, row_number() OVER (PARTITION BY queue_name, id_prefix
ORDER BY enqueued_at DESC, id DESC) retention_rank FROM policies
) DELETE FROM delivery_queue_entries WHERE rowid IN (
SELECT receipt_rowid FROM ranked
WHERE enqueued_at < @now - max_age_ms OR retention_rank > max_entries
)`).run(prefix ? {
now,
...prefix
} : { now });
const ordinaryPruned = prefix ? false : pruneOrdinaryDeliveryReceipts(db, now);
return result.changes > 0 || ordinaryPruned;
}
/** Cheap maintenance cleanup: age predicates only, with no window sort. */
function pruneDeliveryQueueTombstoneAges(db, now) {
db.prepare(`DELETE FROM delivery_queue_entries WHERE rowid IN (
SELECT receipt_rowid FROM (${BOUNDED_DELIVERY_RECEIPTS_SQL})
WHERE enqueued_at < @now - max_age_ms)`).run({ now });
pruneOrdinaryDeliveryReceipts(db, now);
}
/** CAS-compacts one exact row, or deletes it when no fence is authored. */
function terminalizeBoundDeliveryQueueEntry(db, queueName, id, expectedJson, failedEntry, now, expectedStatus = "pending") {
const queueDb = getNodeSqliteKysely(db);
const expected = {
queue_name: queueName,
id,
status: expectedStatus,
entry_json: expectedJson
};
const query = failedEntry ? queueDb.updateTable("delivery_queue_entries").where((eb) => eb.and(expected)).set({
status: "failed",
entry_kind: null,
session_key: null,
channel: null,
target: null,
account_id: null,
last_attempt_at: null,
last_error: null,
platform_send_started_at: null,
recovery_state: failedEntry.recoveryState ?? null,
entry_json: JSON.stringify(failedEntry),
enqueued_at: now,
updated_at: now,
failed_at: now
}) : queueDb.deleteFrom("delivery_queue_entries").where((eb) => eb.and(expected));
return executeSqliteQuerySync(db, query).numAffectedRows === 1n;
}
function pruneOrdinaryDeliveryReceipts(db, now) {
return (executeSqliteQuerySync(db, getNodeSqliteKysely(db).deleteFrom("delivery_queue_entries").where("status", "=", "completed").where("enqueued_at", "<", now - COMPLETED_TOMBSTONE_RETENTION_MS).where((eb) => eb.or([eb("recovery_state", "is", null), eb("recovery_state", "not in", ["completed_permanent", "completed_bounded"])]))).numAffectedRows ?? 0n) > 0n;
}
function inflateDeliveryQueueRow(row) {
let parsed;
try {
parsed = JSON.parse(row.entry_json);
} catch {
return null;
}
return {
...parsed,
id: row.id,
enqueuedAt: coerceRequiredSqliteNumber(row.enqueued_at),
retryCount: coerceRequiredSqliteNumber(row.retry_count),
...row.last_attempt_at == null ? {} : { lastAttemptAt: coerceRequiredSqliteNumber(row.last_attempt_at) },
...row.last_error == null ? {} : { lastError: row.last_error },
...row.platform_send_started_at == null ? {} : { platformSendStartedAt: coerceRequiredSqliteNumber(row.platform_send_started_at) },
...row.recovery_state == null ? {} : { recoveryState: row.recovery_state }
};
}
function deliveryQueueMetadata(queueName, entry) {
const item = entry;
return {
entryKind: item.kind ?? queueName,
sessionKey: item.sessionKey ?? item.session?.key,
channel: item.channel ?? item.route?.channel ?? item.deliveryContext?.channel,
target: item.to ?? item.route?.to ?? item.deliveryContext?.to,
accountId: item.accountId ?? item.route?.accountId ?? item.deliveryContext?.accountId
};
}
/** Canonically serializes a queue row before a transaction acquires the write lock. */
function bindDeliveryQueueEntry(params, now = Date.now()) {
const status = params.status ?? "pending";
const meta = params.metadata ?? deliveryQueueMetadata(params.queueName, params.entry);
return {
mode: params.insertOnly === true ? "insert" : params.updatePendingOnly === true ? "pending" : params.completeExisting === true ? "complete" : "replace",
row: {
queue_name: params.queueName,
id: params.entry.id,
status,
entry_kind: meta.entryKind ?? null,
session_key: meta.sessionKey ?? null,
channel: meta.channel ?? null,
target: meta.target ?? null,
account_id: meta.accountId ?? null,
retry_count: params.entry.retryCount,
last_attempt_at: params.entry.lastAttemptAt ?? null,
last_error: params.entry.lastError ?? null,
recovery_state: params.entry.recoveryState ?? null,
platform_send_started_at: params.entry.platformSendStartedAt ?? null,
entry_json: JSON.stringify(params.entry),
enqueued_at: params.entry.enqueuedAt,
updated_at: now,
failed_at: status === "failed" ? now : null
}
};
}
function createDeliveryQueueUpsert(database, mode) {
const queueDb = getNodeSqliteKysely(database);
return prepareSqliteQuerySync(database, (parameter) => {
const values = {
status: parameter((row) => row.status),
entry_kind: parameter((row) => row.entry_kind),
session_key: parameter((row) => row.session_key),
channel: parameter((row) => row.channel),
target: parameter((row) => row.target),
account_id: parameter((row) => row.account_id),
retry_count: parameter((row) => row.retry_count),
last_attempt_at: parameter((row) => row.last_attempt_at),
last_error: parameter((row) => row.last_error),
recovery_state: parameter((row) => row.recovery_state),
platform_send_started_at: parameter((row) => row.platform_send_started_at),
entry_json: parameter((row) => row.entry_json),
enqueued_at: parameter((row) => row.enqueued_at),
updated_at: parameter((row) => row.updated_at),
failed_at: parameter((row) => row.failed_at)
};
const insert = queueDb.insertInto("delivery_queue_entries").values({
queue_name: parameter((row) => row.queue_name),
id: parameter((row) => row.id),
...values
});
return mode === "insert" ? insert.onConflict((conflict) => conflict.columns(["queue_name", "id"]).doNothing()) : insert.onConflict((conflict) => {
const update = conflict.columns(["queue_name", "id"]).doUpdateSet(values);
if (mode === "pending") return update.where("delivery_queue_entries.status", "=", "pending");
return mode === "complete" ? update.where("delivery_queue_entries.status", "in", ["pending", "failed"]) : update;
});
});
}
const deliveryQueueUpserts = createSqliteQueryCache(() => ({}));
/** Mutates only the exact supplied shared-state handle; never opens or hardens a file. */
function upsertBoundDeliveryQueueEntryInDatabase(bound, database) {
const queries = deliveryQueueUpserts(database.db);
return (queries[bound.mode] ??= createDeliveryQueueUpsert(database.db, bound.mode))(bound.row).numAffectedRows === 1n;
}
/** Recovery and media custody share the same inventory of unfinished work. */
function deliveryQueueEntriesQuery(database, queueNames, mode) {
const query = getNodeSqliteKysely(database.db).selectFrom("delivery_queue_entries").select(deliveryQueueRowColumns).where("queue_name", "in", queueNames);
return mode === "all" ? query : query.where((eb) => mode === "pending" ? eb("status", "=", "pending") : eb.or([eb("status", "=", "pending"), eb.and([eb("status", "=", "failed"), eb("recovery_state", "=", "settlement_pending")])]));
}
function createDeliveryQueueRead(database, mode) {
return prepareSqliteQueryTakeFirstSync(database.db, (parameter) => deliveryQueueEntriesQuery(database, [parameter((params) => params.queueName)], mode).where("id", "=", parameter((params) => params.id)));
}
const deliveryQueueReads = createSqliteQueryCache(() => ({}));
/** Reads one row from the exact supplied handle for cross-owner invariant validation. */
function loadDeliveryQueueEntryInDatabase(database, queueName, id, mode = "all") {
const queries = deliveryQueueReads(database.db);
const readMode = mode === "all" || mode === "pending" ? mode : "unfinished";
const row = (queries[readMode] ??= createDeliveryQueueRead(database, readMode))({
queueName,
id
});
return row ? inflateDeliveryQueueRow(row) : null;
}
//#endregion
//#region src/infra/delivery-queue-sqlite.types.ts
/** Parse only the shipped completion-retention shape for one exact producer ID. */
function parseDeliveryQueueCompletionRetention(value, id) {
if (value === "permanent") return value;
const retention = asNullableRecord(value);
if (!retention) return;
const idPrefix = typeof retention.idPrefix === "string" ? retention.idPrefix : "";
const maxAgeMs = asPositiveSafeInteger(retention.maxAgeMs);
const maxEntries = asPositiveSafeInteger(retention.maxEntries);
if (!idPrefix || !id.startsWith(idPrefix) || maxAgeMs === void 0 || maxEntries === void 0) return;
return {
idPrefix,
maxAgeMs,
maxEntries
};
}
const finite = (value) => typeof value === "number" && Number.isFinite(value);
/** Recover only authored or shipped producer ownership from a failed entry. */
function inferDeliveryQueueFailureRetention(entry, id, queueName, legacyAmbiguousSendEvidence = false) {
const explicit = parseDeliveryQueueCompletionRetention(entry.completionRetention, id) ?? parseDeliveryQueueCompletionRetention(entry.failureRetention, id);
if (explicit) return explicit;
const fence = asNullableRecord(asNullableRecord(entry.terminalPolicy)?.fence);
if (fence?.kind === "none") return;
const fenced = fence?.kind === "permanent" ? "permanent" : parseDeliveryQueueCompletionRetention(fence, id);
if (fenced) return fenced;
const durable = queueName === "outbound-preparing-v1" || queueName === "outbound-legacy-preparing-v1" || queueName === "outbound-prepared-migration-v1" || entry.retainOnFailure === true || asNullableRecord(entry.deliveryCompletion) !== null || queueName === "session" && finite(entry.availableAt);
const ambiguous = legacyAmbiguousSendEvidence && (typeof entry.platformSendAttemptId === "string" && entry.platformSendAttemptId.length > 0 || finite(entry.platformSendStartedAt) || entry.recoveryState === "send_attempt_started" || entry.recoveryState === "unknown_after_send" || queueName === "session" && (finite(entry.deliveryStartedAt) || typeof entry.settlementOutcome === "string" && entry.settlementOutcome.length > 0 || finite(entry.acknowledgedAt)));
return durable || ambiguous ? "permanent" : void 0;
}
/** Additional work needs a live claim; settling an observed outcome only needs exact ownership. */
function hasLiveDeliveryQueueClaim(entry, claimId, now) {
const unexpired = typeof entry.availableAt === "number" && entry.availableAt > now;
return entry.recoveryState === "producer_claimed" ? entry.producerClaimId === claimId && unexpired : (entry.recoveryState === "send_attempt_started" || entry.recoveryState === "unknown_after_send") && entry.platformSendAttemptId === claimId && (entry.requiresProducerClaim !== true || unexpired);
}
/** Strip a terminal queue row to the producer policy needed for admission. */
function projectDeliveryQueueTerminalEntry(entry, terminalAt, terminal, completionRetention) {
const retryCount = Number.isSafeInteger(entry.retryCount) && entry.retryCount >= 0 ? entry.retryCount : 0;
const recoveryState = completionRetention === "permanent" ? "completed_permanent" : completionRetention ? "completed_bounded" : void 0;
return {
id: entry.id,
enqueuedAt: terminalAt,
retryCount,
...terminal === "completed" ? { acknowledgedAt: terminalAt } : { failedAt: terminalAt },
...completionRetention ? { completionRetention } : {},
...recoveryState ? { recoveryState } : {}
};
}
//#endregion
//#region src/state/openclaw-state-db-delivery-queue-backfill.ts
function nonNegativeSafeInteger(value) {
const number = typeof value === "bigint" ? Number(value) : value;
return typeof number === "number" && Number.isSafeInteger(number) && number >= 0 ? number : void 0;
}
/** Compact every preexisting failed row without inferring replay or owner policy. */
function compactLegacyDeliveryQueueFailures(db) {
const migrationNow = Date.now();
const retainPending = db.prepare(`UPDATE delivery_queue_entries SET entry_json = ?
WHERE queue_name = ? AND id = ? AND status = 'pending' AND entry_json = ?`);
const select = db.prepare(`SELECT queue_name, id, status, retry_count, entry_json, updated_at, failed_at, recovery_state
FROM delivery_queue_entries WHERE status IN ('pending', 'failed')`);
select.setReadBigInts(true);
const rows = select.all();
const remove = db.prepare(`DELETE FROM delivery_queue_entries WHERE queue_name = ? AND id = ? AND status = 'failed'`);
const compact = db.prepare(`UPDATE delivery_queue_entries
SET entry_kind = NULL, session_key = NULL, channel = NULL, target = NULL,
account_id = NULL, retry_count = @retryCount, last_attempt_at = NULL,
last_error = NULL, platform_send_started_at = NULL, entry_json = @entryJson,
enqueued_at = @failedAt, failed_at = @failedAt, recovery_state = @recoveryState
WHERE queue_name = @queueName AND id = @id AND status = 'failed'`);
for (const row of rows) {
if (row.recovery_state === "settlement_pending") continue;
const parsedEntry = safeParseJsonRecord(String(row.entry_json));
const entry = parsedEntry ?? {};
const queueName = String(row.queue_name);
const id = String(row.id);
if (row.status === "pending") {
if (entry.retainOnFailure !== true && inferDeliveryQueueFailureRetention(entry, id, queueName, true)) retainPending.run(JSON.stringify({
...entry,
retainOnFailure: true
}), queueName, id, String(row.entry_json));
continue;
}
const failedAt = nonNegativeSafeInteger(row.failed_at) ?? nonNegativeSafeInteger(row.updated_at) ?? migrationNow;
const retryCount = Math.max(nonNegativeSafeInteger(row.retry_count) ?? 0, nonNegativeSafeInteger(entry.retryCount) ?? 0);
const retention = parsedEntry ? inferDeliveryQueueFailureRetention(entry, id, queueName, true) : "permanent";
if (!retention) {
remove.run(queueName, id);
continue;
}
const failedEntry = projectDeliveryQueueTerminalEntry({
id,
retryCount
}, failedAt, "failed", retention);
compact.run({
retryCount,
entryJson: JSON.stringify(failedEntry),
failedAt,
recoveryState: failedEntry.recoveryState ?? null,
queueName,
id
});
}
pruneDeliveryQueueTombstones(db, migrationNow);
}
//#endregion
//#region src/state/openclaw-state-schema.ts
const OPENCLAW_STATE_SCHEMA_SQL = "\n\nCREATE TABLE IF NOT EXISTS mcp_oauth_stores (\n store_key TEXT NOT NULL PRIMARY KEY,\n format_version INTEGER NOT NULL CHECK (format_version = 1),\n store_json TEXT NOT NULL,\n updated_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS mcp_oauth_pending_authorizations (\n state TEXT NOT NULL PRIMARY KEY,\n store_key TEXT NOT NULL,\n create_time INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS diagnostic_events (\n scope TEXT NOT NULL,\n event_key TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n sequence INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (scope, event_key)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_diagnostic_events_scope_sequence\n ON diagnostic_events(scope, sequence, event_key);\n\nCREATE TABLE IF NOT EXISTS skill_usage (\n skill_file TEXT NOT NULL PRIMARY KEY,\n skill_key TEXT NOT NULL,\n skill_name TEXT NOT NULL,\n skill_source TEXT NOT NULL,\n first_used_at_ms INTEGER NOT NULL,\n last_used_at_ms INTEGER NOT NULL,\n use_count INTEGER NOT NULL,\n last_agent_id TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_skill_usage_key\n ON skill_usage(skill_key, skill_file);\n\n-- Profile-owned skill library: additive, absent until first publication/import.\nCREATE TABLE IF NOT EXISTS skill_library_entries (\n skill_id TEXT NOT NULL PRIMARY KEY,\n owner_profile_id TEXT,\n author_profile_id TEXT NOT NULL,\n slug TEXT NOT NULL,\n current_revision TEXT NOT NULL,\n shared INT NOT NULL,\n enabled INT NOT NULL,\n removed INT NOT NULL,\n created_at INT NOT NULL,\n updated_at INT NOT NULL\n) STRICT;\nCREATE TABLE IF NOT EXISTS skill_library_revisions (\n skill_id TEXT NOT NULL,\n revision TEXT NOT NULL,\n description TEXT NOT NULL,\n files_json TEXT NOT NULL,\n created_at INT NOT NULL,\n PRIMARY KEY (skill_id, revision)\n) STRICT;\nCREATE TABLE IF NOT EXISTS skill_library_events (\n event_id TEXT NOT NULL PRIMARY KEY,\n skill_id TEXT NOT NULL,\n revision TEXT NOT NULL,\n action TEXT NOT NULL,\n actor_profile_id TEXT NOT NULL,\n created_at INT NOT NULL\n) STRICT;\nCREATE TABLE IF NOT EXISTS skill_library_uploads (\n upload_id TEXT NOT NULL PRIMARY KEY,\n owner_profile_id TEXT NOT NULL,\n slug TEXT NOT NULL,\n size_bytes INT NOT NULL,\n sha256 TEXT NOT NULL,\n archive_blob BLOB NOT NULL,\n expires_at INT NOT NULL,\n published_skill_id TEXT\n) STRICT;\n-- End profile-owned skill library.\n\nCREATE TABLE IF NOT EXISTS skill_workshop_proposals (\n proposal_id TEXT NOT NULL PRIMARY KEY,\n record_json TEXT NOT NULL,\n owner_agent_id TEXT,\n kind TEXT NOT NULL CHECK (kind IN ('create', 'update')),\n status TEXT NOT NULL CHECK (status IN ('pending', 'applied', 'rejected', 'quarantined', 'stale')),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n draft_hash TEXT NOT NULL,\n origin_agent_id TEXT,\n origin_session_key TEXT,\n origin_run_id TEXT,\n origin_message_id TEXT,\n applied_at TEXT,\n rejected_at TEXT,\n quarantined_at TEXT,\n stale_at TEXT,\n status_reason TEXT\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS skill_workshop_collection_reviews (\n review_id TEXT NOT NULL PRIMARY KEY,\n owner_agent_id TEXT NOT NULL,\n backup_id TEXT NOT NULL,\n create_time INTEGER NOT NULL,\n kept_names_json TEXT NOT NULL,\n written_names_json TEXT NOT NULL,\n dropped_json TEXT NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_skill_workshop_collection_reviews_owner_time\n ON skill_workshop_collection_reviews(owner_agent_id, create_time DESC, review_id);\n\nCREATE TABLE IF NOT EXISTS skill_workshop_proposal_rollbacks (\n proposal_id TEXT NOT NULL PRIMARY KEY,\n written_at TEXT NOT NULL,\n target_skill_file TEXT NOT NULL,\n action TEXT NOT NULL CHECK (action IN ('create', 'update')),\n previous_content_hash TEXT,\n previous_content TEXT,\n support_files_json TEXT,\n FOREIGN KEY (proposal_id) REFERENCES skill_workshop_proposals(proposal_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS skill_workshop_proposal_events (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n proposal_id TEXT NOT NULL,\n proposed_version TEXT NOT NULL,\n revision_hash TEXT NOT NULL,\n event_type TEXT NOT NULL CHECK (event_type IN (\n 'created',\n 'revised',\n 'evaluation_completed',\n 'applied',\n 'rejected',\n 'quarantined',\n 'stale'\n )),\n occurred_at TEXT NOT NULL,\n actor_json TEXT NOT NULL,\n correlation_id TEXT,\n payload_json TEXT,\n FOREIGN KEY (proposal_id) REFERENCES skill_workshop_proposals(proposal_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS audit_events (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n source_id TEXT NOT NULL UNIQUE,\n schema_version INTEGER NOT NULL DEFAULT 1,\n source_sequence INTEGER NOT NULL,\n occurred_at INTEGER NOT NULL,\n kind TEXT NOT NULL,\n action TEXT NOT NULL,\n status TEXT NOT NULL,\n error_code TEXT,\n actor_type TEXT NOT NULL,\n actor_id TEXT NOT NULL,\n agent_id TEXT,\n session_key TEXT,\n session_id TEXT,\n run_id TEXT,\n tool_call_id TEXT,\n tool_name TEXT,\n direction TEXT,\n channel TEXT,\n conversation_kind TEXT,\n message_outcome TEXT,\n reason_code TEXT,\n delivery_kind TEXT,\n failure_stage TEXT,\n duration_ms INTEGER,\n result_count INTEGER,\n account_ref TEXT,\n conversation_ref TEXT,\n message_ref TEXT,\n target_ref TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_time\n ON audit_events(occurred_at DESC, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_agent_sequence\n ON audit_events(agent_id, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_session_sequence\n ON audit_events(session_key, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_run_sequence\n ON audit_events(run_id, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_kind_sequence\n ON audit_events(kind, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_status_sequence\n ON audit_events(status, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_channel_sequence\n ON audit_events(channel, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_audit_events_direction_sequence\n ON audit_events(direction, sequence DESC);\n\nCREATE TABLE IF NOT EXISTS outbound_message_execution_bindings (\n event_id TEXT NOT NULL PRIMARY KEY,\n context_id TEXT NOT NULL CHECK (length(context_id) BETWEEN 1 AND 256),\n execution_id TEXT NOT NULL CHECK (length(execution_id) BETWEEN 1 AND 256),\n run_id TEXT NOT NULL CHECK (length(run_id) BETWEEN 1 AND 256),\n FOREIGN KEY (event_id) REFERENCES audit_events(event_id) ON DELETE CASCADE\n) STRICT;\nCREATE INDEX IF NOT EXISTS outbound_message_execution_bindings_execution_event_idx\n ON outbound_message_execution_bindings (context_id, execution_id, run_id, event_id);\n\nCREATE TABLE IF NOT EXISTS outbound_message_progress (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n progress_id TEXT NOT NULL UNIQUE CHECK (length(progress_id) BETWEEN 1 AND 256),\n source_id TEXT NOT NULL UNIQUE CHECK (length(source_id) BETWEEN 1 AND 512),\n source_sequence INTEGER NOT NULL CHECK (source_sequence >= 1),\n schema_version INTEGER NOT NULL CHECK (schema_version = 1),\n occurred_at INTEGER NOT NULL CHECK (occurred_at >= 0),\n action TEXT NOT NULL CHECK (\n action IN ('message.outbound.queued', 'message.outbound.platform-started')\n ),\n outcome TEXT NOT NULL CHECK (outcome IN ('queued', 'platform_started')),\n actor_type TEXT NOT NULL CHECK (actor_type IN ('agent', 'system')),\n actor_id TEXT NOT NULL CHECK (length(actor_id) BETWEEN 1 AND 256),\n agent_id TEXT CHECK (agent_id IS NULL OR length(agent_id) BETWEEN 1 AND 256),\n run_id TEXT CHECK (run_id IS NULL OR length(run_id) BETWEEN 1 AND 256),\n context_id TEXT,\n execution_id TEXT,\n channel TEXT NOT NULL CHECK (length(channel) BETWEEN 1 AND 256),\n conversation_kind TEXT NOT NULL CHECK (\n conversation_kind IN ('direct', 'group', 'channel', 'unknown')\n ),\n duration_ms INTEGER CHECK (duration_ms IS NULL OR duration_ms >= 0),\n account_ref TEXT,\n conversation_ref TEXT,\n target_ref TEXT,\n UNIQUE (occurred_at, progress_id)\n) STRICT;\nCREATE INDEX IF NOT EXISTS outbound_message_progress_occurred_idx\n ON outbound_message_progress (occurred_at, sequence);\nCREATE INDEX IF NOT EXISTS outbound_message_progress_run_occurred_idx\n ON outbound_message_progress (run_id, occurred_at, sequence);\n\nCREATE TABLE IF NOT EXISTS audit_identity_keys (\n id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1),\n key_id TEXT NOT NULL,\n key BLOB NOT NULL,\n created_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS config_revision_keys (\n id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1),\n hmac_key BLOB NOT NULL CHECK (length(hmac_key) = 32)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS execution_identity_contexts (\n context_id TEXT NOT NULL PRIMARY KEY CHECK (length(context_id) BETWEEN 1 AND 256),\n execution_id TEXT NOT NULL UNIQUE CHECK (length(execution_id) BETWEEN 1 AND 256),\n run_id TEXT NOT NULL CHECK (length(run_id) BETWEEN 1 AND 256),\n created_at INTEGER NOT NULL CHECK (created_at >= 0),\n coverage_state TEXT NOT NULL CHECK (\n coverage_state IN ('attribution-only', 'unattributed', 'unknown', 'unsupported')\n ),\n context_bytes INTEGER NOT NULL CHECK (context_bytes BETWEEN 1 AND 16384),\n context_json TEXT NOT NULL CHECK (length(context_json) > 0),\n UNIQUE (created_at, context_id)\n) STRICT;\nCREATE INDEX IF NOT EXISTS execution_identity_contexts_run_created_idx\n ON execution_identity_contexts (run_id, created_at, execution_id);\n\nCREATE TABLE IF NOT EXISTS execution_decision_facts (\n receipt_id TEXT NOT NULL PRIMARY KEY CHECK (length(receipt_id) BETWEEN 1 AND 256),\n context_id TEXT NOT NULL CHECK (length(context_id) BETWEEN 1 AND 256),\n execution_id TEXT NOT NULL CHECK (length(execution_id) BETWEEN 1 AND 256),\n run_id TEXT NOT NULL CHECK (length(run_id) BETWEEN 1 AND 256),\n action_id TEXT CHECK (action_id IS NULL OR length(action_id) BETWEEN 1 AND 256),\n action_family TEXT NOT NULL CHECK (length(action_family) BETWEEN 1 AND 256),\n decision_outcome TEXT NOT NULL CHECK (\n decision_outcome IN ('allowed', 'denied', 'not-applicable', 'unknown')\n ),\n coverage_state TEXT NOT NULL CHECK (\n coverage_state IN ('enforced', 'attribution-only', 'unattributed', 'unknown', 'unsupported')\n ),\n reason_code TEXT NOT NULL CHECK (length(reason_code) BETWEEN 1 AND 256),\n owner TEXT NOT NULL CHECK (length(owner) BETWEEN 1 AND 256),\n source_ref TEXT NOT NULL CHECK (length(source_ref) BETWEEN 1 AND 256),\n occurred_at INTEGER NOT NULL CHECK (occurred_at >= 0),\n receipt_bytes INTEGER NOT NULL CHECK (receipt_bytes BETWEEN 1 AND 16384),\n receipt_json TEXT NOT NULL CHECK (length(receipt_json) > 0),\n UNIQUE (occurred_at, receipt_id)\n) STRICT;\nCREATE INDEX IF NOT EXISTS execution_decision_facts_context_occurred_idx\n ON execution_decision_facts (context_id, occurred_at, receipt_id);\nCREATE INDEX IF NOT EXISTS execution_decision_facts_run_occurred_idx\n ON execution_decision_facts (run_id, occurred_at, receipt_id);\n\n-- Exact admission identity stays separate from owner-native lifecycle rows so\n-- older readers retain byte-compatible cron/task/flow table definitions.\nCREATE TABLE IF NOT EXISTS execution_owner_lifecycle_bindings (\n owner_kind TEXT NOT NULL,\n owner_id TEXT NOT NULL,\n context_id TEXT NOT NULL,\n execution_id TEXT NOT NULL,\n PRIMARY KEY (owner_kind, owner_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS session_state_events (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n dedupe_key TEXT UNIQUE,\n session_key TEXT NOT NULL,\n session_id TEXT,\n agent_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n actor_type TEXT NOT NULL,\n actor_id TEXT,\n run_id TEXT,\n occurred_at INTEGER NOT NULL,\n summary TEXT NOT NULL,\n payload_json TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_session_state_events_session_sequence\n ON session_state_events(session_key, sequence DESC);\n\nCREATE INDEX IF NOT EXISTS idx_session_state_events_time\n ON session_state_events(occurred_at DESC, sequence DESC);\n\nCREATE TABLE IF NOT EXISTS session_state_heads (\n session_key TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n last_sequence INTEGER NOT NULL,\n pruned_max_sequence INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (session_key, agent_id)\n) STRICT;\n\n-- Notifiable watcher identity is the bare session key, matching the process-local\n-- system-event queue it feeds. Provenance distinguishes explicit immediate-wake\n-- watches from ambient queue-only group watches. Other bare keys\n-- (session.scope=\"global\") are ambiguous across agents and excluded until watcher\n-- identity is agent-scoped end-to-end.\nCREATE TABLE IF NOT EXISTS session_watch_cursors (\n watcher_session_key TEXT NOT NULL,\n watcher_store_path TEXT,\n target_session_key TEXT NOT NULL,\n last_seen_sequence INTEGER NOT NULL DEFAULT 0,\n notified_sequence INTEGER NOT NULL DEFAULT 0,\n material_sequence INTEGER NOT NULL DEFAULT 0,\n provenance TEXT NOT NULL DEFAULT 'explicit' CHECK (provenance IN ('explicit', 'ambient-group')),\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (watcher_session_key, target_session_key)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_session_watch_cursors_target\n ON session_watch_cursors(target_session_key);\n\nCREATE TABLE IF NOT EXISTS session_upstream_links (\n session_key TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n catalog_id TEXT NOT NULL,\n host_id TEXT NOT NULL,\n thread_id TEXT NOT NULL,\n upstream_kind TEXT NOT NULL,\n upstream_ref_json TEXT,\n last_marker_json TEXT,\n last_scanned_at INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n -- (session_key, agent_id) composite identity: under session.scope=\"global\" agents\n -- share bare keys; a key-only row would let one agent overwrite another's upstream.\n PRIMARY KEY (session_key, agent_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_session_upstream_links_catalog_id\n ON session_upstream_links(catalog_id);\n\nCREATE TABLE IF NOT EXISTS state_leases (\n scope TEXT NOT NULL,\n lease_key TEXT NOT NULL,\n owner TEXT NOT NULL,\n expires_at INTEGER,\n heartbeat_at INTEGER,\n payload_json TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, lease_key)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_state_leases_expiry\n ON state_leases(expires_at, scope, lease_key)\n WHERE expires_at IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_state_leases_owner\n ON state_leases(owner, updated_at DESC);\n\nCREATE TABLE IF NOT EXISTS exec_approvals_config (\n config_key TEXT NOT NULL PRIMARY KEY,\n raw_json TEXT NOT NULL,\n socket_path TEXT,\n has_socket_token INTEGER NOT NULL,\n default_security TEXT,\n default_ask TEXT,\n default_ask_fallback TEXT,\n auto_allow_skills INTEGER,\n agent_count INTEGER NOT NULL,\n allowlist_count INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS operator_approvals (\n approval_id TEXT NOT NULL PRIMARY KEY CHECK (\n length(approval_id) > 0 AND approval_id NOT IN ('.', '..')\n ),\n resolution_ref TEXT NOT NULL CHECK (\n length(resolution_ref) = 43 AND resolution_ref NOT GLOB '*[^A-Za-z0-9_-]*'\n ),\n kind TEXT NOT NULL CHECK (kind IN ('exec', 'plugin', 'system-agent')),\n status TEXT NOT NULL CHECK (status IN ('pending', 'allowed', 'denied', 'expired', 'cancelled')),\n presentation_json TEXT NOT NULL,\n requested_by_device_id TEXT,\n requested_by_client_id TEXT,\n requested_by_device_token_auth INTEGER NOT NULL DEFAULT 0,\n reviewer_device_ids_json TEXT NOT NULL,\n source_agent_id TEXT,\n source_session_key TEXT,\n source_session_id TEXT,\n source_run_id TEXT,\n source_tool_call_id TEXT,\n source_tool_name TEXT,\n audience_session_keys_json TEXT NOT NULL,\n runtime_epoch TEXT NOT NULL,\n created_at_ms INTEGER NOT NULL,\n expires_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n decision TEXT CHECK (decision IN ('allow-once', 'allow-always', 'deny')),\n terminal_reason TEXT CHECK (\n terminal_reason IN (\n 'user',\n 'timeout',\n 'malformed-verdict',\n 'no-route',\n 'run-aborted',\n 'gateway-restart',\n 'storage-corrupt'\n )\n ),\n resolved_at_ms INTEGER,\n resolver_kind TEXT CHECK (resolver_kind IN ('device', 'channel', 'runtime', 'system')),\n resolver_id TEXT,\n consumed_at_ms INTEGER,\n consumed_by TEXT,\n CHECK (expires_at_ms >= created_at_ms),\n CHECK (updated_at_ms >= created_at_ms),\n CHECK (resolved_at_ms IS NULL OR resolved_at_ms >= created_at_ms),\n CHECK (resolved_at_ms IS NULL OR resolved_at_ms <= updated_at_ms),\n CHECK (consumed_at_ms IS NULL OR consumed_at_ms >= resolved_at_ms),\n CHECK (consumed_at_ms IS NULL OR consumed_at_ms <= updated_at_ms),\n CHECK (requested_by_device_token_auth IN (0, 1)),\n CHECK (\n (\n status = 'pending'\n AND decision IS NULL\n AND terminal_reason IS NULL\n AND resolved_at_ms IS NULL\n AND resolver_kind IS NULL\n AND resolver_id IS NULL\n AND consumed_at_ms IS NULL\n AND consumed_by IS NULL\n )\n OR (\n status = 'allowed'\n AND decision IN ('allow-once', 'allow-always')\n AND terminal_reason = 'user'\n AND resolved_at_ms IS NOT NULL\n AND resolver_kind IS NOT NULL\n )\n OR (\n status = 'denied'\n AND decision = 'deny'\n AND terminal_reason IN ('user', 'malformed-verdict', 'no-route', 'storage-corrupt')\n AND resolved_at_ms IS NOT NULL\n AND resolver_kind IS NOT NULL\n AND consumed_at_ms IS NULL\n AND consumed_by IS NULL\n )\n OR (\n status = 'expired'\n AND decision = 'deny'\n AND terminal_reason = 'timeout'\n AND resolved_at_ms IS NOT NULL\n AND resolver_kind IS NOT NULL\n AND consumed_at_ms IS NULL\n AND consumed_by IS NULL\n )\n OR (\n status = 'cancelled'\n AND decision = 'deny'\n AND terminal_reason IN ('run-aborted', 'gateway-restart')\n AND resolved_at_ms IS NOT NULL\n AND resolver_kind IS NOT NULL\n AND consumed_at_ms IS NULL\n AND consumed_by IS NULL\n )\n ),\n CHECK (\n (consumed_at_ms IS NULL AND consumed_by IS NULL)\n OR (\n status = 'allowed'\n AND decision = 'allow-once'\n AND consumed_at_ms IS NOT NULL\n AND consumed_by IS NOT NULL\n )\n )\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_status_expiry\n ON operator_approvals(status, expires_at_ms, approval_id);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_operator_approvals_resolution_ref\n ON operator_approvals(resolution_ref);\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_source_session_created\n ON operator_approvals(source_session_key, created_at_ms DESC, approval_id);\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_source_run_resolved\n ON operator_approvals(source_run_id, resolved_at_ms, approval_id)\n WHERE source_run_id IS NOT NULL AND resolved_at_ms IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_resolved\n ON operator_approvals(resolved_at_ms, approval_id)\n WHERE resolved_at_ms IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_runtime_pending\n ON operator_approvals(runtime_epoch, approval_id)\n WHERE status = 'pending';\n\nCREATE TABLE IF NOT EXISTS operator_approval_execution_identities (\n approval_id TEXT NOT NULL PRIMARY KEY\n REFERENCES operator_approvals(approval_id) ON DELETE CASCADE,\n source_context_id TEXT NOT NULL CHECK (\n length(source_context_id) BETWEEN 1 AND 256 AND source_context_id = trim(source_context_id)\n ),\n source_execution_id TEXT NOT NULL CHECK (\n length(source_execution_id) BETWEEN 1 AND 256 AND source_execution_id = trim(source_execution_id)\n )\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS operator_approval_standing_grants (\n grant_id TEXT NOT NULL PRIMARY KEY CHECK (length(grant_id) > 0),\n minted_by_approval_id TEXT NOT NULL\n REFERENCES operator_approvals(approval_id) ON DELETE CASCADE,\n agent_id TEXT NOT NULL CHECK (length(agent_id) > 0),\n cron_job_id TEXT NOT NULL CHECK (length(cron_job_id) > 0),\n job_config_revision TEXT NOT NULL CHECK (length(job_config_revision) > 0),\n operation_binding TEXT NOT NULL CHECK (length(operation_binding) > 0),\n created_at_ms INTEGER NOT NULL,\n expires_at_ms INTEGER CHECK (expires_at_ms IS NULL OR expires_at_ms >= created_at_ms),\n revoked_at_ms INTEGER,\n revoked_by TEXT,\n last_used_at_ms INTEGER,\n use_count INTEGER NOT NULL DEFAULT 0\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_operator_approval_standing_grants_binding\n ON operator_approval_standing_grants(agent_id, cron_job_id, operation_binding, created_at_ms DESC);\n\nCREATE TABLE IF NOT EXISTS operator_approval_standing_grant_generations (\n grant_id TEXT NOT NULL PRIMARY KEY\n REFERENCES operator_approval_standing_grants(grant_id) ON DELETE CASCADE,\n job_definition_generation INTEGER NOT NULL CHECK (job_definition_generation >= 1)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS schema_meta (\n meta_key TEXT NOT NULL PRIMARY KEY,\n role TEXT NOT NULL,\n schema_version INTEGER NOT NULL,\n agent_id TEXT,\n app_version TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS config_machine_state (\n state_key TEXT NOT NULL PRIMARY KEY,\n value_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS device_pairing_pending (\n request_id TEXT NOT NULL PRIMARY KEY,\n device_id TEXT NOT NULL,\n public_key TEXT NOT NULL,\n display_name TEXT,\n platform TEXT,\n device_family TEXT,\n client_id TEXT,\n client_mode TEXT,\n browser_origin TEXT,\n role TEXT,\n roles_json TEXT,\n scopes_json TEXT,\n remote_ip TEXT,\n silent INTEGER,\n is_repair INTEGER,\n ts INTEGER NOT NULL,\n refreshed_at_ms INTEGER\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_device_pairing_pending_device\n ON device_pairing_pending(device_id, ts DESC);\n\nCREATE TABLE IF NOT EXISTS device_pairing_paired (\n device_id TEXT NOT NULL PRIMARY KEY,\n public_key TEXT NOT NULL,\n display_name TEXT,\n operator_label TEXT,\n platform TEXT,\n device_family TEXT,\n client_id TEXT,\n client_mode TEXT,\n browser_origin TEXT,\n role TEXT,\n roles_json TEXT,\n scopes_json TEXT,\n approved_scopes_json TEXT,\n remote_ip TEXT,\n tokens_json TEXT,\n approved_via TEXT,\n node_surface_json TEXT,\n pending_node_surface_json TEXT,\n created_at_ms INTEGER NOT NULL,\n approved_at_ms INTEGER NOT NULL,\n last_seen_at_ms INTEGER,\n last_seen_reason TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_device_pairing_paired_approved\n ON device_pairing_paired(approved_at_ms DESC, device_id);\n\nCREATE TABLE IF NOT EXISTS device_bootstrap_tokens (\n token_key TEXT NOT NULL PRIMARY KEY,\n token TEXT NOT NULL,\n setup_id TEXT,\n ts INTEGER NOT NULL,\n device_id TEXT,\n public_key TEXT,\n profile_json TEXT,\n redeemed_profile_json TEXT,\n pending_profile_json TEXT,\n issued_at_ms INTEGER NOT NULL,\n last_used_at_ms INTEGER\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_device_bootstrap_tokens_ts\n ON device_bootstrap_tokens(ts);\n\n-- Terminal outcome of a redeemed setup credential. The bootstrap row is deleted\n-- on redemption, so this is the only durable proof a setup code succeeded; the\n-- presenting client reconciles it when the completion broadcast is missed.\n-- Non-secret only: never the bootstrap token or anything derived from it.\n-- Bounded by retention to a handful of live rows, so the primary key is the\n-- only access path worth having.\nCREATE TABLE IF NOT EXISTS device_pair_setup_completions (\n setup_id TEXT NOT NULL PRIMARY KEY,\n device_id TEXT NOT NULL,\n device_name TEXT,\n access TEXT NOT NULL,\n completed_at_ms INTEGER NOT NULL,\n delivery_state TEXT NOT NULL CHECK (delivery_state IN ('uncertain', 'confirmed')),\n retain_until_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS device_pairing_join_codes (\n shortcode TEXT,\n payload_json TEXT,\n created_at_ms INTEGER,\n expires_at_ms INTEGER\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS device_identities (\n identity_key TEXT NOT NULL PRIMARY KEY,\n device_id TEXT NOT NULL,\n public_key_pem TEXT NOT NULL,\n private_key_pem TEXT NOT NULL,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_device_identities_device\n ON device_identities(device_id, updated_at_ms DESC);\n\nCREATE TABLE IF NOT EXISTS device_auth_tokens (\n device_id TEXT NOT NULL,\n role TEXT NOT NULL,\n token TEXT NOT NULL,\n scopes_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (device_id, role)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_device_auth_tokens_updated\n ON device_auth_tokens(updated_at_ms DESC, device_id, role);\n\nCREATE TABLE IF NOT EXISTS gateway_origin_device_tokens (\n gateway_scope TEXT NOT NULL,\n device_id TEXT NOT NULL,\n role TEXT NOT NULL,\n token TEXT NOT NULL,\n scopes_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (gateway_scope, device_id, role)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS macos_port_guardian_records (\n pid INTEGER NOT NULL PRIMARY KEY,\n port INTEGER NOT NULL,\n command TEXT NOT NULL,\n mode TEXT NOT NULL,\n timestamp REAL NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_macos_port_guardian_records_port\n ON macos_port_guardian_records(port, timestamp DESC);\n\nCREATE TABLE IF NOT EXISTS workspace_setup_state (\n workspace_key TEXT NOT NULL PRIMARY KEY,\n -- NULL only for attestation-only rows whose legacy source never recorded a\n -- path (orphan hashed-key attestations); setup rows always carry one.\n workspace_path TEXT,\n -- NULL setup columns mean an attestation-only row: replaceWorkspaceAttestation\n -- may record hashes before any setup milestone exists for the workspace.\n version INTEGER,\n bootstrap_seeded_at TEXT,\n setup_completed_at TEXT,\n updated_at INTEGER,\n attested_at_ms INTEGER,\n attestation_updated_at_ms INTEGER,\n CHECK (version IS NULL OR workspace_path IS NOT NULL)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_workspace_setup_state_path\n ON workspace_setup_state(workspace_path);\n\nCREATE TABLE IF NOT EXISTS workspace_path_aliases (\n alias_key TEXT NOT NULL PRIMARY KEY,\n alias_path TEXT NOT NULL,\n workspace_key TEXT NOT NULL,\n workspace_path TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_workspace_path_aliases_workspace\n ON workspace_path_aliases(workspace_key);\n\n\n\nCREATE TABLE IF NOT EXISTS workspace_generated_bootstrap_hashes (\n workspace_key TEXT NOT NULL,\n filename TEXT NOT NULL,\n sha256 TEXT NOT NULL,\n PRIMARY KEY (workspace_key, filename),\n FOREIGN KEY (workspace_key) REFERENCES workspace_setup_state(workspace_key) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS native_hook_relay_bridges (\n relay_id TEXT NOT NULL PRIMARY KEY,\n pid INTEGER NOT NULL,\n hostname TEXT NOT NULL,\n port INTEGER NOT NULL,\n token TEXT NOT NULL,\n expires_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_native_hook_relay_bridges_expires\n ON native_hook_relay_bridges(expires_at_ms, relay_id);\n\nCREATE TABLE IF NOT EXISTS managed_outgoing_image_records (\n attachment_id TEXT NOT NULL PRIMARY KEY,\n session_key TEXT NOT NULL,\n agent_id TEXT,\n message_id TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT,\n retention_class TEXT,\n alt TEXT NOT NULL,\n original_media_root TEXT NOT NULL,\n original_media_id TEXT NOT NULL,\n original_media_subdir TEXT NOT NULL,\n original_content_type TEXT NOT NULL,\n original_width INTEGER,\n original_height INTEGER,\n original_size_bytes INTEGER,\n original_filename TEXT,\n record_json TEXT NOT NULL,\n cleanup_pending INTEGER NOT NULL DEFAULT 0 CHECK (cleanup_pending IN (0, 1))\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_managed_outgoing_images_session\n ON managed_outgoing_image_records(session_key, created_at DESC, attachment_id);\n\nCREATE INDEX IF NOT EXISTS idx_managed_outgoing_images_message\n ON managed_outgoing_image_records(session_key, message_id, attachment_id)\n WHERE message_id IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_managed_outgoing_images_agent_session\n ON managed_outgoing_image_records(session_key, agent_id, created_at DESC, attachment_id);\n\nCREATE INDEX IF NOT EXISTS idx_managed_outgoing_images_agent_message\n ON managed_outgoing_image_records(session_key, agent_id, message_id, attachment_id)\n WHERE message_id IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS channel_pairing_requests (\n channel_key TEXT NOT NULL,\n account_id TEXT NOT NULL,\n request_id TEXT NOT NULL,\n code TEXT NOT NULL,\n created_at TEXT NOT NULL,\n last_seen_at TEXT NOT NULL,\n meta_json TEXT,\n PRIMARY KEY (channel_key, account_id, request_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_channel_pairing_requests_code\n ON channel_pairing_requests(channel_key, code);\n\nCREATE INDEX IF NOT EXISTS idx_channel_pairing_requests_created\n ON channel_pairing_requests(channel_key, created_at, request_id);\n\nCREATE TABLE IF NOT EXISTS channel_pairing_allow_entries (\n channel_key TEXT NOT NULL,\n account_id TEXT NOT NULL,\n entry TEXT NOT NULL,\n sort_order INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (channel_key, account_id, entry)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_channel_pairing_allow_account\n ON channel_pairing_allow_entries(channel_key, account_id, sort_order, entry);\n\nCREATE TABLE IF NOT EXISTS web_push_subscriptions (\n endpoint_hash TEXT NOT NULL PRIMARY KEY,\n subscription_id TEXT NOT NULL UNIQUE,\n endpoint TEXT NOT NULL,\n p256dh TEXT NOT NULL,\n auth TEXT NOT NULL,\n device_id TEXT,\n user_profile_id TEXT,\n preferences_json TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_web_push_subscriptions_updated\n ON web_push_subscriptions(updated_at_ms DESC, subscription_id);\n\nCREATE TABLE IF NOT EXISTS web_push_approval_deliveries (\n approval_id TEXT NOT NULL\n REFERENCES operator_approvals(approval_id) ON DELETE CASCADE,\n subscription_id TEXT NOT NULL\n REFERENCES web_push_subscriptions(subscription_id) ON DELETE CASCADE,\n device_id TEXT NOT NULL,\n user_profile_id TEXT,\n prepared_at_ms INTEGER NOT NULL,\n PRIMARY KEY (approval_id, subscription_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_web_push_approval_deliveries_subscription\n ON web_push_approval_deliveries(subscription_id, approval_id);\n\nCREATE TABLE IF NOT EXISTS apns_registrations (\n node_id TEXT NOT NULL PRIMARY KEY,\n transport TEXT NOT NULL,\n token TEXT,\n relay_handle TEXT,\n send_grant TEXT,\n installation_id TEXT,\n relay_origin TEXT,\n topic TEXT NOT NULL,\n environment TEXT NOT NULL,\n distribution TEXT,\n token_debug_suffix TEXT,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_apns_registrations_updated\n ON apns_registrations(updated_at_ms DESC, node_id);\n\nCREATE TABLE IF NOT EXISTS apns_registration_tombstones (\n node_id TEXT NOT NULL PRIMARY KEY,\n deleted_at_ms INTEGER NOT NULL\n) STRICT;\n\n-- Node-host-owned launch journal. The descriptor and its credential remain\n-- process memory only; this table records bounded supervision facts.\nCREATE TABLE IF NOT EXISTS node_worker_launches (\n launch_id TEXT NOT NULL PRIMARY KEY\n CHECK (length(launch_id) BETWEEN 1 AND 256 AND instr(launch_id, char(0)) = 0),\n plan_hash TEXT NOT NULL\n CHECK (length(plan_hash) = 64 AND plan_hash NOT GLOB '*[^0-9a-f]*'),\n gateway_namespace TEXT NOT NULL\n CHECK (\n length(gateway_namespace) BETWEEN 1 AND 128\n AND gateway_namespace NOT GLOB '*[^A-Za-z0-9._-]*'\n AND gateway_namespace GLOB '[A-Za-z0-9]*'\n ),\n environment_id TEXT NOT NULL\n CHECK (length(environment_id) BETWEEN 1 AND 256 AND instr(environment_id, char(0)) = 0),\n session_id TEXT NOT NULL\n CHECK (length(session_id) BETWEEN 1 AND 256 AND instr(session_id, char(0)) = 0),\n owner_epoch INTEGER NOT NULL CHECK (owner_epoch BETWEEN 1 AND 9007199254740991),\n placement_generation INTEGER NOT NULL\n CHECK (placement_generation BETWEEN 0 AND 9007199254740991),\n run_id TEXT NOT NULL\n CHECK (length(run_id) BETWEEN 1 AND 256 AND instr(run_id, char(0)) = 0),\n state TEXT NOT NULL\n CHECK (state IN ('pending', 'running', 'completed', 'failed', 'interrupted', 'cancelled')),\n supervisor_pid INTEGER NOT NULL CHECK (supervisor_pid BETWEEN 1 AND 2147483647),\n supervisor_start_time INTEGER NOT NULL\n CHECK (supervisor_start_time BETWEEN 0 AND 9007199254740991),\n worker_pid INTEGER CHECK (worker_pid IS NULL OR worker_pid BETWEEN 1 AND 2147483647),\n worker_start_time INTEGER CHECK (\n worker_start_time IS NULL OR worker_start_time BETWEEN 0 AND 9007199254740991\n ),\n result_json TEXT CHECK (\n result_json IS NULL\n OR (\n length(CAST(result_json AS BLOB)) BETWEEN 1 AND 65536\n AND instr(result_json, char(0)) = 0\n AND json_valid(result_json)\n )\n ),\n error_text TEXT CHECK (\n error_text IS NULL\n OR (\n length(CAST(error_text AS BLOB)) BETWEEN 1 AND 4096\n AND instr(error_text, char(0)) = 0\n AND instr(error_text, char(10)) = 0\n AND instr(error_text, char(13)) = 0\n )\n ),\n completed_at_ms INTEGER CHECK (\n completed_at_ms IS NULL OR completed_at_ms BETWEEN 0 AND 9007199254740991\n ),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms BETWEEN 0 AND 9007199254740991),\n updated_at_ms INTEGER NOT NULL CHECK (\n updated_at_ms BETWEEN created_at_ms AND 9007199254740991\n ),\n CHECK ((worker_pid IS NULL) = (worker_start_time IS NULL)),\n CHECK (\n (state = 'pending'\n AND worker_pid IS NULL AND result_json IS NULL AND error_text IS NULL\n AND completed_at_ms IS NULL)\n OR\n (state = 'running'\n AND worker_pid IS NOT NULL AND result_json IS NULL AND error_text IS NULL\n AND completed_at_ms IS NULL)\n OR\n (state = 'completed'\n AND result_json IS NOT NULL AND error_text IS NULL\n AND completed_at_ms BETWEEN created_at_ms AND updated_at_ms)\n OR\n (state IN ('failed', 'interrupted', 'cancelled')\n AND result_json IS NULL AND error_text IS NOT NULL\n AND completed_at_ms BETWEEN created_at_ms AND updated_at_ms)\n )\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_node_worker_launches_terminal_completed\n ON node_worker_launches(completed_at_ms, launch_id)\n WHERE completed_at_ms IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS node_worker_launch_containers (\n launch_id TEXT PRIMARY KEY,\n container_json TEXT\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS node_worker_launch_cleanup (\n launch_id TEXT NOT NULL PRIMARY KEY\n REFERENCES node_worker_launches(launch_id) ON DELETE CASCADE,\n cleanup_mode TEXT NOT NULL CHECK (cleanup_mode IN ('process-group', 'owned-anchor')),\n lineage_settled INTEGER CHECK (lineage_settled IS NULL OR lineage_settled = 1)\n) STRICT;\n\n-- Older readers retain owned-anchor custody: lineage EOF is not this certificate.\nCREATE TABLE IF NOT EXISTS node_worker_launch_process_scopes (\n launch_id TEXT NOT NULL PRIMARY KEY\n REFERENCES node_worker_launches(launch_id) ON DELETE CASCADE,\n scope_kind TEXT NOT NULL CHECK (scope_kind = 'linux-subreaper'),\n descendants_reaped INTEGER CHECK (descendants_reaped IS NULL OR descendants_reaped = 1)\n) STRICT;\n\n-- Turn receipts have a shorter lifetime than their physical worker owner.\n-- Keeping the launch running preserves capacity and predecessor cleanup semantics.\nCREATE TABLE IF NOT EXISTS node_worker_turns (\n turn_id TEXT NOT NULL PRIMARY KEY\n CHECK (length(turn_id) BETWEEN 1 AND 256 AND instr(turn_id, char(0)) = 0),\n owner_launch_id TEXT NOT NULL\n REFERENCES node_worker_launches(launch_id) ON DELETE CASCADE,\n plan_hash TEXT NOT NULL\n CHECK (length(plan_hash) = 64 AND plan_hash NOT GLOB '*[^0-9a-f]*'),\n run_id TEXT NOT NULL\n CHECK (length(run_id) BETWEEN 1 AND 256 AND instr(run_id, char(0)) = 0),\n state TEXT NOT NULL\n CHECK (state IN ('running', 'completed', 'failed', 'interrupted', 'cancelled')),\n result_json TEXT CHECK (\n result_json IS NULL\n OR (\n length(CAST(result_json AS BLOB)) BETWEEN 1 AND 65536\n AND instr(result_json, char(0)) = 0\n AND json_valid(result_json)\n )\n ),\n error_text TEXT CHECK (\n error_text IS NULL\n OR (\n length(CAST(error_text AS BLOB)) BETWEEN 1 AND 4096\n AND instr(error_text, char(0)) = 0\n AND instr(error_text, char(10)) = 0\n AND instr(error_text, char(13)) = 0\n )\n ),\n completed_at_ms INTEGER CHECK (\n completed_at_ms IS NULL OR completed_at_ms BETWEEN 0 AND 9007199254740991\n ),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms BETWEEN 0 AND 9007199254740991),\n updated_at_ms INTEGER NOT NULL CHECK (\n updated_at_ms BETWEEN created_at_ms AND 9007199254740991\n ),\n CHECK (\n (state = 'running'\n AND result_json IS NULL AND error_text IS NULL AND completed_at_ms IS NULL)\n OR\n (state = 'completed'\n AND result_json IS NOT NULL AND error_text IS NULL\n AND completed_at_ms BETWEEN created_at_ms AND updated_at_ms)\n OR\n (state IN ('failed', 'interrupted', 'cancelled')\n AND result_json IS NULL AND error_text IS NOT NULL\n AND completed_at_ms BETWEEN created_at_ms AND updated_at_ms)\n )\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_node_worker_turns_terminal_completed\n ON node_worker_turns(completed_at_ms, turn_id)\n WHERE completed_at_ms IS NOT NULL;\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_node_worker_turns_active_owner\n ON node_worker_turns(owner_launch_id)\n WHERE state = 'running';\n\nCREATE TABLE IF NOT EXISTS config_health_entries (\n config_path TEXT NOT NULL PRIMARY KEY,\n last_known_good_json TEXT,\n last_promoted_good_json TEXT,\n last_observed_suspicious_signature TEXT,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS clawhub_promotion_claims (\n slug TEXT NOT NULL PRIMARY KEY,\n provider TEXT,\n model_keys_json TEXT NOT NULL,\n ends_at_ms INTEGER NOT NULL,\n claimed_at_ms INTEGER NOT NULL\n) STRICT;\n\n\n\nCREATE TABLE IF NOT EXISTS official_external_plugin_catalog_snapshots (\n feed_url TEXT NOT NULL PRIMARY KEY,\n body TEXT NOT NULL,\n status INTEGER NOT NULL,\n etag TEXT,\n last_modified TEXT,\n checksum TEXT NOT NULL,\n saved_at TEXT NOT NULL,\n trust_mode TEXT,\n trust_key_id TEXT,\n trust_signature_count INTEGER,\n trust_threshold INTEGER,\n trust_verified_at TEXT,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_official_external_plugin_catalog_snapshots_updated\n ON official_external_plugin_catalog_snapshots(updated_at_ms DESC, feed_url);\n\nCREATE TABLE IF NOT EXISTS update_runs (\n run_id TEXT PRIMARY KEY NOT NULL,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n trigger TEXT NOT NULL CHECK (trigger IN ('chat', 'control-ui', 'cli', 'campaign', 'mac-app', 'api')),\n phase TEXT NOT NULL CHECK (phase IN ('requested', 'staging', 'validating', 'repairing', 'activating', 'restarting', 'verifying', 'finished')),\n status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'rolled-back', 'skipped')),\n reason TEXT,\n origin_json TEXT NOT NULL CHECK (length(CAST(origin_json AS BLOB)) <= 16384),\n target_json TEXT NOT NULL CHECK (length(CAST(target_json AS BLOB)) <= 16384),\n before_json TEXT NOT NULL CHECK (length(CAST(before_json AS BLOB)) <= 16384),\n after_json TEXT NOT NULL CHECK (length(CAST(after_json AS BLOB)) <= 16384),\n steps_json TEXT NOT NULL CHECK (length(CAST(steps_json AS BLOB)) <= 16384),\n verification_json TEXT NOT NULL CHECK (length(CAST(verification_json AS BLOB)) <= 16384),\n repair_json TEXT NOT NULL CHECK (length(CAST(repair_json AS BLOB)) <= 16384),\n confirmed_at_ms INTEGER,\n finished_at_ms INTEGER,\n downtime_ms INTEGER,\n CHECK ((status = 'running' AND phase != 'finished' AND finished_at_ms IS NULL) OR\n (status != 'running' AND phase = 'finished' AND finished_at_ms IS NOT NULL))\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_update_runs_created\n ON update_runs(created_at_ms DESC, run_id);\nCREATE INDEX IF NOT EXISTS idx_update_runs_active\n ON update_runs(status, created_at_ms DESC, run_id);\n\nCREATE TABLE IF NOT EXISTS gateway_restart_sentinel (\n sentinel_key TEXT NOT NULL PRIMARY KEY,\n version INTEGER NOT NULL,\n kind TEXT NOT NULL,\n status TEXT NOT NULL,\n ts INTEGER NOT NULL,\n session_key TEXT,\n thread_id TEXT,\n delivery_channel TEXT,\n delivery_to TEXT,\n delivery_account_id TEXT,\n message TEXT,\n continuation_json TEXT,\n doctor_hint TEXT,\n stats_json TEXT,\n payload_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_gateway_restart_sentinel_ts\n ON gateway_restart_sentinel(ts DESC, sentinel_key);\n\nCREATE TABLE IF NOT EXISTS gateway_restart_intent (\n intent_key TEXT NOT NULL PRIMARY KEY,\n kind TEXT NOT NULL,\n pid INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n reason TEXT,\n force INTEGER,\n wait_ms INTEGER,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS gateway_restart_handoff (\n handoff_key TEXT NOT NULL PRIMARY KEY,\n kind TEXT NOT NULL,\n version INTEGER NOT NULL,\n intent_id TEXT NOT NULL,\n pid INTEGER NOT NULL,\n process_instance_id TEXT,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n reason TEXT,\n restart_trace_started_at INTEGER,\n restart_trace_last_at INTEGER,\n source TEXT NOT NULL,\n restart_kind TEXT NOT NULL,\n supervisor_mode TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_gateway_restart_handoff_expiry\n ON gateway_restart_handoff(expires_at, pid);\n\nCREATE TABLE IF NOT EXISTS gateway_boot_lifecycle (\n boot_id TEXT NOT NULL PRIMARY KEY,\n pid INTEGER NOT NULL,\n started_at_ms INTEGER NOT NULL,\n completed_at_ms INTEGER,\n outcome TEXT,\n startup_reason TEXT,\n reason TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_gateway_boot_lifecycle_started\n ON gateway_boot_lifecycle(started_at_ms);\n\nCREATE TABLE IF NOT EXISTS acp_sessions (\n session_key TEXT NOT NULL PRIMARY KEY,\n session_id TEXT,\n backend TEXT NOT NULL,\n agent TEXT NOT NULL,\n runtime_session_name TEXT NOT NULL,\n identity_json TEXT,\n mode TEXT NOT NULL,\n runtime_options_json TEXT,\n cwd TEXT,\n state TEXT NOT NULL,\n last_activity_at INTEGER NOT NULL,\n last_error TEXT,\n updated_at INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_acp_sessions_state_activity\n ON acp_sessions(state, last_activity_at DESC, session_key);\n\nCREATE INDEX IF NOT EXISTS idx_acp_sessions_agent_activity\n ON acp_sessions(agent, last_activity_at DESC, session_key);\n\nCREATE TABLE IF NOT EXISTS acp_replay_sessions (\n session_id TEXT NOT NULL PRIMARY KEY,\n session_key TEXT NOT NULL,\n cwd TEXT NOT NULL,\n complete INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n next_seq INTEGER NOT NULL,\n -- Running estimate of this session's ledger footprint (row overhead plus\n -- all event rows), maintained at insert/trim so budget checks never scan\n -- acp_replay_events (#100622).\n estimated_bytes INTEGER NOT NULL DEFAULT 0\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_acp_replay_sessions_key_updated\n ON acp_replay_sessions(session_key, complete, updated_at DESC, session_id);\n\nCREATE INDEX IF NOT EXISTS idx_acp_replay_sessions_updated\n ON acp_replay_sessions(updated_at DESC, session_id);\n\nCREATE TABLE IF NOT EXISTS acp_replay_events (\n session_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n at INTEGER NOT NULL,\n session_key TEXT NOT NULL,\n run_id TEXT,\n update_json TEXT NOT NULL,\n estimated_bytes INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (session_id, seq),\n FOREIGN KEY (session_id) REFERENCES acp_replay_sessions(session_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_acp_replay_events_session_seq\n ON acp_replay_events(session_id, seq);\n\nCREATE TABLE IF NOT EXISTS agent_databases (\n agent_id TEXT NOT NULL,\n path TEXT NOT NULL,\n schema_version INTEGER NOT NULL,\n last_seen_at INTEGER NOT NULL,\n size_bytes INTEGER,\n PRIMARY KEY (agent_id, path)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agent_deletion_journal (\n agent_id TEXT PRIMARY KEY,\n operation_id TEXT NOT NULL DEFAULT '',\n agent_dir TEXT NOT NULL,\n workspace_dir TEXT NOT NULL,\n sessions_dir TEXT NOT NULL,\n database_paths_json TEXT NOT NULL DEFAULT '[]',\n cleanup_paths_json TEXT NOT NULL DEFAULT '[]',\n created_at INTEGER NOT NULL,\n cleanup_completed INTEGER NOT NULL DEFAULT 0,\n delete_files INTEGER NOT NULL DEFAULT 1\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agent_provenance (\n agent_id TEXT PRIMARY KEY,\n created_via TEXT NOT NULL CHECK (created_via IN ('operator', 'agent', 'claw')),\n creator_agent_id TEXT,\n created_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS agent_database_leases (\n lease_id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL,\n path TEXT NOT NULL,\n owner_pid INTEGER NOT NULL,\n owner_start_time INTEGER,\n opened_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS plugin_state_entries (\n plugin_id TEXT NOT NULL,\n namespace TEXT NOT NULL,\n entry_key TEXT NOT NULL,\n value_json TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (plugin_id, namespace, entry_key)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_plugin_state_expiry\n ON plugin_state_entries(expires_at)\n WHERE expires_at IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_plugin_state_listing\n ON plugin_state_entries(plugin_id, namespace, created_at, entry_key, expires_at);\n\nCREATE TABLE IF NOT EXISTS channel_ingress_events (\n queue_name TEXT NOT NULL,\n event_id TEXT NOT NULL,\n channel_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n status TEXT NOT NULL,\n lane_key TEXT,\n payload_json TEXT NOT NULL,\n metadata_json TEXT,\n received_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n claim_token TEXT,\n claim_owner TEXT,\n claimed_at INTEGER,\n attempts INTEGER NOT NULL DEFAULT 0,\n last_attempt_at INTEGER,\n last_error TEXT,\n failed_reason TEXT,\n failed_at INTEGER,\n completed_at INTEGER,\n completed_metadata_json TEXT,\n PRIMARY KEY (queue_name, event_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_channel_ingress_pending\n ON channel_ingress_events(queue_name, status, received_at, event_id);\n\nCREATE INDEX IF NOT EXISTS idx_channel_ingress_claims\n ON channel_ingress_events(queue_name, status, claimed_at);\n\nCREATE INDEX IF NOT EXISTS idx_channel_ingress_lane\n ON channel_ingress_events(queue_name, status, lane_key);\n\nCREATE TABLE IF NOT EXISTS plugin_blob_entries (\n plugin_id TEXT NOT NULL,\n namespace TEXT NOT NULL,\n entry_key TEXT NOT NULL,\n metadata_json TEXT NOT NULL,\n blob BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER,\n PRIMARY KEY (plugin_id, namespace, entry_key)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_plugin_blob_expiry\n ON plugin_blob_entries(expires_at)\n WHERE expires_at IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_plugin_blob_listing\n ON plugin_blob_entries(plugin_id, namespace, created_at, entry_key);\n\nCREATE TABLE IF NOT EXISTS skill_uploads (\n upload_id TEXT NOT NULL PRIMARY KEY,\n kind TEXT NOT NULL,\n slug TEXT NOT NULL,\n force INTEGER NOT NULL,\n size_bytes INTEGER NOT NULL,\n sha256 TEXT,\n actual_sha256 TEXT,\n received_bytes INTEGER NOT NULL,\n archive_blob BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL,\n committed INTEGER NOT NULL,\n committed_at INTEGER,\n idempotency_key_hash TEXT UNIQUE\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_skill_uploads_expiry\n ON skill_uploads(expires_at);\n\nCREATE INDEX IF NOT EXISTS idx_skill_uploads_idempotency\n ON skill_uploads(idempotency_key_hash)\n WHERE idempotency_key_hash IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS skill_upload_chunks (\n upload_id TEXT NOT NULL,\n byte_offset INTEGER NOT NULL CHECK (byte_offset >= 0),\n size_bytes INTEGER NOT NULL CHECK (size_bytes > 0),\n chunk_blob BLOB NOT NULL,\n PRIMARY KEY (upload_id, byte_offset),\n FOREIGN KEY (upload_id) REFERENCES skill_uploads(upload_id) ON DELETE CASCADE,\n CHECK (length(chunk_blob) = size_bytes)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capture_sessions (\n id TEXT NOT NULL PRIMARY KEY,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n mode TEXT NOT NULL,\n source_scope TEXT NOT NULL,\n source_process TEXT NOT NULL,\n proxy_url TEXT\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capture_blobs (\n blob_id TEXT NOT NULL PRIMARY KEY,\n content_type TEXT,\n encoding TEXT NOT NULL,\n size_bytes INTEGER NOT NULL,\n sha256 TEXT NOT NULL,\n data BLOB NOT NULL,\n created_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS capture_events (\n id INTEGER NOT NULL PRIMARY KEY,\n session_id TEXT NOT NULL,\n ts INTEGER NOT NULL,\n source_scope TEXT NOT NULL,\n source_process TEXT NOT NULL,\n protocol TEXT NOT NULL,\n direction TEXT NOT NULL,\n kind TEXT NOT NULL,\n flow_id TEXT NOT NULL,\n method TEXT,\n host TEXT,\n path TEXT,\n status INTEGER,\n close_code INTEGER,\n content_type TEXT,\n headers_json TEXT,\n data_text TEXT,\n data_blob_id TEXT,\n data_sha256 TEXT,\n error_text TEXT,\n meta_json TEXT,\n FOREIGN KEY (session_id) REFERENCES capture_sessions(id) ON DELETE CASCADE,\n FOREIGN KEY (data_blob_id) REFERENCES capture_blobs(blob_id) ON DELETE SET NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS capture_events_session_ts_idx\n ON capture_events(session_id, ts);\n\nCREATE INDEX IF NOT EXISTS capture_events_flow_idx\n ON capture_events(flow_id, ts);\n\nCREATE TABLE IF NOT EXISTS sandbox_registry_entries (\n registry_kind TEXT NOT NULL,\n container_name TEXT NOT NULL,\n session_key TEXT,\n backend_id TEXT,\n runtime_label TEXT,\n image TEXT,\n created_at_ms INTEGER,\n last_used_at_ms INTEGER,\n config_label_kind TEXT,\n config_hash TEXT,\n cdp_port INTEGER,\n no_vnc_port INTEGER,\n entry_json TEXT NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (registry_kind, container_name)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_sandbox_registry_updated\n ON sandbox_registry_entries(registry_kind, updated_at DESC, container_name);\n\nCREATE INDEX IF NOT EXISTS idx_sandbox_registry_session\n ON sandbox_registry_entries(registry_kind, session_key, last_used_at_ms DESC, container_name)\n WHERE session_key IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_sandbox_registry_last_used\n ON sandbox_registry_entries(registry_kind, last_used_at_ms DESC, container_name)\n WHERE last_used_at_ms IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS cron_jobs (\n store_key TEXT NOT NULL,\n job_id TEXT NOT NULL,\n declaration_key TEXT,\n owner_agent_id TEXT,\n name TEXT NOT NULL,\n description TEXT,\n enabled INTEGER NOT NULL,\n agent_id TEXT,\n payload_kind TEXT NOT NULL,\n job_json TEXT NOT NULL,\n grant_definition_revision TEXT,\n grant_definition_generation INTEGER,\n grant_definition_updated_at INTEGER,\n state_json TEXT NOT NULL DEFAULT '{}',\n runtime_updated_at_ms INTEGER,\n schedule_identity TEXT,\n sort_order INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (store_key, job_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_cron_jobs_store_order\n ON cron_jobs(store_key, sort_order ASC, updated_at ASC, job_id);\n\n-- One owner-native receipt is also the durable execution fence. Receipts\n-- survive job deletion so operators can distinguish a run from log inference.\nCREATE TABLE IF NOT EXISTS cron_run_receipts (\n receipt_id TEXT PRIMARY KEY,\n store_key TEXT NOT NULL,\n job_id TEXT NOT NULL,\n config_revision TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n request_run_id TEXT,\n delivery_attempt_state TEXT NOT NULL DEFAULT 'unknown' CHECK (delivery_attempt_state IN ('unknown', 'not-started', 'started')),\n status TEXT NOT NULL,\n owner_pid INTEGER NOT NULL,\n owner_start_time INTEGER,\n started_at_ms INTEGER NOT NULL,\n finished_at_ms INTEGER,\n error_text TEXT,\n CHECK (status IN ('running', 'ok', 'error', 'skipped', 'interrupted', 'superseded')),\n CHECK (\n (status = 'running' AND finished_at_ms IS NULL)\n OR\n (status != 'running' AND finished_at_ms IS NOT NULL)\n )\n) STRICT;\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_cron_run_receipts_active_job\n ON cron_run_receipts(store_key, job_id)\n WHERE status = 'running';\n\nCREATE INDEX IF NOT EXISTS idx_cron_run_receipts_job_history\n ON cron_run_receipts(store_key, job_id, started_at_ms DESC, receipt_id DESC);\n\n-- Retirement follows the receipt's retention without changing its released shape.\nCREATE TABLE IF NOT EXISTS cron_run_trigger_state_retirements (\n receipt_id TEXT PRIMARY KEY\n REFERENCES cron_run_receipts(receipt_id) ON DELETE CASCADE\n) STRICT;\n\n-- Runtime-private authority is independent of job_json so downgraded writers\n-- can rewrite recognized job config without erasing or silently widening it.\nCREATE TABLE IF NOT EXISTS cron_job_runtime_authorities (\n store_key TEXT NOT NULL,\n job_id TEXT NOT NULL,\n authority_json TEXT,\n authority_input_fingerprint TEXT,\n recovery_required INTEGER NOT NULL,\n PRIMARY KEY (store_key, job_id),\n FOREIGN KEY (store_key, job_id)\n REFERENCES cron_jobs(store_key, job_id) ON DELETE CASCADE,\n CHECK (recovery_required IN (0, 1)),\n CHECK (\n (recovery_required = 0 AND authority_json IS NOT NULL AND authority_input_fingerprint IS NOT NULL)\n OR\n (recovery_required = 1 AND authority_json IS NULL AND authority_input_fingerprint IS NULL)\n )\n) STRICT;\n\n-- Scratch is separate from cron_jobs so scheduler state writes and downgraded\n-- full-row replacement preserve it. New builds prune rows explicitly on job removal.\n-- content NULL is a tombstone: it keeps the revision lineage monotonic across\n-- unset/recreate so stale compare-and-swap writes cannot resurrect old content.\nCREATE TABLE IF NOT EXISTS cron_job_scratch (\n store_key TEXT NOT NULL,\n job_id TEXT NOT NULL,\n content TEXT,\n revision INTEGER NOT NULL,\n source_sha256 TEXT,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (store_key, job_id),\n CHECK (revision >= 1),\n CHECK (content IS NULL OR length(CAST(content AS BLOB)) <= 262144)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_cron_job_scratch_store_updated\n ON cron_job_scratch(store_key, updated_at_ms DESC, job_id);\n\nCREATE TABLE IF NOT EXISTS delivery_queue_entries (\n queue_name TEXT NOT NULL,\n id TEXT NOT NULL,\n status TEXT NOT NULL,\n entry_kind TEXT,\n session_key TEXT,\n channel TEXT,\n target TEXT,\n account_id TEXT,\n retry_count INTEGER NOT NULL DEFAULT 0,\n last_attempt_at INTEGER,\n last_error TEXT,\n recovery_state TEXT,\n platform_send_started_at INTEGER,\n entry_json TEXT NOT NULL,\n enqueued_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n failed_at INTEGER,\n PRIMARY KEY (queue_name, id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_delivery_queue_pending\n ON delivery_queue_entries(queue_name, status, enqueued_at, id);\n\nCREATE INDEX IF NOT EXISTS idx_delivery_queue_failed\n ON delivery_queue_entries(status, queue_name, failed_at, id);\n\nCREATE INDEX IF NOT EXISTS idx_delivery_queue_session\n ON delivery_queue_entries(queue_name, status, session_key, enqueued_at, id)\n WHERE session_key IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_delivery_queue_target\n ON delivery_queue_entries(queue_name, status, channel, target, enqueued_at, id)\n WHERE channel IS NOT NULL AND target IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS task_runs (\n task_id TEXT NOT NULL PRIMARY KEY,\n runtime TEXT NOT NULL,\n task_kind TEXT,\n source_id TEXT,\n requester_session_key TEXT,\n owner_key TEXT NOT NULL,\n scope_kind TEXT NOT NULL,\n child_session_key TEXT,\n parent_flow_id TEXT,\n parent_task_id TEXT,\n agent_id TEXT,\n requester_agent_id TEXT,\n run_id TEXT,\n execution_owner_host TEXT,\n execution_owner_pid INTEGER,\n execution_owner_start_identity INTEGER,\n label TEXT,\n task TEXT NOT NULL,\n status TEXT NOT NULL,\n delivery_status TEXT NOT NULL,\n notify_policy TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n started_at INTEGER,\n ended_at INTEGER,\n last_event_at INTEGER,\n cleanup_after INTEGER,\n tool_use_count INTEGER,\n last_tool_name TEXT,\n error TEXT,\n progress_summary TEXT,\n terminal_summary TEXT,\n terminal_outcome TEXT,\n detail_json TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_task_runs_run_id ON task_runs(run_id);\nCREATE INDEX IF NOT EXISTS idx_task_runs_status ON task_runs(status);\nCREATE INDEX IF NOT EXISTS idx_task_runs_runtime_status ON task_runs(runtime, status);\nCREATE INDEX IF NOT EXISTS idx_task_runs_cleanup_after ON task_runs(cleanup_after);\nCREATE INDEX IF NOT EXISTS idx_task_runs_last_event_at ON task_runs(last_event_at);\nCREATE INDEX IF NOT EXISTS idx_task_runs_owner_key ON task_runs(owner_key);\nCREATE INDEX IF NOT EXISTS idx_task_runs_parent_flow_id ON task_runs(parent_flow_id);\nCREATE INDEX IF NOT EXISTS idx_task_runs_child_session_key ON task_runs(child_session_key);\nCREATE INDEX IF NOT EXISTS idx_task_runs_requester_session_key ON task_runs(requester_session_key);\nCREATE INDEX IF NOT EXISTS idx_task_runs_runtime_source_ended\n ON task_runs(runtime, source_id, ended_at, created_at, task_id);\nCREATE INDEX IF NOT EXISTS idx_task_runs_runtime_ended\n ON task_runs(runtime, ended_at, created_at, task_id);\n\nCREATE TABLE IF NOT EXISTS subagent_runs (\n run_id TEXT NOT NULL PRIMARY KEY,\n child_session_key TEXT NOT NULL,\n controller_session_key TEXT,\n controller_store_path TEXT,\n requester_session_key TEXT NOT NULL,\n requester_store_path TEXT,\n created_at INTEGER NOT NULL,\n payload_json TEXT NOT NULL DEFAULT '{}'\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_subagent_runs_child_session_key\n ON subagent_runs(child_session_key, created_at DESC, run_id);\nCREATE INDEX IF NOT EXISTS idx_subagent_runs_requester_session_key\n ON subagent_runs(requester_session_key, created_at DESC, run_id);\nCREATE INDEX IF NOT EXISTS idx_subagent_runs_controller_session_key\n ON subagent_runs(controller_session_key, created_at DESC, run_id);\n\nCREATE TABLE IF NOT EXISTS current_conversation_bindings (\n binding_key TEXT NOT NULL PRIMARY KEY,\n binding_id TEXT NOT NULL,\n target_session_key TEXT NOT NULL,\n channel TEXT NOT NULL,\n account_id TEXT NOT NULL,\n conversation_kind TEXT NOT NULL,\n parent_conversation_id TEXT,\n conversation_id TEXT NOT NULL,\n target_kind TEXT NOT NULL,\n status TEXT NOT NULL,\n bound_at INTEGER NOT NULL,\n expires_at INTEGER,\n metadata_json TEXT,\n record_json TEXT NOT NULL,\n updated_at INTEGER NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_current_conversation_bindings_target\n ON current_conversation_bindings(target_session_key, updated_at DESC, binding_key);\nCREATE INDEX IF NOT EXISTS idx_current_conversation_bindings_conversation\n ON current_conversation_bindings(channel, account_id, conversation_kind, conversation_id);\nCREATE INDEX IF NOT EXISTS idx_current_conversation_bindings_expires\n ON current_conversation_bindings(expires_at, binding_key);\n\nCREATE TABLE IF NOT EXISTS plugin_binding_approvals (\n plugin_root TEXT NOT NULL,\n channel TEXT NOT NULL,\n account_id TEXT NOT NULL,\n plugin_id TEXT NOT NULL,\n plugin_name TEXT,\n approved_at INTEGER NOT NULL,\n PRIMARY KEY (plugin_root, channel, account_id)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_plugin_binding_approvals_plugin\n ON plugin_binding_approvals(plugin_id, approved_at DESC);\n\nCREATE TABLE IF NOT EXISTS task_delivery_state (\n task_id TEXT NOT NULL PRIMARY KEY,\n requester_origin_json TEXT,\n last_notified_event_at INTEGER,\n FOREIGN KEY (task_id) REFERENCES task_runs(task_id) ON DELETE CASCADE\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS flow_runs (\n flow_id TEXT NOT NULL PRIMARY KEY,\n shape TEXT,\n sync_mode TEXT NOT NULL DEFAULT 'managed',\n owner_key TEXT NOT NULL,\n requester_origin_json TEXT,\n controller_id TEXT,\n revision INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL,\n notify_policy TEXT NOT NULL,\n goal TEXT NOT NULL,\n current_step TEXT,\n blocked_task_id TEXT,\n blocked_summary TEXT,\n state_json TEXT,\n wait_json TEXT,\n cancel_requested_at INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n ended_at INTEGER\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_flow_runs_status ON flow_runs(status);\nCREATE INDEX IF NOT EXISTS idx_flow_runs_owner_key ON flow_runs(owner_key);\nCREATE INDEX IF NOT EXISTS idx_flow_runs_updated_at ON flow_runs(updated_at);\n\n-- Durable meeting-capture sessions are gateway-global rather than agent-session\n-- transcripts. JSON/JSONL files are doctor import inputs or explicit CLI exports.\nCREATE TABLE IF NOT EXISTS meeting_transcript_sessions (\n session_id TEXT NOT NULL,\n started_at TEXT NOT NULL,\n selector TEXT NOT NULL UNIQUE,\n export_key TEXT NOT NULL,\n session_slug TEXT NOT NULL,\n provider_id TEXT NOT NULL,\n title TEXT,\n source_json TEXT NOT NULL,\n stopped_at TEXT,\n metadata_json TEXT,\n export_manifest_json TEXT NOT NULL DEFAULT '{}',\n export_pending_json TEXT NOT NULL DEFAULT '[]',\n next_utterance_seq INTEGER NOT NULL DEFAULT 0 CHECK (next_utterance_seq >= 0),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= 0),\n PRIMARY KEY (session_id, started_at)\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_meeting_transcript_sessions_started\n ON meeting_transcript_sessions(started_at DESC, session_id);\n\nCREATE INDEX IF NOT EXISTS idx_meeting_transcript_sessions_id\n ON meeting_transcript_sessions(session_id, started_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_meeting_transcript_sessions_slug\n ON meeting_transcript_sessions(session_slug, started_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_meeting_transcript_sessions_export_key\n ON meeting_transcript_sessions(export_key);\n\nCREATE TABLE IF NOT EXISTS meeting_transcript_utterances (\n session_id TEXT NOT NULL,\n session_started_at TEXT NOT NULL,\n sequence INTEGER NOT NULL CHECK (sequence >= 0),\n utterance_id TEXT,\n started_at TEXT,\n ended_at TEXT,\n speaker_id TEXT,\n speaker_label TEXT,\n text TEXT NOT NULL,\n final INTEGER CHECK (final IN (0, 1)),\n metadata_json TEXT,\n PRIMARY KEY (session_id, session_started_at, sequence),\n FOREIGN KEY (session_id, session_started_at)\n REFERENCES meeting_transcript_sessions(session_id, started_at)\n ON DELETE CASCADE\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_meeting_transcript_utterances_id\n ON meeting_transcript_utterances(session_id, session_started_at, utterance_id)\n WHERE utterance_id IS NOT NULL;\n\nCREATE TABLE IF NOT EXISTS meeting_transcript_summaries (\n session_id TEXT NOT NULL,\n session_started_at TEXT NOT NULL,\n generated_at TEXT,\n summary_json TEXT,\n markdown TEXT,\n utterance_count INTEGER NOT NULL CHECK (utterance_count >= 0),\n PRIMARY KEY (session_id, session_started_at),\n FOREIGN KEY (session_id, session_started_at)\n REFERENCES meeting_transcript_sessions(session_id, started_at)\n ON DELETE CASCADE,\n CHECK (summary_json IS NOT NULL OR markdown IS NOT NULL)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS migration_runs (\n id TEXT NOT NULL PRIMARY KEY,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL,\n report_json TEXT NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_migration_runs_started\n ON migration_runs(started_at DESC, id);\n\nCREATE TABLE IF NOT EXISTS migration_sources (\n source_key TEXT NOT NULL PRIMARY KEY,\n migration_kind TEXT NOT NULL,\n source_path TEXT NOT NULL,\n target_table TEXT NOT NULL,\n source_sha256 TEXT,\n source_size_bytes INTEGER,\n source_record_count INTEGER,\n last_run_id TEXT NOT NULL,\n status TEXT NOT NULL,\n imported_at INTEGER NOT NULL,\n removed_source INTEGER NOT NULL DEFAULT 0,\n report_json TEXT NOT NULL,\n FOREIGN KEY (last_run_id) REFERENCES migration_runs(id) ON DELETE CASCADE\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_migration_sources_path\n ON migration_sources(source_path, migration_kind, target_table);\n\nCREATE INDEX IF NOT EXISTS idx_migration_sources_run\n ON migration_sources(last_run_id, source_path);\n\nCREATE TABLE IF NOT EXISTS backup_runs (\n id TEXT NOT NULL PRIMARY KEY,\n created_at INTEGER NOT NULL,\n archive_path TEXT NOT NULL,\n status TEXT NOT NULL,\n manifest_json TEXT NOT NULL\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_backup_runs_created\n ON backup_runs(created_at DESC, id);\n\nCREATE TABLE IF NOT EXISTS worktrees (\n id TEXT NOT NULL PRIMARY KEY,\n repo_fingerprint TEXT NOT NULL,\n repo_root TEXT NOT NULL,\n path TEXT NOT NULL,\n branch TEXT NOT NULL,\n base_ref TEXT NOT NULL,\n owner_kind TEXT NOT NULL CHECK (owner_kind IN ('manual', 'workboard', 'session')),\n owner_id TEXT,\n snapshot_ref TEXT,\n provisioned_paths_json TEXT,\n created_at INTEGER NOT NULL,\n last_active_at INTEGER NOT NULL,\n removed_at INTEGER,\n run_end_cleanup_json TEXT,\n gc_protection_json TEXT\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_worktrees_repo_fingerprint\n ON worktrees(repo_fingerprint);\n\nCREATE INDEX IF NOT EXISTS idx_worktrees_removed_at\n ON worktrees(removed_at);\n\nCREATE TABLE IF NOT EXISTS worktree_provisioned_file_chunks (\n worktree_id TEXT NOT NULL,\n path TEXT NOT NULL,\n chunk_index INTEGER NOT NULL CHECK (chunk_index >= 0),\n data BLOB NOT NULL,\n PRIMARY KEY (worktree_id, path, chunk_index)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS worktree_templates (\n cache_key TEXT NOT NULL PRIMARY KEY,\n id TEXT NOT NULL UNIQUE,\n repo_root TEXT NOT NULL,\n common_dir TEXT NOT NULL,\n worktree_root TEXT NOT NULL,\n path TEXT NOT NULL,\n backend TEXT NOT NULL,\n source_commit TEXT NOT NULL,\n content_key TEXT NOT NULL,\n status TEXT NOT NULL CHECK (status IN ('preparing', 'ready')),\n created_at INTEGER NOT NULL,\n last_used_at INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT NOT NULL PRIMARY KEY,\n display_name TEXT NOT NULL,\n repo_root TEXT NOT NULL,\n origin_url TEXT,\n source TEXT NOT NULL CHECK (source IN ('registered', 'cloned')),\n created_at_ms INT NOT NULL,\n updated_at_ms INT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS user_preferences (\n profile_id TEXT NOT NULL,\n pref_key TEXT NOT NULL,\n value_json TEXT NOT NULL,\n updated_at_ms INT NOT NULL,\n PRIMARY KEY (profile_id, pref_key)\n) STRICT;\n\n-- Gateway-owned custom session group catalog (names + display order).\n-- Membership stays on each session entry's category field; this table only\n-- owns which groups exist and how operator UIs order them.\nCREATE TABLE IF NOT EXISTS session_groups (\n name TEXT NOT NULL PRIMARY KEY,\n position INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n cwd TEXT,\n worktree INTEGER\n) STRICT;\n\n-- Gateway-owned durable cloud worker lifecycle. Provider-specific execution\n-- stays in plugins; this table records only core reconciliation facts.\nCREATE TABLE IF NOT EXISTS worker_environments (\n environment_id TEXT NOT NULL PRIMARY KEY,\n provider_id TEXT NOT NULL,\n profile_id TEXT NOT NULL,\n profile_snapshot_json TEXT NOT NULL,\n last_activated_at_ms INTEGER,\n preparation_key TEXT,\n preparation_purpose TEXT,\n preparation_demand_at_ms INTEGER,\n preparation_expires_at_ms INTEGER,\n preparation_consumed_at_ms INTEGER CHECK (\n (preparation_key IS NULL AND preparation_demand_at_ms IS NULL\n AND preparation_expires_at_ms IS NULL AND preparation_consumed_at_ms IS NULL)\n OR\n (preparation_key IS NOT NULL AND length(preparation_key) = 64\n AND preparation_key NOT GLOB '*[^0-9a-f]*'\n AND preparation_demand_at_ms IS NOT NULL\n AND preparation_demand_at_ms BETWEEN 0 AND 9007199254740991\n AND preparation_expires_at_ms IS NOT NULL\n AND preparation_expires_at_ms > preparation_demand_at_ms\n AND preparation_expires_at_ms <= 9007199254740991\n AND (preparation_consumed_at_ms IS NULL\n OR (preparation_consumed_at_ms >= preparation_demand_at_ms\n AND preparation_consumed_at_ms < preparation_expires_at_ms)))\n ),\n provision_operation_id TEXT NOT NULL UNIQUE,\n lease_id TEXT,\n node_setup_id TEXT,\n node_device_id TEXT,\n ssh_host TEXT,\n ssh_port INTEGER CHECK (ssh_port IS NULL OR (ssh_port >= 1 AND ssh_port <= 65535)),\n ssh_user TEXT,\n ssh_host_key TEXT,\n ssh_key_ref_json TEXT,\n desktop_json TEXT,\n state TEXT NOT NULL CHECK (\n state IN (\n 'requested',\n 'provisioning',\n 'bootstrapping',\n 'ready',\n 'attached',\n 'idle',\n 'draining',\n 'destroying',\n 'destroyed',\n 'failed',\n 'orphaned'\n )\n ),\n bootstrap_bundle_hash TEXT,\n bootstrap_openclaw_version TEXT,\n bootstrap_protocol_features_json TEXT,\n bootstrap_install_kind TEXT,\n owner_epoch INTEGER NOT NULL DEFAULT 0 CHECK (owner_epoch >= 0),\n teardown_terminal_state TEXT CHECK (teardown_terminal_state IN ('destroyed', 'failed')),\n attached_session_ids_json TEXT NOT NULL DEFAULT '[]',\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n state_changed_at_ms INTEGER NOT NULL,\n idle_since_at_ms INTEGER,\n destroy_requested_at_ms INTEGER,\n last_error TEXT,\n shared_host INTEGER\n) STRICT;\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_worker_environments_provider_lease\n ON worker_environments(provider_id, lease_id)\n WHERE lease_id IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS idx_worker_environments_terminal_changed\n ON worker_environments(state_changed_at_ms, environment_id);\n\n-- A dedicated node registers its fixed build paths before ready, then binds\n-- them once. The environment belongs to the Gateway's separate database.\nCREATE TABLE IF NOT EXISTS node_worker_prepared_workspaces (\n preparation_key TEXT NOT NULL PRIMARY KEY CHECK (\n length(preparation_key) = 64 AND preparation_key NOT GLOB '*[^0-9a-f]*'\n ),\n cache_key TEXT NOT NULL CHECK (\n length(cache_key) = 64 AND cache_key NOT GLOB '*[^0-9a-f]*'\n ),\n gateway_namespace TEXT NOT NULL CHECK (length(gateway_namespace) > 0),\n workspace_dir TEXT NOT NULL UNIQUE CHECK (length(workspace_dir) > 0),\n home_dir TEXT NOT NULL CHECK (length(home_dir) > 0),\n source_manifest_ref TEXT NOT NULL CHECK (\n length(source_manifest_ref) = 71 AND substr(source_manifest_ref, 1, 7) = 'sha256:'\n AND substr(source_manifest_ref, 8) NOT GLOB '*[^0-9a-f]*'\n ),\n prepared_manifest_ref TEXT NOT NULL CHECK (\n length(prepared_manifest_ref) = 71 AND substr(prepared_manifest_ref, 1, 7) = 'sha256:'\n AND substr(prepared_manifest_ref, 8) NOT GLOB '*[^0-9a-f]*'\n ),\n state TEXT NOT NULL CHECK (state IN ('available', 'bound', 'retiring', 'retired')),\n environment_id TEXT NOT NULL CHECK (length(environment_id) > 0),\n session_id TEXT,\n session_key TEXT,\n owner_epoch INTEGER CHECK (owner_epoch BETWEEN 1 AND 9007199254740991),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms BETWEEN 0 AND 9007199254740991),\n bound_at_ms INTEGER CHECK (bound_at_ms BETWEEN created_at_ms AND 9007199254740991),\n retired_at_ms INTEGER CHECK (\n retired_at_ms BETWEEN coalesce(bound_at_ms, created_at_ms) AND 9007199254740991\n ),\n CHECK (\n (session_id IS NULL AND session_key IS NULL AND owner_epoch IS NULL AND bound_at_ms IS NULL)\n OR\n (session_id IS NOT NULL AND length(session_id) > 0\n AND session_key IS NOT NULL AND length(session_key) > 0\n AND owner_epoch IS NOT NULL AND bound_at_ms IS NOT NULL)\n ),\n CHECK (\n (state = 'available' AND bound_at_ms IS NULL AND retired_at_ms IS NULL)\n OR (state = 'bound' AND bound_at_ms IS NOT NULL AND retired_at_ms IS NULL)\n OR (state = 'retiring' AND retired_at_ms IS NULL)\n OR (state = 'retired' AND retired_at_ms IS NOT NULL)\n )\n) STRICT;\n\n-- Provider-advertised fallback ports preserve stable retry order separately\n-- from the downgrade-sensitive canonical worker environment row.\nCREATE TABLE IF NOT EXISTS worker_environment_ssh_fallback_ports (\n environment_id TEXT NOT NULL,\n position INTEGER NOT NULL CHECK (position >= 0 AND position <= 9),\n port INTEGER NOT NULL CHECK (port >= 1 AND port <= 65535),\n PRIMARY KEY (environment_id, position),\n UNIQUE (environment_id, port),\n FOREIGN KEY (environment_id) REFERENCES worker_environments(environment_id) ON DELETE CASCADE\n) STRICT;\n\n-- Logical sessions own repository intent and accepted artifact references,\n-- independently of the worker or rotating transcript session id.\nCREATE TABLE IF NOT EXISTS session_repository_workspaces (\n workspace_id TEXT NOT NULL PRIMARY KEY CHECK (length(workspace_id) = 36),\n agent_id TEXT NOT NULL CHECK (length(agent_id) BETWEEN 1 AND 128),\n session_key TEXT NOT NULL CHECK (length(session_key) BETWEEN 1 AND 1024),\n url TEXT NOT NULL CHECK (length(url) BETWEEN 1 AND 4096),\n requested_ref TEXT CHECK (requested_ref IS NULL OR length(requested_ref) BETWEEN 1 AND 1024),\n run_setup_script INTEGER NOT NULL DEFAULT 0 CHECK (run_setup_script IN (0, 1)),\n base_commit TEXT CHECK (base_commit IS NULL OR length(base_commit) IN (40, 64)),\n base_manifest_hash TEXT,\n branch TEXT NOT NULL CHECK (length(branch) BETWEEN 1 AND 256),\n checkpoint_ref TEXT,\n manifest_hash TEXT,\n revision INTEGER NOT NULL CHECK (revision >= 0),\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n UNIQUE (agent_id, session_key),\n CHECK (base_manifest_hash IS NULL OR base_commit IS NOT NULL),\n CHECK ((checkpoint_ref IS NULL AND manifest_hash IS NULL)\n OR (checkpoint_ref IS NOT NULL AND manifest_hash IS NOT NULL AND base_manifest_hash IS NOT NULL))\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS github_repository_publication_requests (\n request_id TEXT NOT NULL PRIMARY KEY,\n owner_profile_id TEXT,\n connection_generation TEXT,\n idempotency_key TEXT NOT NULL,\n request_digest TEXT NOT NULL,\n session_id TEXT NOT NULL,\n session_lifecycle_revision TEXT,\n requester_authority_json TEXT,\n session_key TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n workspace_id TEXT NOT NULL,\n checkpoint_ref TEXT,\n checkpoint_digest TEXT,\n claim_id TEXT,\n run_id TEXT,\n environment_id TEXT,\n owner_epoch INTEGER,\n placement_generation INTEGER,\n identity_source TEXT NOT NULL CHECK (identity_source IN ('system-detected', 'system-configured', 'agent-override', 'personal')),\n identity_profile_id TEXT,\n identity_account_id INTEGER NOT NULL,\n identity_login TEXT NOT NULL,\n title TEXT,\n body TEXT,\n status TEXT NOT NULL CHECK (status IN ('requested', 'publishing', 'needs_confirmation', 'published', 'failed')),\n gateway_instance_id TEXT,\n execution_id TEXT,\n last_effect TEXT CHECK (last_effect IN ('push', 'pull_request')),\n effect_state TEXT CHECK (effect_state IN ('dispatched', 'observed')),\n push_repository TEXT,\n repository TEXT,\n branch TEXT NOT NULL,\n base_branch TEXT,\n source_head_commit TEXT,\n source_index_tree TEXT,\n workspace_tree TEXT,\n previous_head_commit TEXT,\n pushed_head_commit TEXT,\n head_commit TEXT,\n pull_request_url TEXT,\n error_code TEXT,\n next_action TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n reported_at_ms INTEGER,\n CHECK ((identity_source = 'personal' AND owner_profile_id IS NOT NULL AND connection_generation IS NOT NULL)\n OR (identity_source <> 'personal' AND owner_profile_id IS NULL AND connection_generation IS NULL)),\n CHECK ((checkpoint_ref IS NULL AND checkpoint_digest IS NULL) OR (checkpoint_ref IS NOT NULL AND checkpoint_digest IS NOT NULL)),\n CHECK ((last_effect IS NULL AND effect_state IS NULL) OR (last_effect IS NOT NULL AND effect_state IS NOT NULL))\n) STRICT;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_github_repository_publication_shared_request\n ON github_repository_publication_requests(session_id, idempotency_key) WHERE owner_profile_id IS NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_github_repository_publication_personal_request\n ON github_repository_publication_requests(owner_profile_id, session_id, idempotency_key) WHERE owner_profile_id IS NOT NULL;\n\n-- Session placement lives in the shared state database so local admission,\n-- worker admission, and environment attachment use one durable authority.\nCREATE TABLE IF NOT EXISTS worker_session_placements (\n session_id TEXT NOT NULL PRIMARY KEY,\n agent_id TEXT NOT NULL,\n session_key TEXT NOT NULL,\n execution_mode TEXT CHECK (execution_mode IN ('worker-turn', 'remote-exec')),\n state TEXT NOT NULL CHECK (\n state IN (\n 'local',\n 'requested',\n 'provisioning',\n 'syncing',\n 'starting',\n 'active',\n 'draining',\n 'reconciling',\n 'reclaimed',\n 'failed'\n )\n ),\n environment_id TEXT,\n transition_generation INTEGER NOT NULL DEFAULT 0 CHECK (transition_generation >= 0),\n active_owner_epoch INTEGER CHECK (active_owner_epoch IS NULL OR active_owner_epoch >= 1),\n workspace_base_manifest_ref TEXT,\n remote_workspace_dir TEXT,\n worker_bundle_hash TEXT,\n last_transcript_ack_cursor INTEGER CHECK (\n last_transcript_ack_cursor IS NULL OR last_transcript_ack_cursor >= 0\n ),\n last_live_event_ack_cursor INTEGER CHECK (\n last_live_event_ack_cursor IS NULL OR last_live_event_ack_cursor >= 0\n ),\n recovery_error TEXT,\n turn_claim_owner TEXT CHECK (turn_claim_owner IN ('local', 'worker')),\n turn_claim_id TEXT,\n turn_claim_run_id TEXT,\n turn_claim_generation INTEGER CHECK (\n turn_claim_generation IS NULL OR turn_claim_generation >= 0\n ),\n turn_claim_owner_epoch INTEGER CHECK (\n turn_claim_owner_epoch IS NULL OR turn_claim_owner_epoch >= 1\n ),\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n state_changed_at_ms INTEGER NOT NULL,\n terminal_reason TEXT,\n terminal_at_ms INTEGER,\n CHECK (\n (state IN ('local', 'requested')\n AND environment_id IS NULL AND active_owner_epoch IS NULL\n AND workspace_base_manifest_ref IS NULL AND remote_workspace_dir IS NULL\n AND worker_bundle_hash IS NULL\n AND last_transcript_ack_cursor IS NULL AND last_live_event_ack_cursor IS NULL\n AND recovery_error IS NULL)\n OR\n (state IS 'provisioning'\n AND active_owner_epoch IS NULL\n AND workspace_base_manifest_ref IS NULL AND remote_workspace_dir IS NULL\n AND worker_bundle_hash IS NULL\n AND last_transcript_ack_cursor IS NULL AND last_live_event_ack_cursor IS NULL\n AND recovery_error IS NULL)\n OR\n (state IS 'syncing'\n AND environment_id IS NOT NULL AND active_owner_epoch IS NULL\n AND workspace_base_manifest_ref IS NULL AND remote_workspace_dir IS NULL\n AND worker_bundle_hash IS NOT NULL\n AND last_transcript_ack_cursor IS NULL AND last_live_event_ack_cursor IS NULL\n AND recovery_error IS NULL)\n OR\n (state IS 'starting'\n AND environment_id IS NOT NULL AND active_owner_epoch IS NULL\n AND workspace_base_manifest_ref IS NOT NULL AND remote_workspace_dir IS NOT NULL\n AND worker_bundle_hash IS NOT NULL\n AND last_transcript_ack_cursor IS NULL AND last_live_event_ack_cursor IS NULL\n AND recovery_error IS NULL)\n OR\n (state IN ('active', 'draining', 'reconciling')\n AND environment_id IS NOT NULL AND active_owner_epoch IS NOT NULL\n AND workspace_base_manifest_ref IS NOT NULL AND remote_workspace_dir IS NOT NULL\n AND worker_bundle_hash IS NOT NULL AND recovery_error IS NULL)\n OR\n (state IS 'reclaimed'\n AND environment_id IS NOT NULL AND active_owner_epoch IS NOT NULL\n AND workspace_base_manifest_ref IS NOT NULL AND remote_workspace_dir IS NOT NULL\n AND worker_bundle_hash IS NOT NULL AND recovery_error IS NULL\n AND turn_claim_owner IS NULL AND turn_claim_id IS NULL AND turn_claim_run_id IS NULL\n AND turn_claim_generation IS NULL AND turn_claim_owner_epoch IS NULL)\n OR\n (state IS 'failed' AND recovery_error IS NOT NULL)\n ),\n CHECK (\n (turn_claim_owner IS NULL AND turn_claim_id IS NULL AND turn_claim_run_id IS NULL\n AND turn_claim_generation IS NULL AND turn_claim_owner_epoch IS NULL)\n OR\n (turn_claim_owner IS 'local' AND turn_claim_id IS NOT NULL\n AND turn_claim_run_id IS NOT NULL AND turn_claim_generation IS NOT NULL\n AND turn_claim_owner_epoch IS NULL)\n OR\n (turn_claim_owner IS 'worker' AND turn_claim_id IS NOT NULL\n AND turn_claim_run_id IS NOT NULL AND turn_claim_generation IS NOT NULL\n AND turn_claim_owner_epoch IS NOT NULL)\n ),\n CHECK (\n turn_claim_owner IS NULL\n OR\n (turn_claim_owner IS 'local' AND (\n state IN ('local', 'requested', 'failed')\n OR (state IN ('active', 'draining') AND execution_mode IS 'remote-exec')\n ))\n OR\n (turn_claim_owner IS 'worker' AND state IN ('active', 'draining')\n AND (execution_mode IS NULL OR execution_mode IS 'worker-turn')\n AND turn_claim_owner_epoch IS active_owner_epoch)\n )\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_worker_session_placements_session_key\n ON worker_session_placements(agent_id, session_key);\n\nCREATE INDEX IF NOT EXISTS idx_worker_session_placements_reconcile\n ON worker_session_placements(updated_at_ms, session_id);\n\nCREATE INDEX IF NOT EXISTS idx_worker_session_placements_environment\n ON worker_session_placements(environment_id)\n WHERE environment_id IS NOT NULL;\n\n-- Planned placement moves retain their exact source CAS and bounded target\n-- without widening the stable placement-state vocabulary. The opaque operation\n-- id fences stale asynchronous completion; it is correlation, never authority.\nCREATE TABLE IF NOT EXISTS worker_session_placement_moves (\n operation_id TEXT NOT NULL PRIMARY KEY,\n session_id TEXT NOT NULL UNIQUE\n REFERENCES worker_session_placements(session_id) ON DELETE CASCADE,\n source_generation INTEGER NOT NULL CHECK (source_generation >= 0),\n source_environment_id TEXT NOT NULL CHECK (\n length(source_environment_id) BETWEEN 1 AND 256\n AND source_environment_id = trim(source_environment_id)\n ),\n source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch >= 1),\n target_kind TEXT NOT NULL CHECK (target_kind IN ('gateway', 'profile', 'device')),\n target_id TEXT,\n -- Keep these nullable columns constraint-free so lazy ALTER TABLE produces the\n -- same shape as fresh databases; placement-move code validates their values.\n target_machine_class TEXT,\n target_os TEXT,\n -- Explicit source abandonment is a durable operator decision. Keep the bit\n -- bare and nullable so same-version older readers can safely omit it.\n abandon_source INTEGER,\n last_error TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n CHECK (\n (target_kind IS 'gateway' AND target_id IS NULL)\n OR\n (target_kind IN ('profile', 'device')\n AND target_id IS NOT NULL\n AND length(target_id) BETWEEN 1 AND 256\n AND target_id = trim(target_id))\n )\n) STRICT;\n\n-- Worker-visible session RPC authority is persisted against the exact turn\n-- claim. The launch descriptor is informative only; Gateway dispatch always\n-- revalidates this record and the live placement claim before executing.\nCREATE TABLE IF NOT EXISTS worker_turn_tool_authorities (\n session_id TEXT NOT NULL PRIMARY KEY,\n environment_id TEXT NOT NULL,\n owner_epoch INTEGER NOT NULL CHECK (owner_epoch >= 1),\n placement_generation INTEGER NOT NULL CHECK (placement_generation >= 0),\n claim_id TEXT NOT NULL,\n run_id TEXT NOT NULL,\n tool_names_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES worker_session_placements(session_id) ON DELETE CASCADE\n) STRICT;\n\n-- Tool-call ids are idempotency keys only within one exact source turn claim.\n-- A running operation from another Gateway instance is ambiguous and is never\n-- replayed. A persisted random seed separates durable downstream identities\n-- from Gateway authentication keys and survives ordinary process restarts.\nCREATE TABLE IF NOT EXISTS worker_session_tool_operations (\n source_session_id TEXT NOT NULL,\n source_claim_id TEXT NOT NULL,\n tool_call_id TEXT NOT NULL,\n tool_name TEXT NOT NULL CHECK (tool_name IN ('sessions_spawn', 'sessions_send')),\n request_digest TEXT NOT NULL,\n operation_seed TEXT NOT NULL,\n status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'unknown')),\n child_session_key TEXT,\n result_json TEXT,\n gateway_instance_id TEXT NOT NULL,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (source_session_id, source_claim_id, tool_call_id),\n FOREIGN KEY (source_session_id)\n REFERENCES worker_session_placements(session_id) ON DELETE CASCADE\n) STRICT;\n\n-- Local sandbox execution is a projection of a session-owned managed worktree.\n-- No cascade: an older binary must not discard pending edits with a session row.\nCREATE TABLE IF NOT EXISTS local_workspace_projections (\n worktree_id TEXT NOT NULL PRIMARY KEY,\n agent_id TEXT NOT NULL,\n session_key TEXT NOT NULL,\n session_id TEXT NOT NULL,\n lifecycle_revision TEXT,\n projection_path TEXT NOT NULL UNIQUE,\n base_commit TEXT NOT NULL,\n source_paths_json TEXT NOT NULL,\n baseline_json TEXT,\n baseline_ref TEXT,\n pending_ref TEXT,\n pending_target TEXT CHECK (pending_target IN ('canonical', 'projection')),\n paused_runtimes_json TEXT,\n journal_json TEXT,\n journal_pack BLOB CHECK (journal_pack IS NULL OR length(journal_pack) <= 268435456),\n revision INTEGER NOT NULL DEFAULT 0 CHECK (revision >= 0),\n created_at_ms INTEGER NOT NULL,\n CHECK ((baseline_json IS NULL) = (baseline_ref IS NULL)),\n CHECK ((journal_json IS NULL) = (journal_pack IS NULL))\n) STRICT;\n\n-- A reconciliation journal is written before managed-worktree mutation. The\n-- bounded Git base snapshot repairs any subset left by an interrupted apply.\nCREATE TABLE IF NOT EXISTS worker_workspace_reconciliations (\n session_id TEXT NOT NULL PRIMARY KEY,\n environment_id TEXT NOT NULL,\n owner_epoch INTEGER NOT NULL CHECK (owner_epoch >= 1),\n placement_generation INTEGER NOT NULL CHECK (placement_generation >= 0),\n base_manifest_ref TEXT NOT NULL,\n current_manifest_ref TEXT NOT NULL,\n plan_json TEXT NOT NULL,\n base_pack BLOB NOT NULL CHECK (length(base_pack) <= 268435456),\n created_at_ms INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES worker_session_placements(session_id) ON DELETE CASCADE\n) STRICT;\n\n-- A completed remote turn is fenced from stale-claim teardown until its\n-- workspace result is durably reconciled into the managed worktree.\nCREATE TABLE IF NOT EXISTS worker_workspace_pending_results (\n session_id TEXT NOT NULL PRIMARY KEY,\n environment_id TEXT NOT NULL,\n owner_epoch INTEGER NOT NULL CHECK (owner_epoch >= 1),\n placement_generation INTEGER NOT NULL CHECK (placement_generation >= 0),\n claim_id TEXT NOT NULL,\n run_id TEXT NOT NULL,\n gateway_instance_id TEXT NOT NULL,\n recovery_requested_at_ms INTEGER,\n workspace_accepted_at_ms INTEGER,\n staged_result_ref TEXT,\n repository_workspace_id TEXT,\n created_at_ms INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES worker_session_placements(session_id) ON DELETE CASCADE\n) STRICT;\n\n-- GitHub publication intent records the authoritative session worktree. Cloud\n-- requests execute only after the exact turn claim's result is accepted locally.\n-- Secrets stay in the effective Gateway-owned GitHub profile and never enter\n-- this row or the worker protocol.\nCREATE TABLE IF NOT EXISTS github_publication_requests (\n request_id TEXT NOT NULL PRIMARY KEY,\n idempotency_key TEXT NOT NULL,\n request_digest TEXT NOT NULL,\n session_id TEXT NOT NULL,\n session_key TEXT NOT NULL,\n agent_id TEXT NOT NULL,\n worktree_id TEXT NOT NULL,\n repository_fingerprint TEXT NOT NULL,\n claim_id TEXT,\n run_id TEXT,\n environment_id TEXT,\n owner_epoch INTEGER CHECK (owner_epoch IS NULL OR owner_epoch >= 1),\n placement_generation INTEGER CHECK (\n placement_generation IS NULL OR placement_generation >= 0\n ),\n identity_source TEXT NOT NULL CHECK (\n identity_source IN ('system-detected', 'system-configured', 'agent-override')\n ),\n identity_profile_id TEXT,\n identity_account_id INTEGER NOT NULL CHECK (identity_account_id >= 1),\n identity_login TEXT NOT NULL,\n title TEXT,\n body TEXT,\n status TEXT NOT NULL CHECK (\n status IN ('requested', 'publishing', 'published', 'failed')\n ),\n gateway_instance_id TEXT,\n repository TEXT,\n branch TEXT NOT NULL,\n base_branch TEXT,\n source_head_commit TEXT,\n source_index_tree TEXT,\n workspace_tree TEXT,\n head_commit TEXT,\n pull_request_url TEXT,\n error_code TEXT,\n next_action TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n reported_at_ms INTEGER,\n UNIQUE (session_id, idempotency_key),\n CHECK (\n (claim_id IS NULL AND run_id IS NULL AND environment_id IS NULL\n AND owner_epoch IS NULL AND placement_generation IS NULL)\n OR\n (claim_id IS NOT NULL AND run_id IS NOT NULL AND placement_generation IS NOT NULL\n AND ((environment_id IS NULL AND owner_epoch IS NULL)\n OR (environment_id IS NOT NULL AND owner_epoch IS NOT NULL)))\n ),\n CHECK (\n (identity_source IS 'system-detected' AND identity_profile_id IS NULL)\n OR\n (identity_source IN ('system-configured', 'agent-override')\n AND identity_profile_id IS NOT NULL)\n ),\n CHECK (\n (source_head_commit IS NULL AND source_index_tree IS NULL AND workspace_tree IS NULL)\n OR\n (source_head_commit IS NOT NULL AND workspace_tree IS NOT NULL)\n ),\n CHECK (\n (status IS 'published' AND pull_request_url IS NOT NULL AND error_code IS NULL\n AND next_action IS NULL)\n OR\n (status IS 'failed' AND pull_request_url IS NULL AND error_code IS NOT NULL\n AND next_action IS NOT NULL)\n OR\n (status IN ('requested', 'publishing') AND pull_request_url IS NULL\n AND error_code IS NULL AND next_action IS NULL)\n )\n) STRICT;\n\nCREATE INDEX IF NOT EXISTS idx_github_publication_requests_pending\n ON github_publication_requests(status, updated_at_ms, request_id);\n\n-- Personal requests cannot be interpreted or resumed by older shared publishers.\nCREATE TABLE IF NOT EXISTS github_personal_publication_requests (\n request_id TEXT NOT NULL PRIMARY KEY CHECK (length(request_id) = 36),\n owner_profile_id TEXT NOT NULL CHECK (length(owner_profile_id) BETWEEN 1 AND 128),\n connection_generation TEXT NOT NULL CHECK (length(connection_generation) = 36),\n idempotency_key TEXT NOT NULL CHECK (length(idempotency_key) BETWEEN 1 AND 128),\n request_digest TEXT NOT NULL CHECK (length(request_digest) = 64),\n session_id TEXT NOT NULL CHECK (length(session_id) BETWEEN 1 AND 128),\n session_key TEXT NOT NULL CHECK (length(session_key) BETWEEN 1 AND 1024),\n agent_id TEXT NOT NULL CHECK (length(agent_id) BETWEEN 1 AND 128),\n worktree_id TEXT NOT NULL CHECK (length(worktree_id) BETWEEN 1 AND 128),\n repository_fingerprint TEXT NOT NULL CHECK (length(repository_fingerprint) BETWEEN 1 AND 256),\n identity_source TEXT NOT NULL CHECK (identity_source = 'personal'),\n identity_profile_id TEXT NOT NULL CHECK (length(identity_profile_id) = 36),\n identity_account_id INTEGER NOT NULL CHECK (identity_account_id >= 1),\n identity_login TEXT NOT NULL CHECK (length(identity_login) BETWEEN 1 AND 39),\n title TEXT CHECK (title IS NULL OR length(title) BETWEEN 1 AND 256),\n body TEXT CHECK (body IS NULL OR length(body) BETWEEN 1 AND 8192),\n status TEXT NOT NULL CHECK (status IN ('requested', 'publishing', 'needs_confirmation', 'published', 'failed')),\n gateway_instance_id TEXT CHECK (gateway_instance_id IS NULL OR length(gateway_instance_id) BETWEEN 1 AND 128),\n execution_id TEXT CHECK (execution_id IS NULL OR length(execution_id) = 36),\n last_effect TEXT CHECK (last_effect IS NULL OR last_effect IN ('push', 'pull_request')),\n effect_state TEXT CHECK (effect_state IS NULL OR effect_state IN ('dispatched', 'observed')),\n push_repository TEXT NOT NULL CHECK (length(push_repository) BETWEEN 3 AND 256),\n repository TEXT NOT NULL CHECK (length(repository) BETWEEN 3 AND 256),\n branch TEXT NOT NULL CHECK (length(branch) BETWEEN 1 AND 256),\n base_branch TEXT NOT NULL CHECK (length(base_branch) BETWEEN 1 AND 256),\n source_head_commit TEXT NOT NULL CHECK (length(source_head_commit) IN (40, 64)),\n source_index_tree TEXT NOT NULL CHECK (length(source_index_tree) IN (40, 64)),\n workspace_tree TEXT NOT NULL CHECK (length(workspace_tree) IN (40, 64)),\n head_commit TEXT CHECK (head_commit IS NULL OR length(head_commit) IN (40, 64)),\n pull_request_url TEXT CHECK (pull_request_url IS NULL OR length(pull_request_url) BETWEEN 1 AND 2048),\n error_code TEXT CHECK (error_code IS NULL OR length(error_code) BETWEEN 1 AND 64),\n next_action TEXT CHECK (next_action IS NULL OR length(next_action) BETWEEN 1 AND 1024),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= created_at_ms),\n reported_at_ms INTEGER,\n UNIQUE (owner_profile_id, session_id, idempotency_key),\n CHECK ((status = 'publishing' AND gateway_instance_id IS NOT NULL AND execution_id IS NOT NULL) OR status <> 'publishing'),\n CHECK ((last_effect IS NULL AND effect_state IS NULL) OR (last_effect IS NOT NULL AND effect_state IS NOT NULL)),\n CHECK ((status = 'published' AND pull_request_url IS NOT NULL AND head_commit IS NOT NULL AND error_code IS NULL AND next_action IS NULL)\n OR (status = 'failed' AND error_code IS NOT NULL AND next_action IS NOT NULL)\n OR (status IN ('requested', 'publishing', 'needs_confirmation') AND error_code IS NULL AND next_action IS NULL))\n) STRICT;\nCREATE INDEX IF NOT EXISTS idx_github_personal_publication_owner_session\n ON github_personal_publication_requests(owner_profile_id, session_id, created_at_ms);\nCREATE INDEX IF NOT EXISTS idx_github_personal_publication_pending\n ON github_personal_publication_requests(status, updated_at_ms, request_id);\n\n-- Older readers validate both local receipt tables exactly. Their immutable\n-- lifecycle binding stays in a first-use companion so those schemas remain readable.\nCREATE TABLE IF NOT EXISTS github_publication_session_lifecycles (\n publication_kind TEXT NOT NULL CHECK (publication_kind IN ('shared', 'personal')),\n request_id TEXT NOT NULL,\n lifecycle_revision TEXT,\n requester_authority_json TEXT,\n PRIMARY KEY (publication_kind, request_id)\n) STRICT;\n\n-- One active, opaque admission credential per worker environment. Plaintext\n-- may be retried until delivery acknowledgement but never enters durable state.\nCREATE TABLE IF NOT EXISTS worker_environment_credentials (\n environment_id TEXT NOT NULL PRIMARY KEY,\n credential_hash TEXT NOT NULL UNIQUE,\n bundle_hash TEXT NOT NULL,\n session_id TEXT,\n rpc_set_version INTEGER NOT NULL CHECK (rpc_set_version >= 1),\n owner_epoch INTEGER NOT NULL CHECK (owner_epoch >= 0),\n expires_at_ms INTEGER NOT NULL CHECK (expires_at_ms >= 0),\n delivered_at_ms INTEGER CHECK (delivered_at_ms >= 0),\n FOREIGN KEY (environment_id) REFERENCES worker_environments(environment_id) ON DELETE CASCADE\n) STRICT;\n\n-- One durable sequence cursor per attached session owner epoch. The environment\n-- binding prevents independent workers with coincident epochs from sharing replay state.\nCREATE TABLE IF NOT EXISTS worker_transcript_commit_heads (\n session_id TEXT NOT NULL,\n run_epoch INTEGER NOT NULL CHECK (run_epoch >= 0),\n environment_id TEXT NOT NULL,\n next_seq INTEGER NOT NULL CHECK (next_seq >= 1),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= 0),\n PRIMARY KEY (session_id, run_epoch)\n) STRICT;\n\n-- Pending rows preserve a claimed request across gateway restarts. Terminal rows\n-- cache the exact result returned for deterministic at-least-once replay.\nCREATE TABLE IF NOT EXISTS worker_transcript_commits (\n session_id TEXT NOT NULL,\n run_epoch INTEGER NOT NULL CHECK (run_epoch >= 0),\n seq INTEGER NOT NULL CHECK (seq >= 1),\n request_hash TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('pending', 'terminal')),\n result_json TEXT,\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= 0),\n PRIMARY KEY (session_id, run_epoch, seq),\n FOREIGN KEY (session_id, run_epoch)\n REFERENCES worker_transcript_commit_heads(session_id, run_epoch)\n ON DELETE CASCADE,\n CHECK (\n (state = 'pending' AND result_json IS NULL) OR\n (state = 'terminal' AND result_json IS NOT NULL)\n )\n) STRICT;\n\n-- Pending rows preserve a claimed inference turn across gateway restarts.\n-- Terminal rows cache the exact outcome returned for deterministic replay.\nCREATE TABLE IF NOT EXISTS worker_inference_turns (\n session_id TEXT NOT NULL,\n run_epoch INTEGER NOT NULL CHECK (run_epoch >= 0),\n run_id TEXT NOT NULL,\n turn_id TEXT NOT NULL,\n environment_id TEXT NOT NULL,\n request_hash TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('pending', 'terminal')),\n terminal_json TEXT,\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= 0),\n PRIMARY KEY (session_id, run_epoch, run_id, turn_id),\n FOREIGN KEY (environment_id) REFERENCES worker_environments(environment_id) ON DELETE CASCADE,\n CHECK (\n (state = 'pending' AND terminal_json IS NULL) OR\n (state = 'terminal' AND terminal_json IS NOT NULL)\n )\n) STRICT;\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_worker_inference_turns_pending_run\n ON worker_inference_turns(session_id, run_epoch, run_id)\n WHERE state = 'pending';\n\nCREATE TABLE IF NOT EXISTS fleet_cells (\n tenant_id TEXT NOT NULL PRIMARY KEY,\n created_at_ms INTEGER NOT NULL,\n image TEXT NOT NULL,\n runtime TEXT NOT NULL,\n host_port INTEGER NOT NULL,\n container_name TEXT NOT NULL,\n data_dir TEXT NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS claw_installs (\n agent_id TEXT NOT NULL PRIMARY KEY,\n schema_version TEXT NOT NULL,\n source_kind TEXT NOT NULL,\n claw_name TEXT NOT NULL,\n claw_version TEXT NOT NULL,\n package_root TEXT NOT NULL,\n manifest_path TEXT NOT NULL,\n integrity_kind TEXT NOT NULL,\n integrity TEXT NOT NULL,\n source_byte_length INTEGER NOT NULL,\n manifest_schema_version INTEGER NOT NULL,\n plan_integrity TEXT NOT NULL,\n workspace TEXT NOT NULL UNIQUE,\n agent_config_digest TEXT NOT NULL,\n agent_owned_paths_json TEXT NOT NULL,\n bootstrap_source_path TEXT,\n bootstrap_content_digest TEXT,\n status TEXT NOT NULL CHECK (\n status IN ('pending', 'workspace_ready', 'config_committed', 'complete', 'partial')\n ),\n added_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS claw_workspace_files (\n agent_id TEXT NOT NULL,\n target_path TEXT NOT NULL,\n schema_version TEXT NOT NULL,\n workspace TEXT NOT NULL,\n source_path TEXT NOT NULL,\n content_digest TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (agent_id, target_path)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS claw_package_refs (\n agent_id TEXT NOT NULL,\n package_kind TEXT NOT NULL,\n package_source TEXT NOT NULL,\n package_ref TEXT NOT NULL,\n package_version TEXT NOT NULL,\n package_integrity TEXT NOT NULL,\n schema_version TEXT NOT NULL,\n claw_name TEXT NOT NULL,\n package_status TEXT NOT NULL,\n relationship TEXT NOT NULL CHECK (relationship IN ('managed', 'referenced')),\n origin TEXT NOT NULL CHECK (origin IN ('claw-introduced', 'pre-existing')),\n independent_owner INTEGER NOT NULL CHECK (independent_owner IN (0, 1)),\n extension_id TEXT,\n extension_format TEXT,\n extension_detected_format TEXT,\n extension_mapped_json TEXT,\n extension_unavailable_json TEXT,\n extension_adapter_identity TEXT,\n installed_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (agent_id, package_kind, package_source, package_ref, package_version)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS claw_cron_refs (\n agent_id TEXT NOT NULL,\n manifest_id TEXT NOT NULL,\n schema_version TEXT NOT NULL,\n declaration_key TEXT NOT NULL UNIQUE,\n scheduler_job_id TEXT UNIQUE,\n status TEXT NOT NULL,\n job_json TEXT NOT NULL,\n error TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (agent_id, manifest_id)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS claw_mcp_server_refs (\n agent_id TEXT NOT NULL,\n name TEXT NOT NULL,\n schema_version TEXT NOT NULL,\n config_digest TEXT NOT NULL,\n relationship TEXT NOT NULL CHECK (relationship IN ('managed', 'referenced')),\n origin TEXT NOT NULL CHECK (origin IN ('claw-introduced', 'pre-existing')),\n independent_owner INTEGER NOT NULL DEFAULT 0 CHECK (independent_owner IN (0, 1)),\n status TEXT NOT NULL,\n error TEXT,\n created_at_ms INTEGER NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (agent_id, name)\n) STRICT;\n\nCREATE TABLE IF NOT EXISTS user_profile_identities (\n provider TEXT NOT NULL,\n subject TEXT NOT NULL,\n profile_id TEXT NOT NULL,\n canonical_login TEXT,\n created_at INTEGER NOT NULL,\n authorization_id TEXT,\n authorization_basis_json TEXT,\n PRIMARY KEY (provider, subject)\n) STRICT;\nCREATE INDEX IF NOT EXISTS idx_user_profile_identities_profile_id\n ON user_profile_identities(profile_id);\nCREATE UNIQUE INDEX IF NOT EXISTS idx_user_profile_identities_authorization\n ON user_profile_identities(authorization_id);\n\nCREATE TABLE IF NOT EXISTS outbound_media_provenance (\n realpath TEXT NOT NULL PRIMARY KEY,\n kind TEXT NOT NULL,\n version INTEGER NOT NULL,\n sha256 TEXT NOT NULL,\n size_bytes INTEGER NOT NULL,\n created_at_ms INTEGER NOT NULL\n) STRICT;\n\n-- scope_id is non-null because SQLite treats NULLs as distinct in unique indexes/PKs,\n-- which would allow duplicate team rows. This PK also avoids a rebuild for identity scope.\nCREATE TABLE IF NOT EXISTS secret_store_entries (\n scope_kind TEXT NOT NULL CHECK (scope_kind IN ('team', 'identity')),\n scope_id TEXT NOT NULL,\n name TEXT NOT NULL,\n value TEXT NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('secret', 'env')),\n created_at_ms INTEGER NOT NULL CHECK (created_at_ms >= 0),\n updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= 0),\n updated_by TEXT,\n deleted_at_ms INTEGER,\n allowed_hosts TEXT,\n CHECK ((scope_kind = 'team' AND scope_id = '') OR (scope_kind = 'identity' AND length(scope_id) > 0)),\n PRIMARY KEY (scope_kind, scope_id, name)\n) STRICT;\nCREATE INDEX IF NOT EXISTS secret_store_entries_live_idx\n ON secret_store_entries (scope_kind, scope_id, name) WHERE deleted_at_ms IS NULL;\n";
//#endregion
//#region src/state/openclaw-state-db-operator-approval-migration.ts
const COLUMNS = [
"approval_id",
"resolution_ref",
"kind",
"status",
"presentation_json",
"requested_by_device_id",
"requested_by_client_id",
"requested_by_device_token_auth",
"reviewer_device_ids_json",
"source_agent_id",
"source_session_key",
"source_session_id",
"source_run_id",
"source_tool_call_id",
"source_tool_name",
"audience_session_keys_json",
"runtime_epoch",
"created_at_ms",
"expires_at_ms",
"updated_at_ms",
"decision",
"terminal_reason",
"resolved_at_ms",
"resolver_kind",
"resolver_id",
"consumed_at_ms",
"consumed_by"
];
function tableSql(db) {
const row = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'operator_approvals'").get();
return typeof row?.sql === "string" ? row.sql : void 0;
}
function hasCanonicalOperatorApprovalKinds(db) {
if (!tableExists(db, "operator_approvals")) return true;
return /kind\s+text\s+not\s+null\s+check\s*\(\s*kind\s+in\s*\(\s*'exec'\s*,\s*'plugin'\s*,\s*'system-agent'\s*\)\s*\)/.test(tableSql(db)?.toLowerCase() ?? "");
}
function assertCanonicalOperatorApprovalKinds(db, pathname) {
if (!hasCanonicalOperatorApprovalKinds(db)) throw new SqliteSchemaMismatchError(`OpenClaw state database ${pathname} has a legacy operator approval schema; run openclaw doctor --fix to migrate it.`);
}
function isCanonicalOperatorApprovalKind(value) {
return value === "exec" || value === "plugin" || value === "system-agent";
}
function detectOperatorApprovalSchemaMigration(db, path) {
return hasCanonicalOperatorApprovalKinds(db) ? [] : [{
kind: "operator-approvals-system-agent",
path
}];
}
function normalizeDdl(sql) {
return sql.replace(/\s+/g, " ").trim().replace(/;$/, "");
}
function canonicalOperatorApprovalCreateSql() {
const marker = "CREATE TABLE IF NOT EXISTS operator_approvals (";
const tableTerminator = "\n) STRICT;";
const start = OPENCLAW_STATE_SCHEMA_SQL.indexOf(marker);
const end = OPENCLAW_STATE_SCHEMA_SQL.indexOf(`${tableTerminator}\n\nCREATE INDEX IF NOT EXISTS idx_operator_approvals_status_expiry`, start);
if (start < 0 || end < 0) throw new Error("canonical operator approval schema is unavailable");
return OPENCLAW_STATE_SCHEMA_SQL.slice(start, end + 10);
}
function alterAppendedResolutionRefCreateSql(sql) {
const resolutionRefStart = sql.indexOf("\n resolution_ref ");
const followingColumnStart = sql.indexOf("\n kind ", resolutionRefStart);
const tailColumn = "\n consumed_by TEXT,";
const tailColumnStart = sql.indexOf(tailColumn, followingColumnStart);
if (resolutionRefStart < 0 || followingColumnStart < 0 || tailColumnStart < 0) throw new Error("canonical operator approval resolution reference schema is unavailable");
return (sql.slice(0, resolutionRefStart) + sql.slice(followingColumnStart)).replace(tailColumn, `${tailColumn} resolution_ref TEXT,`);
}
function hasExactLegacyOperatorApprovalSchema(db) {
const live = tableSql(db);
if (!live) return false;
const exactStrictLegacy = canonicalOperatorApprovalCreateSql().replace("CREATE TABLE IF NOT EXISTS operator_approvals (", "CREATE TABLE operator_approvals (").replace(/'exec',\s*'plugin',\s*'system-agent'/, "'exec', 'plugin'");
const normalizedLive = normalizeDdl(live);
return [exactStrictLegacy, alterAppendedResolutionRefCreateSql(exactStrictLegacy)].some((strictLegacy) => [strictLegacy, strictLegacy.replace(/\) STRICT;$/u, ");")].map(normalizeDdl).includes(normalizedLive));
}
function canonicalCreateSql() {
return canonicalOperatorApprovalCreateSql().replace("CREATE TABLE IF NOT EXISTS operator_approvals (", "CREATE TABLE operator_approvals_migration_new (");
}
function operatorApprovalIndexSql() {
const statements = OPENCLAW_STATE_SCHEMA_SQL.split(";").map((statement) => statement.trim()).filter((statement) => /^CREATE (?:UNIQUE )?INDEX IF NOT EXISTS idx_operator_approvals_/.test(statement));
if (statements.length === 0) throw new Error("canonical operator approval index schema is unavailable");
return `${statements.join(";\n")};`;
}
function repairOperatorApprovalKinds(db) {
if (hasCanonicalOperatorApprovalKinds(db) || tableExists(db, "operator_approvals_migration_new") || !hasExactLegacyOperatorApprovalSchema(db)) return false;
const columns = COLUMNS.join(", ");
runSqliteImmediateTransactionSync(db, () => {
db.exec(canonicalCreateSql());
db.exec(`
INSERT INTO operator_approvals_migration_new (${columns})
SELECT ${columns} FROM operator_approvals
WHERE typeof(resolution_ref) = 'text'
AND length(resolution_ref) = 43
AND resolution_ref NOT GLOB '*[^A-Za-z0-9_-]*';
DROP TABLE operator_approvals;
ALTER TABLE operator_approvals_migration_new RENAME TO operator_approvals;
`);
db.exec(operatorApprovalIndexSql());
});
return true;
}
function repairOperatorApprovalSchema(db) {
return repairOperatorApprovalKinds(db) ? ["Migrated shared state operator approvals → OpenClaw system changes"] : [];
}
//#endregion
//#region src/state/openclaw-state-db-legacy-backfills.ts
function ensureOperatorApprovalResolutionRefs(db) {
if (!tableExists(db, "operator_approvals")) return;
runSqliteImmediateTransactionSync(db, () => {
ensureColumn(db, "operator_approvals", "resolution_ref TEXT");
const rows = db.prepare("SELECT approval_id, kind, resolution_ref FROM operator_approvals").all();
const update = db.prepare("UPDATE operator_approvals SET resolution_ref = ? WHERE approval_id = ?");
for (const row of rows) {
if (typeof row.approval_id !== "string" || !isCanonicalOperatorApprovalKind(row.kind)) throw new Error("operator approval row cannot be assigned a transport reference");
const resolutionRef = buildApprovalResolutionRef({
approvalId: row.approval_id,
approvalKind: row.kind
});
if (row.resolution_ref !== resolutionRef) update.run(resolutionRef, row.approval_id);
}
if (db.prepare(`SELECT canonical.approval_id
FROM operator_approvals AS canonical
JOIN operator_approvals AS referenced
ON canonical.approval_id = referenced.resolution_ref
WHERE canonical.approval_id <> referenced.approval_id
LIMIT 1`).get()) throw new Error("operator approval ids conflict with durable transport references");
db.exec(`
CREATE UNIQUE INDEX IF NOT EXISTS idx_operator_approvals_resolution_ref
ON operator_approvals(resolution_ref);
`);
});
}
function nullableTextValue(record, key) {
if (!record || !Object.hasOwn(record, key)) return;
const value = record[key];
return typeof value === "string" || value === null ? value : void 0;
}
/** Promote shipped retained results before runtime hydrates canonical subagent state. */
function repairLegacySubagentRetainedResults(db) {
if (!tableExists(db, "subagent_runs")) return;
const repair = () => {
const hasLegacyPendingPayload = tableHasColumn(db, "subagent_runs", "pending_final_delivery_payload_json");
const rows = db.prepare(hasLegacyPendingPayload ? "SELECT run_id, payload_json, pending_final_delivery_payload_json FROM subagent_runs" : "SELECT run_id, payload_json FROM subagent_runs").all();
const updateRun = db.prepare(`UPDATE subagent_runs
SET payload_json = ?
WHERE run_id = ?`);
for (const row of rows) {
const stored = safeParseJsonRecord(row.payload_json) ?? null;
const parent = stored ? asNullableRecord(stored.parentCompletion) : null;
const payload = parent?.completionTarget === "parent" ? parent : stored;
const completion = payload ? asNullableRecord(payload.completion) : null;
if (!payload || !completion) continue;
const delivery = asNullableRecord(payload.delivery);
const deliveryPayload = delivery ? asNullableRecord(delivery.payload) : null;
const pendingPayload = row.pending_final_delivery_payload_json ? safeParseJsonRecord(row.pending_final_delivery_payload_json) ?? null : null;
if (!Boolean(deliveryPayload && (Object.hasOwn(deliveryPayload, "frozenResultText") || Object.hasOwn(deliveryPayload, "fallbackFrozenResultText")) || pendingPayload && (Object.hasOwn(pendingPayload, "frozenResultText") || Object.hasOwn(pendingPayload, "fallbackFrozenResultText")))) continue;
const legacyPrimary = nullableTextValue(deliveryPayload, "frozenResultText") ?? nullableTextValue(pendingPayload, "frozenResultText");
const legacyFallback = nullableTextValue(deliveryPayload, "fallbackFrozenResultText") ?? nullableTextValue(pendingPayload, "fallbackFrozenResultText");
if (nullableTextValue(completion, "resultText") == null && legacyPrimary !== void 0) completion.resultText = legacyPrimary;
if (nullableTextValue(completion, "fallbackResultText") == null && legacyFallback !== void 0) completion.fallbackResultText = legacyFallback;
delete deliveryPayload?.frozenResultText;
delete deliveryPayload?.fallbackFrozenResultText;
updateRun.run(JSON.stringify(stored), row.run_id);
}
};
if (db.isTransaction) {
repair();
return;
}
runSqliteImmediateTransactionSync(db, repair);
}
/** Canonicalize shipped subagent rows whose pause/kill owner only wrote root terminal fields. */
function repairLegacySubagentExecutionPayloads(db) {
if (!tableExists(db, "subagent_runs")) return;
db.exec(`
UPDATE subagent_runs
SET payload_json = json_remove(
CASE
WHEN json_extract(payload_json, '$.pauseReason') = 'sessions_yield'
AND json_extract(payload_json, '$.execution.status') <> 'terminal'
AND json_type(payload_json, '$.endedAt') IN ('integer', 'real')
THEN json_remove(json_set(
payload_json,
'$.execution.status', 'terminal',
'$.execution.endedAt', json_extract(payload_json, '$.endedAt')
), '$.execution.outcome')
WHEN (json_type(payload_json, '$.killReconciliation') = 'object'
OR json_extract(payload_json, '$.endedReason') = 'subagent-killed')
AND json_extract(payload_json, '$.execution.status') <> 'terminal'
AND json_type(payload_json, '$.endedAt') IN ('integer', 'real')
AND json_type(payload_json, '$.outcome') = 'object'
THEN json_set(
payload_json,
'$.execution.status', 'terminal',
'$.execution.endedAt', json_extract(payload_json, '$.endedAt'),
'$.execution.outcome', json_extract(payload_json, '$.outcome')
)
ELSE payload_json
END,
'$.startedAt', '$.endedAt', '$.outcome'
)
WHERE json_valid(payload_json)
AND (json_type(payload_json, '$.startedAt') IS NOT NULL
OR json_type(payload_json, '$.endedAt') IS NOT NULL
OR json_type(payload_json, '$.outcome') IS NOT NULL);
`);
}
/** Canonicalize the shipped suspension reason before runtime hydrates subagent state. */
function repairLegacySubagentSuspensionReasons(db) {
if (!tableExists(db, "subagent_runs")) return;
db.exec(`
UPDATE subagent_runs
SET payload_json = json_set(payload_json, '$.delivery.suspendedReason', 'permanent_failure')
WHERE json_valid(payload_json)
AND json_extract(payload_json, '$.delivery.suspendedReason') = 'retry-limit';
`);
}
function backfillAcpReplayEstimatedBytes(db) {
if (!tableExists(db, "acp_replay_events") || !tableHasColumn(db, "acp_replay_events", "estimated_bytes")) return;
const replayDb = getNodeSqliteKysely(db);
const updateEvent = db.prepare("UPDATE acp_replay_events SET estimated_bytes = ? WHERE session_id = ? AND seq = ?");
for (const row of iterateSqliteQuerySync(db, replayDb.selectFrom("acp_replay_events").select([
"session_id",
"seq",
"session_key",
"run_id",
"update_json",
"estimated_bytes"
]))) {
const expected = estimateAcpEventRowBytes({
sessionId: row.session_id,
sessionKey: row.session_key,
runId: row.run_id,
updateJson: row.update_json
});
if (coerceRequiredSqliteNumber(row.estimated_bytes) !== expected) updateEvent.run(expected, row.session_id, row.seq);
}
const updateSession = db.prepare("UPDATE acp_replay_sessions SET estimated_bytes = ? WHERE session_id = ?");
for (const row of iterateSqliteQuerySync(db, replayDb.selectFrom("acp_replay_sessions as s").select([
"s.session_id",
"s.session_key",
"s.cwd",
"s.estimated_bytes"
]).select((eb) => eb.fn.coalesce(eb.selectFrom("acp_replay_events as e").select((events) => events.fn.sum("e.estimated_bytes").as("total")).whereRef("e.session_id", "=", "s.session_id"), eb.val(0)).as("event_bytes")))) {
const expected = estimateAcpSessionRowBytes({
sessionId: row.session_id,
sessionKey: row.session_key,
cwd: row.cwd
}) + coerceRequiredSqliteNumber(row.event_bytes);
if (coerceRequiredSqliteNumber(row.estimated_bytes) !== expected) updateSession.run(expected, row.session_id);
}
}
function backfillCronRunLogEntryJson(db) {
if (!tableExists(db, "cron_run_logs") || !tableHasColumn(db, "cron_run_logs", "entry_json")) return;
const rows = db.prepare(`SELECT store_key, job_id, seq, ts
FROM cron_run_logs
WHERE entry_json = '{}'`).all();
if (rows.length === 0) return;
const update = db.prepare(`UPDATE cron_run_logs
SET entry_json = ?
WHERE store_key = ? AND job_id = ? AND seq = ?`);
for (const row of rows) update.run(JSON.stringify({
ts: coerceRequiredSqliteNumber(row.ts),
jobId: row.job_id,
action: "finished"
}), row.store_key, row.job_id, row.seq);
}
function textField(record, key) {
const value = record?.[key];
return typeof value === "string" && value.trim() ? value : null;
}
function backfillCronJobsFromJobJson(db) {
if (!tableExists(db, "cron_jobs") || !tableHasColumn(db, "cron_jobs", "job_json") || !tableHasColumn(db, "cron_jobs", "payload_kind")) return;
const rows = db.prepare(`SELECT store_key, job_id, job_json, updated_at
FROM cron_jobs
WHERE payload_kind = 'message'
OR name = ''`).all();
if (rows.length === 0) return;
const update = db.prepare(`UPDATE cron_jobs
SET name = ?,
enabled = ?,
agent_id = ?,
payload_kind = ?,
runtime_updated_at_ms = ?
WHERE store_key = ?
AND job_id = ?`);
for (const row of rows) {
const job = safeParseJsonRecord(row.job_json) ?? null;
if (!job) continue;
const schedule = asNullableRecord(job.schedule);
const payload = asNullableRecord(job.payload);
const scheduleKind = textField(schedule, "kind");
const payloadKind = textField(payload, "kind");
const isAt = scheduleKind === "at" && textField(schedule, "at");
const isEvery = scheduleKind === "every" && asFiniteNumber(schedule?.everyMs) != null;
const isCron = scheduleKind === "cron" && textField(schedule, "expr");
const isSystemEvent = payloadKind === "systemEvent" && textField(payload, "text");
const isAgentTurn = payloadKind === "agentTurn" && textField(payload, "message");
if (!schedule || !payload || !isAt && !isEvery && !isCron || !isSystemEvent && !isAgentTurn) continue;
update.run(textField(job, "name") ?? row.job_id, job.enabled === false ? 0 : 1, textField(job, "agentId"), payloadKind, asFiniteNumber(job.updatedAtMs) ?? (coerceRequiredSqliteNumber(row.updated_at) || 0), row.store_key, row.job_id);
}
}
function backfillDeliveryQueueEntriesFromEntryJson(db) {
if (!tableExists(db, "delivery_queue_entries") || !tableHasColumn(db, "delivery_queue_entries", "entry_json") || !tableHasColumn(db, "delivery_queue_entries", "retry_count")) return;
compactLegacyDeliveryQueueFailures(db);
const rows = db.prepare(`SELECT queue_name, id, entry_json
FROM delivery_queue_entries
WHERE status = 'pending'
AND (retry_count = 0
OR last_attempt_at IS NULL
OR last_error IS NULL
OR recovery_state IS NULL
OR platform_send_started_at IS NULL
OR entry_kind IS NULL
OR session_key IS NULL
OR channel IS NULL
OR target IS NULL
OR account_id IS NULL)`).all();
if (rows.length === 0) return;
const update = db.prepare(`UPDATE delivery_queue_entries
SET entry_kind = COALESCE(?, entry_kind),
session_key = COALESCE(?, session_key),
channel = COALESCE(?, channel),
target = COALESCE(?, target),
account_id = COALESCE(?, account_id),
retry_count = ?,
last_attempt_at = COALESCE(?, last_attempt_at),
last_error = COALESCE(?, last_error),
recovery_state = COALESCE(?, recovery_state),
platform_send_started_at = COALESCE(?, platform_send_started_at)
WHERE queue_name = ?
AND id = ?`);
for (const row of rows) {
const entry = safeParseJsonRecord(row.entry_json) ?? null;
if (!entry) continue;
const session = asNullableRecord(entry.session);
const route = asNullableRecord(entry.route);
const deliveryContext = asNullableRecord(entry.deliveryContext);
update.run(textField(entry, "kind"), textField(entry, "sessionKey") ?? textField(session, "key"), textField(entry, "channel") ?? textField(route, "channel") ?? textField(deliveryContext, "channel"), textField(entry, "to") ?? textField(route, "to") ?? textField(deliveryContext, "to"), textField(entry, "accountId") ?? textField(route, "accountId") ?? textField(deliveryContext, "accountId"), asSafeIntegerInRange(entry.retryCount, { min: 0 }) ?? 0, asSafeIntegerInRange(entry.lastAttemptAt, { min: 0 }) ?? null, textField(entry, "lastError"), textField(entry, "recoveryState"), asSafeIntegerInRange(entry.platformSendStartedAt, { min: 0 }) ?? null, row.queue_name, row.id);
}
}
//#endregion
export { ORDERED_STARTUP_ADDITIVE_STATE_COLUMNS as A, terminalizeBoundDeliveryQueueEntry as C, CLAW_FIRST_USE_ADDITIVE_STATE_COLUMN_DEFINITIONS as D, estimateAcpSessionRowBytes as E, CLAW_LAZY_ADDITIVE_STATE_COLUMN_DEFINITIONS as O, pruneDeliveryQueueTombstones as S, estimateAcpEventRowBytes as T, bindDeliveryQueueEntry as _, ensureOperatorApprovalResolutionRefs as a, loadDeliveryQueueEntryInDatabase as b, repairLegacySubagentSuspensionReasons as c, repairOperatorApprovalSchema as d, OPENCLAW_STATE_SCHEMA_SQL as f, projectDeliveryQueueTerminalEntry as g, parseDeliveryQueueCompletionRetention as h, backfillDeliveryQueueEntriesFromEntryJson as i, CLAW_STARTUP_ADDITIVE_STATE_COLUMN_DEFINITIONS as k, assertCanonicalOperatorApprovalKinds as l, inferDeliveryQueueFailureRetention as m, backfillCronJobsFromJobJson as n, repairLegacySubagentExecutionPayloads as o, hasLiveDeliveryQueueClaim as p, backfillCronRunLogEntryJson as r, repairLegacySubagentRetainedResults as s, backfillAcpReplayEstimatedBytes as t, detectOperatorApprovalSchemaMigration as u, deliveryQueueEntriesQuery as v, upsertBoundDeliveryQueueEntryInDatabase as w, pruneDeliveryQueueTombstoneAges as x, inflateDeliveryQueueRow as y };