mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-18 02:14:43 +00:00
feat(store): persist subagent tasks to PostgreSQL (#600)
- Migration 000034: subagent_tasks table with tenant scope, JSONB metadata + GIN index, partial index for archival candidates - SubagentTaskStore interface with Create/Get/UpdateStatus/List/Archive - PG implementation with parameterized queries and tenant isolation - SQLite schema v3→4 migration + no-op stub for Lite edition - Wire into store.Stores and factories
This commit is contained in:
1 parent
7d35ee53c7
commit
d8fc97ec63
11 files changed
+436
-3
No files matched your search
@@ -50,7 +50,8 @@ func NewSQLiteStores(cfg store.StoreConfig) (*store.Stores, error) {
|
||||
Activity: NewSQLiteActivityStore(db),
|
||||
APIKeys: NewSQLiteAPIKeyStore(db),
|
||||
ConfigPermissions: NewSQLiteConfigPermissionStore(db),
|
||||
Memory: NewSQLiteMemoryStore(db),
|
||||
Memory: NewSQLiteMemoryStore(db),
|
||||
SubagentTasks: NewSQLiteSubagentTaskStore(),
|
||||
// Phase 2 Batch B+C stores (nil = gracefully skipped by gateway):
|
||||
// AgentLinks, KnowledgeGraph, SecureCLI
|
||||
}, nil
|
||||
|
||||
@@ -14,7 +14,7 @@ var schemaSQL string
|
||||
|
||||
// SchemaVersion is the current SQLite schema version.
|
||||
// Bump this when adding new migration steps below.
|
||||
const SchemaVersion = 3
|
||||
const SchemaVersion = 4
|
||||
|
||||
// migrations maps version → SQL to apply when upgrading FROM that version.
|
||||
// schema.sql always represents the LATEST full schema (for fresh DBs).
|
||||
@@ -42,6 +42,36 @@ UPDATE cron_jobs SET
|
||||
deliver_to = COALESCE(json_extract(payload, '$.to'), ''),
|
||||
wake_heartbeat = COALESCE(json_extract(payload, '$.wake_heartbeat'), 0)
|
||||
WHERE payload IS NOT NULL;`,
|
||||
// Version 3 → 4: add subagent_tasks table for subagent lifecycle persistence.
|
||||
3: `CREATE TABLE IF NOT EXISTS subagent_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
parent_agent_key VARCHAR(255) NOT NULL,
|
||||
session_key VARCHAR(500),
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'running',
|
||||
result TEXT,
|
||||
depth INTEGER NOT NULL DEFAULT 1,
|
||||
model VARCHAR(255),
|
||||
provider VARCHAR(255),
|
||||
iterations INTEGER NOT NULL DEFAULT 0,
|
||||
input_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
output_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
origin_channel VARCHAR(50),
|
||||
origin_chat_id VARCHAR(255),
|
||||
origin_peer_kind VARCHAR(20),
|
||||
origin_user_id VARCHAR(255),
|
||||
spawned_by TEXT,
|
||||
completed_at TEXT,
|
||||
archived_at TEXT,
|
||||
metadata TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
updated_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_parent_status ON subagent_tasks(tenant_id, parent_agent_key, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_session ON subagent_tasks(session_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_created ON subagent_tasks(tenant_id, created_at);`,
|
||||
}
|
||||
|
||||
// EnsureSchema creates tables if they don't exist and applies incremental migrations.
|
||||
|
||||
@@ -1311,3 +1311,38 @@ CREATE TABLE IF NOT EXISTS system_configs (
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_system_configs_tenant ON system_configs(tenant_id);
|
||||
|
||||
-- ============================================================
|
||||
-- Table: subagent_tasks
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subagent_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
parent_agent_key VARCHAR(255) NOT NULL,
|
||||
session_key VARCHAR(500),
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'running',
|
||||
result TEXT,
|
||||
depth INTEGER NOT NULL DEFAULT 1,
|
||||
model VARCHAR(255),
|
||||
provider VARCHAR(255),
|
||||
iterations INTEGER NOT NULL DEFAULT 0,
|
||||
input_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
output_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
origin_channel VARCHAR(50),
|
||||
origin_chat_id VARCHAR(255),
|
||||
origin_peer_kind VARCHAR(20),
|
||||
origin_user_id VARCHAR(255),
|
||||
spawned_by TEXT,
|
||||
completed_at TEXT,
|
||||
archived_at TEXT,
|
||||
metadata TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
updated_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_parent_status ON subagent_tasks(tenant_id, parent_agent_key, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_session ON subagent_tasks(session_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagent_tasks_created ON subagent_tasks(tenant_id, created_at);
|
||||
@@ -0,0 +1,38 @@
|
||||
//go:build sqlite || sqliteonly
|
||||
|
||||
package sqlitestore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/nextlevelbuilder/goclaw/internal/store"
|
||||
)
|
||||
|
||||
// SQLiteSubagentTaskStore is a no-op implementation for the desktop/lite edition.
|
||||
// Subagent task persistence is a standard-edition feature.
|
||||
type SQLiteSubagentTaskStore struct{}
|
||||
|
||||
func NewSQLiteSubagentTaskStore() *SQLiteSubagentTaskStore { return &SQLiteSubagentTaskStore{} }
|
||||
|
||||
func (s *SQLiteSubagentTaskStore) Create(context.Context, *store.SubagentTaskData) error { return nil }
|
||||
func (s *SQLiteSubagentTaskStore) Get(context.Context, uuid.UUID) (*store.SubagentTaskData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s *SQLiteSubagentTaskStore) UpdateStatus(context.Context, uuid.UUID, string, *string, int, int64, int64) error {
|
||||
return nil
|
||||
}
|
||||
func (s *SQLiteSubagentTaskStore) ListByParent(context.Context, string, string) ([]store.SubagentTaskData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s *SQLiteSubagentTaskStore) ListBySession(context.Context, string) ([]store.SubagentTaskData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s *SQLiteSubagentTaskStore) Archive(context.Context, time.Duration) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (s *SQLiteSubagentTaskStore) UpdateMetadata(context.Context, uuid.UUID, map[string]any) error {
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user