Files
openclaw/src/state/openclaw-agent-schema.sql
DinoMC 017217bae3 fix(heartbeat): remember silent task results for the next user turn (#95838)
* fix(heartbeat): retain silent task outcomes

Co-authored-by: DinoMC <9079110+DinoMC@users.noreply.github.com>

* fix(heartbeat): keep outcome type internal

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 13:24:48 +01:00

403 lines
13 KiB
SQL

CREATE TABLE IF NOT EXISTS schema_meta (
meta_key TEXT NOT NULL PRIMARY KEY,
role TEXT NOT NULL,
schema_version INTEGER NOT NULL,
agent_id TEXT,
app_version TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS state_leases (
scope TEXT NOT NULL,
lease_key TEXT NOT NULL,
owner TEXT NOT NULL,
expires_at INTEGER,
heartbeat_at INTEGER,
payload_json TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (scope, lease_key)
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_state_leases_expiry
ON state_leases(expires_at, scope, lease_key)
WHERE expires_at IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_agent_state_leases_owner
ON state_leases(owner, updated_at DESC);
CREATE TABLE IF NOT EXISTS sessions (
session_id TEXT NOT NULL PRIMARY KEY,
session_key TEXT NOT NULL,
session_scope TEXT NOT NULL DEFAULT 'conversation' CHECK (session_scope IN ('conversation', 'shared-main', 'group', 'channel')),
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
transcript_updated_at INTEGER DEFAULT NULL,
transcript_observed_at INTEGER DEFAULT NULL,
session_entry_provenance INTEGER NOT NULL DEFAULT 0 CHECK (session_entry_provenance IN (0, 1)),
acp_owned INTEGER NOT NULL DEFAULT 0 CHECK (acp_owned IN (0, 1)),
plugin_owner_id TEXT,
hook_external_content_source TEXT CHECK (hook_external_content_source IS NULL OR hook_external_content_source IN ('gmail', 'webhook')),
started_at INTEGER,
ended_at INTEGER,
status TEXT CHECK (status IS NULL OR status IN ('running', 'done', 'failed', 'killed', 'timeout')),
chat_type TEXT CHECK (chat_type IS NULL OR chat_type IN ('direct', 'group', 'channel')),
channel TEXT,
account_id TEXT,
primary_conversation_id TEXT,
model_provider TEXT,
model TEXT,
agent_harness_id TEXT,
parent_session_key TEXT,
spawned_by TEXT,
display_name TEXT,
FOREIGN KEY (primary_conversation_id) REFERENCES conversations(conversation_id) ON DELETE SET NULL
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_sessions_updated_at
ON sessions(updated_at DESC, session_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_created_at
ON sessions(created_at DESC, session_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_conversation
ON sessions(primary_conversation_id, updated_at DESC, session_id)
WHERE primary_conversation_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS session_routes (
session_key TEXT NOT NULL PRIMARY KEY,
session_id TEXT NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_session_routes_session_id
ON session_routes(session_id);
CREATE TABLE IF NOT EXISTS conversations (
conversation_id TEXT NOT NULL PRIMARY KEY,
channel TEXT NOT NULL,
account_id TEXT NOT NULL,
kind TEXT NOT NULL CHECK (kind IN ('direct', 'group', 'channel')),
peer_id TEXT NOT NULL,
delivery_target TEXT NOT NULL,
parent_conversation_id TEXT,
thread_id TEXT,
native_channel_id TEXT,
native_direct_user_id TEXT,
label TEXT,
metadata_json TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_conversations_lookup
ON conversations(channel, account_id, kind, peer_id, thread_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_conversations_identity
ON conversations(
channel,
account_id,
kind,
peer_id,
IFNULL(parent_conversation_id, ''),
IFNULL(thread_id, '')
);
CREATE INDEX IF NOT EXISTS idx_agent_conversations_updated
ON conversations(updated_at DESC, conversation_id);
CREATE TABLE IF NOT EXISTS conversation_deliveries (
operation_id TEXT NOT NULL PRIMARY KEY,
operation_kind TEXT NOT NULL CHECK (operation_kind IN ('send', 'turn')),
conversation_id TEXT NOT NULL,
source_session_key TEXT,
message_hash TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('created', 'queued', 'sent', 'suppressed', 'rejected', 'unknown', 'replied')),
prepared_message_id TEXT,
platform_message_id TEXT,
queue_id TEXT,
rejection_error TEXT,
reply_message_id TEXT,
reply_to_id TEXT,
reply_thread_id TEXT,
reply_text TEXT,
reply_timestamp INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
CHECK (
(status = 'rejected' AND rejection_error IS NOT NULL) OR
(status != 'rejected' AND rejection_error IS NULL)
),
FOREIGN KEY (conversation_id) REFERENCES conversations(conversation_id) ON DELETE CASCADE
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_conversation_deliveries_reply
ON conversation_deliveries(conversation_id, platform_message_id, prepared_message_id)
WHERE status IN ('queued', 'sent', 'replied');
CREATE INDEX IF NOT EXISTS idx_agent_conversation_deliveries_updated
ON conversation_deliveries(updated_at DESC, operation_id);
CREATE TABLE IF NOT EXISTS session_conversations (
session_id TEXT NOT NULL,
conversation_id TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'primary' CHECK (role IN ('primary', 'participant', 'related')),
first_seen_at INTEGER NOT NULL,
last_seen_at INTEGER NOT NULL,
PRIMARY KEY (session_id, conversation_id, role),
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE,
FOREIGN KEY (conversation_id) REFERENCES conversations(conversation_id) ON DELETE CASCADE
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_session_conversations_conversation
ON session_conversations(conversation_id, last_seen_at DESC, session_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_session_conversations_primary
ON session_conversations(session_id)
WHERE role = 'primary';
CREATE TABLE IF NOT EXISTS session_entries (
session_key TEXT NOT NULL PRIMARY KEY,
session_id TEXT NOT NULL,
entry_json TEXT NOT NULL,
updated_at INTEGER NOT NULL,
status TEXT CHECK (status IS NULL OR status IN ('running', 'done', 'failed', 'killed', 'timeout')),
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_session_entries_updated_at
ON session_entries(updated_at DESC, session_key);
CREATE INDEX IF NOT EXISTS idx_agent_session_entries_session_updated
ON session_entries(session_id, updated_at DESC, session_key);
CREATE INDEX IF NOT EXISTS idx_agent_session_entries_status
ON session_entries(status, session_key)
WHERE status IS NOT NULL;
CREATE TABLE IF NOT EXISTS heartbeat_outcomes (
session_key TEXT NOT NULL PRIMARY KEY,
run_session_key TEXT NOT NULL,
outcome TEXT NOT NULL CHECK (outcome IN ('progress', 'done', 'blocked', 'needs_attention')),
summary TEXT NOT NULL,
response_reason TEXT,
priority TEXT CHECK (priority IS NULL OR priority IN ('low', 'normal', 'high')),
next_check TEXT,
task_names_json TEXT,
wake_source TEXT,
wake_reason TEXT,
occurred_at INTEGER NOT NULL,
context_run_id TEXT,
context_claimed_at INTEGER,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS transcript_events (
session_id TEXT NOT NULL,
seq INTEGER NOT NULL,
event_json TEXT NOT NULL,
created_at INTEGER NOT NULL,
PRIMARY KEY (session_id, seq),
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
) STRICT;
CREATE TABLE IF NOT EXISTS trajectory_runtime_events (
session_id TEXT NOT NULL,
seq INTEGER NOT NULL,
run_id TEXT,
event_json TEXT NOT NULL,
created_at INTEGER NOT NULL,
PRIMARY KEY (session_id, seq),
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_trajectory_runtime_run
ON trajectory_runtime_events(session_id, run_id, seq)
WHERE run_id IS NOT NULL;
CREATE TABLE IF NOT EXISTS transcript_event_identities (
session_id TEXT NOT NULL,
event_id TEXT NOT NULL,
seq INTEGER NOT NULL,
event_type TEXT,
parent_id TEXT,
message_idempotency_key TEXT,
created_at INTEGER NOT NULL,
PRIMARY KEY (session_id, event_id),
FOREIGN KEY (session_id, seq) REFERENCES transcript_events(session_id, seq) ON DELETE CASCADE
) STRICT;
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_transcript_message_idempotency
ON transcript_event_identities(session_id, message_idempotency_key)
WHERE message_idempotency_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_agent_transcript_event_parent
ON transcript_event_identities(session_id, parent_id)
WHERE parent_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_agent_transcript_event_sequence
ON transcript_event_identities(session_id, event_type, seq DESC);
CREATE TABLE IF NOT EXISTS cache_entries (
scope TEXT NOT NULL,
key TEXT NOT NULL,
value_json TEXT,
blob BLOB,
expires_at INTEGER,
updated_at INTEGER NOT NULL,
PRIMARY KEY (scope, key)
) STRICT;
CREATE INDEX IF NOT EXISTS idx_agent_cache_expiry
ON cache_entries(scope, expires_at, key)
WHERE expires_at IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_agent_cache_updated
ON cache_entries(scope, updated_at DESC, key);
CREATE TABLE IF NOT EXISTS auth_profile_store (
store_key TEXT NOT NULL PRIMARY KEY,
store_json TEXT NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS auth_profile_state (
state_key TEXT NOT NULL PRIMARY KEY,
state_json TEXT NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS memory_index_meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS memory_index_sources (
id INTEGER PRIMARY KEY,
path TEXT NOT NULL,
source TEXT NOT NULL DEFAULT 'memory',
hash TEXT NOT NULL,
mtime REAL NOT NULL,
size INTEGER NOT NULL,
UNIQUE (path, source)
) STRICT;
CREATE TABLE IF NOT EXISTS memory_index_chunks (
id TEXT PRIMARY KEY,
path TEXT NOT NULL,
source TEXT NOT NULL DEFAULT 'memory',
start_line INTEGER NOT NULL,
end_line INTEGER NOT NULL,
hash TEXT NOT NULL,
model TEXT NOT NULL,
text TEXT NOT NULL,
embedding TEXT NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS memory_embedding_cache (
provider TEXT NOT NULL,
model TEXT NOT NULL,
provider_key TEXT NOT NULL,
hash TEXT NOT NULL,
embedding TEXT NOT NULL,
dims INTEGER,
updated_at INTEGER NOT NULL,
PRIMARY KEY (provider, model, provider_key, hash)
) STRICT;
CREATE TABLE IF NOT EXISTS memory_index_state (
id INTEGER PRIMARY KEY CHECK (id = 1),
revision INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS session_transcript_index_state (
session_id TEXT NOT NULL PRIMARY KEY,
indexed_seq INTEGER NOT NULL,
leaf_event_id TEXT,
needs_rebuild INTEGER NOT NULL DEFAULT 0,
active_event_count INTEGER NOT NULL DEFAULT 0,
active_message_count INTEGER NOT NULL DEFAULT 0,
updated_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS session_transcript_active_events (
session_id TEXT NOT NULL,
active_position INTEGER NOT NULL CHECK (active_position >= 0),
event_seq INTEGER NOT NULL,
message_position INTEGER CHECK (message_position IS NULL OR message_position >= 0),
PRIMARY KEY (session_id, active_position),
FOREIGN KEY (session_id, event_seq) REFERENCES transcript_events(session_id, seq) ON DELETE CASCADE
) STRICT;
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_transcript_active_event_seq
ON session_transcript_active_events(session_id, event_seq);
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_transcript_active_messages
ON session_transcript_active_events(session_id, message_position)
WHERE message_position IS NOT NULL;
CREATE VIRTUAL TABLE IF NOT EXISTS session_transcript_fts USING fts5(
text,
session_id UNINDEXED,
message_id UNINDEXED,
role UNINDEXED,
timestamp UNINDEXED,
tokenize = 'unicode61 remove_diacritics 2'
);
INSERT OR IGNORE INTO memory_index_state (id, revision) VALUES (1, 0);
CREATE TRIGGER IF NOT EXISTS memory_index_sources_revision_after_insert
AFTER INSERT ON memory_index_sources
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE TRIGGER IF NOT EXISTS memory_index_sources_revision_after_update
AFTER UPDATE ON memory_index_sources
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE TRIGGER IF NOT EXISTS memory_index_sources_revision_after_delete
AFTER DELETE ON memory_index_sources
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE TRIGGER IF NOT EXISTS memory_index_chunks_revision_after_insert
AFTER INSERT ON memory_index_chunks
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE TRIGGER IF NOT EXISTS memory_index_chunks_revision_after_update
AFTER UPDATE ON memory_index_chunks
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE TRIGGER IF NOT EXISTS memory_index_chunks_revision_after_delete
AFTER DELETE ON memory_index_chunks
BEGIN
UPDATE memory_index_state SET revision = revision + 1 WHERE id = 1;
END;
CREATE INDEX IF NOT EXISTS idx_memory_embedding_cache_updated_at
ON memory_embedding_cache(updated_at);
CREATE INDEX IF NOT EXISTS idx_memory_index_sources_source
ON memory_index_sources(source);
CREATE INDEX IF NOT EXISTS idx_memory_index_chunks_path_source
ON memory_index_chunks(path, source);
CREATE INDEX IF NOT EXISTS idx_memory_index_chunks_path
ON memory_index_chunks(path);
CREATE INDEX IF NOT EXISTS idx_memory_index_chunks_source
ON memory_index_chunks(source);