chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:28:13 +08:00
commit 0878425be3
1160 changed files with 491311 additions and 0 deletions
@@ -0,0 +1,359 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE roles (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
CONSTRAINT roles_name_unique UNIQUE (name)
);
CREATE INDEX roles_name_idx ON roles(name);
INSERT INTO roles (name) VALUES
('Admin'),
('User')
ON CONFLICT DO NOTHING;
CREATE TABLE privileges (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
role_id BIGINT NOT NULL REFERENCES roles(id),
name TEXT NOT NULL,
CONSTRAINT privileges_role_name_unique UNIQUE (role_id, name)
);
CREATE INDEX privileges_role_id_idx ON privileges(role_id);
CREATE INDEX privileges_name_idx ON privileges(name);
INSERT INTO privileges (role_id, name) VALUES
(1, 'users.create'),
(1, 'users.delete'),
(1, 'users.edit'),
(1, 'users.view'),
(1, 'roles.view'),
(1, 'providers.view'),
(1, 'prompts.view'),
(1, 'prompts.edit'),
(1, 'screenshots.admin'),
(1, 'screenshots.view'),
(1, 'screenshots.download'),
(1, 'screenshots.subscribe'),
(1, 'msglogs.admin'),
(1, 'msglogs.view'),
(1, 'msglogs.subscribe'),
(1, 'termlogs.admin'),
(1, 'termlogs.view'),
(1, 'termlogs.subscribe'),
(1, 'flows.admin'),
(1, 'flows.create'),
(1, 'flows.delete'),
(1, 'flows.edit'),
(1, 'flows.view'),
(1, 'flows.subscribe'),
(1, 'tasks.admin'),
(1, 'tasks.view'),
(1, 'tasks.subscribe'),
(1, 'subtasks.admin'),
(1, 'subtasks.view'),
(1, 'containers.admin'),
(1, 'containers.view')
ON CONFLICT DO NOTHING;
CREATE TYPE USER_TYPE AS ENUM ('local','oauth');
CREATE TYPE USER_STATUS AS ENUM ('created','active','blocked');
CREATE TABLE users (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
hash TEXT NOT NULL DEFAULT MD5(RANDOM()::text),
type USER_TYPE NOT NULL DEFAULT 'local',
mail TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
password TEXT DEFAULT NULL,
status USER_STATUS NOT NULL DEFAULT 'created',
role_id BIGINT NOT NULL DEFAULT '2' REFERENCES roles(id),
password_change_required BOOLEAN NOT NULL DEFAULT false,
provider TEXT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT users_mail_unique UNIQUE (mail),
CONSTRAINT users_hash_unique UNIQUE (hash)
);
CREATE INDEX users_role_id_idx ON users(role_id);
CREATE INDEX users_hash_idx ON users(hash);
INSERT INTO users (mail, name, password, status, role_id, password_change_required) VALUES
(
'admin@pentagi.com',
'admin',
'$2a$10$deVOk0o1nYRHpaVXjIcyCuRmaHvtoMN/2RUT7w5XbZTeiWKEbXx9q',
'active',
1,
true
)
ON CONFLICT DO NOTHING;
CREATE TABLE prompts (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type TEXT NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
prompt TEXT NOT NULL,
CONSTRAINT prompts_type_user_id_unique UNIQUE (type, user_id)
);
CREATE INDEX prompts_type_idx ON prompts(type);
CREATE INDEX prompts_user_id_idx ON prompts(user_id);
CREATE INDEX prompts_prompt_idx ON prompts(prompt);
CREATE TYPE FLOW_STATUS AS ENUM ('created','running','waiting','finished','failed');
CREATE TABLE flows (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
status FLOW_STATUS NOT NULL DEFAULT 'created',
title TEXT NOT NULL DEFAULT 'untitled',
model TEXT NOT NULL,
model_provider TEXT NOT NULL,
language TEXT NOT NULL,
functions JSON NOT NULL DEFAULT '{}',
prompts JSON NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ NULL
);
CREATE INDEX flows_status_idx ON flows(status);
CREATE INDEX flows_title_idx ON flows(title);
CREATE INDEX flows_language_idx ON flows(language);
CREATE INDEX flows_model_provider_idx ON flows(model_provider);
CREATE INDEX flows_user_id_idx ON flows(user_id);
CREATE TYPE CONTAINER_TYPE AS ENUM ('primary','secondary');
CREATE TYPE CONTAINER_STATUS AS ENUM ('starting','running','stopped','deleted','failed');
CREATE TABLE containers (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type CONTAINER_TYPE NOT NULL DEFAULT 'primary',
name TEXT NOT NULL DEFAULT MD5(RANDOM()::text),
image TEXT NOT NULL,
status CONTAINER_STATUS NOT NULL DEFAULT 'starting',
local_id TEXT,
local_dir TEXT,
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT containers_local_id_unique UNIQUE (local_id)
);
CREATE INDEX containers_type_idx ON containers(type);
CREATE INDEX containers_name_idx ON containers(name);
CREATE INDEX containers_status_idx ON containers(status);
CREATE INDEX containers_flow_id_idx ON containers(flow_id);
CREATE TYPE TASK_STATUS AS ENUM ('created','running','waiting','finished','failed');
CREATE TABLE tasks (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
status TASK_STATUS NOT NULL DEFAULT 'created',
title TEXT NOT NULL DEFAULT 'untitled',
input TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX tasks_status_idx ON tasks(status);
CREATE INDEX tasks_title_idx ON tasks(title);
CREATE INDEX tasks_input_idx ON tasks(input);
CREATE INDEX tasks_result_idx ON tasks(result);
CREATE INDEX tasks_flow_id_idx ON tasks(flow_id);
CREATE TYPE SUBTASK_STATUS AS ENUM ('created','running','waiting','finished','failed');
CREATE TABLE subtasks (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
status SUBTASK_STATUS NOT NULL DEFAULT 'created',
title TEXT NOT NULL,
description TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
task_id BIGINT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX subtasks_status_idx ON subtasks(status);
CREATE INDEX subtasks_title_idx ON subtasks(title);
CREATE INDEX subtasks_description_idx ON subtasks(description);
CREATE INDEX subtasks_result_idx ON subtasks(result);
CREATE INDEX subtasks_task_id_idx ON subtasks(task_id);
CREATE TYPE TOOLCALL_STATUS AS ENUM ('received','running','finished','failed');
CREATE TABLE toolcalls (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
call_id TEXT NOT NULL,
status TOOLCALL_STATUS NOT NULL DEFAULT 'received',
name TEXT NOT NULL,
args JSON NOT NULL,
result TEXT NOT NULL DEFAULT '',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX toolcalls_call_id_idx ON toolcalls(call_id);
CREATE INDEX toolcalls_status_idx ON toolcalls(status);
CREATE INDEX toolcalls_name_idx ON toolcalls(name);
CREATE INDEX toolcalls_flow_id_idx ON toolcalls(flow_id);
CREATE INDEX toolcalls_task_id_idx ON toolcalls(task_id);
CREATE INDEX toolcalls_subtask_id_idx ON toolcalls(subtask_id);
CREATE TYPE MSGCHAIN_TYPE AS ENUM (
'primary_agent',
'reporter',
'generator',
'refiner',
'reflector',
'enricher',
'adviser',
'coder',
'memorist',
'searcher',
'installer',
'pentester',
'summarizer'
);
CREATE TABLE msgchains (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
model TEXT NOT NULL,
model_provider TEXT NOT NULL,
usage_in BIGINT NOT NULL DEFAULT 0,
usage_out BIGINT NOT NULL DEFAULT 0,
chain JSON NOT NULL,
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX msgchains_type_idx ON msgchains(type);
CREATE INDEX msgchains_flow_id_idx ON msgchains(flow_id);
CREATE INDEX msgchains_task_id_idx ON msgchains(task_id);
CREATE INDEX msgchains_subtask_id_idx ON msgchains(subtask_id);
CREATE TYPE TERMLOG_TYPE AS ENUM ('stdin', 'stdout','stderr');
CREATE TABLE termlogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type TERMLOG_TYPE NOT NULL,
text TEXT NOT NULL,
container_id BIGINT NOT NULL REFERENCES containers(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX termlogs_type_idx ON termlogs(type);
-- CREATE INDEX termlogs_text_idx ON termlogs(text);
CREATE INDEX termlogs_container_id_idx ON termlogs(container_id);
CREATE TYPE MSGLOG_TYPE AS ENUM ('thoughts', 'browser', 'terminal', 'file', 'search', 'advice', 'ask', 'input', 'done');
CREATE TABLE msglogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type MSGLOG_TYPE NOT NULL,
message TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX msglogs_type_idx ON msglogs(type);
CREATE INDEX msglogs_message_idx ON msglogs(message);
CREATE INDEX msglogs_flow_id_idx ON msglogs(flow_id);
CREATE INDEX msglogs_task_id_idx ON msglogs(task_id);
CREATE INDEX msglogs_subtask_id_idx ON msglogs(subtask_id);
CREATE TABLE screenshots (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
url TEXT NOT NULL,
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX screenshots_flow_id_idx ON screenshots(flow_id);
CREATE INDEX screenshots_name_idx ON screenshots(name);
CREATE INDEX screenshots_url_idx ON screenshots(url);
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS
$$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_flows_modified
BEFORE UPDATE ON flows
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_tasks_modified
BEFORE UPDATE ON tasks
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_subtasks_modified
BEFORE UPDATE ON subtasks
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_containers_modified
BEFORE UPDATE ON containers
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_toolcalls_modified
BEFORE UPDATE ON toolcalls
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_msgchains_modified
BEFORE UPDATE ON msgchains
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE screenshots;
DROP TABLE msglogs;
DROP TABLE termlogs;
DROP TABLE msgchains;
DROP TABLE toolcalls;
DROP TABLE subtasks;
DROP TABLE tasks;
DROP TABLE containers;
DROP TABLE flows;
DROP TABLE users;
DROP TABLE roles;
DROP TABLE privileges;
DROP TYPE MSGLOG_TYPE;
DROP TYPE TERMLOG_TYPE;
DROP TYPE MSGCHAIN_TYPE;
DROP TYPE TOOLCALL_STATUS;
DROP TYPE SUBTASK_STATUS;
DROP TYPE TASK_STATUS;
DROP TYPE CONTAINER_STATUS;
DROP TYPE CONTAINER_TYPE;
DROP TYPE FLOW_STATUS;
DROP TYPE USER_STATUS;
DROP TYPE USER_TYPE;
DROP FUNCTION update_modified_column;
-- +goose StatementEnd
@@ -0,0 +1,101 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'agentlogs.admin'),
(1, 'agentlogs.view'),
(1, 'agentlogs.subscribe'),
(1, 'vecstorelogs.admin'),
(1, 'vecstorelogs.view'),
(1, 'vecstorelogs.subscribe'),
(1, 'searchlogs.admin'),
(1, 'searchlogs.view'),
(1, 'searchlogs.subscribe')
ON CONFLICT DO NOTHING;
CREATE TABLE agentlogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
initiator MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
executor MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
task TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX agentlogs_initiator_idx ON agentlogs(initiator);
CREATE INDEX agentlogs_executor_idx ON agentlogs(executor);
CREATE INDEX agentlogs_task_idx ON agentlogs(task);
CREATE INDEX agentlogs_flow_id_idx ON agentlogs(flow_id);
CREATE INDEX agentlogs_task_id_idx ON agentlogs(task_id);
CREATE INDEX agentlogs_subtask_id_idx ON agentlogs(subtask_id);
CREATE TYPE VECSTORE_ACTION_TYPE AS ENUM ('retrieve', 'store');
CREATE TABLE vecstorelogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
initiator MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
executor MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
filter JSON NOT NULL DEFAULT '{}',
query TEXT NOT NULL,
action VECSTORE_ACTION_TYPE NOT NULL,
result TEXT NOT NULL,
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX vecstorelogs_initiator_idx ON vecstorelogs(initiator);
CREATE INDEX vecstorelogs_executor_idx ON vecstorelogs(executor);
CREATE INDEX vecstorelogs_query_idx ON vecstorelogs(query);
CREATE INDEX vecstorelogs_action_idx ON vecstorelogs(action);
CREATE INDEX vecstorelogs_flow_id_idx ON vecstorelogs(flow_id);
CREATE INDEX vecstorelogs_task_id_idx ON vecstorelogs(task_id);
CREATE INDEX vecstorelogs_subtask_id_idx ON vecstorelogs(subtask_id);
CREATE TYPE SEARCHENGINE_TYPE AS ENUM ('google', 'tavily', 'traversaal', 'browser');
CREATE TABLE searchlogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
initiator MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
executor MSGCHAIN_TYPE NOT NULL DEFAULT 'primary_agent',
engine SEARCHENGINE_TYPE NOT NULL,
query TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE,
subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX searchlogs_initiator_idx ON searchlogs(initiator);
CREATE INDEX searchlogs_executor_idx ON searchlogs(executor);
CREATE INDEX searchlogs_engine_idx ON searchlogs(engine);
CREATE INDEX searchlogs_query_idx ON searchlogs(query);
CREATE INDEX searchlogs_flow_id_idx ON searchlogs(flow_id);
CREATE INDEX searchlogs_task_id_idx ON searchlogs(task_id);
CREATE INDEX searchlogs_subtask_id_idx ON searchlogs(subtask_id);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE agentlogs;
DROP TABLE vecstorelogs;
DROP TABLE searchlogs;
DROP TYPE VECSTORE_ACTION_TYPE;
DROP TYPE SEARCHENGINE_TYPE;
DELETE FROM privileges WHERE name IN (
'agentlogs.admin',
'agentlogs.view',
'agentlogs.subscribe',
'vecstorelogs.admin',
'vecstorelogs.view',
'vecstorelogs.subscribe',
'searchlogs.admin',
'searchlogs.view',
'searchlogs.subscribe'
);
-- +goose StatementEnd
@@ -0,0 +1,35 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(2, 'roles.view'),
(2, 'providers.view'),
(2, 'prompts.view'),
(2, 'screenshots.view'),
(2, 'screenshots.download'),
(2, 'screenshots.subscribe'),
(2, 'msglogs.view'),
(2, 'msglogs.subscribe'),
(2, 'termlogs.view'),
(2, 'termlogs.subscribe'),
(2, 'flows.create'),
(2, 'flows.delete'),
(2, 'flows.edit'),
(2, 'flows.view'),
(2, 'flows.subscribe'),
(2, 'tasks.view'),
(2, 'tasks.subscribe'),
(2, 'subtasks.view'),
(2, 'containers.view'),
(2, 'agentlogs.view'),
(2, 'agentlogs.subscribe'),
(2, 'vecstorelogs.view'),
(2, 'vecstorelogs.subscribe'),
(2, 'searchlogs.view'),
(2, 'searchlogs.subscribe')
ON CONFLICT DO NOTHING;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE role_id = 2;
-- +goose StatementEnd
@@ -0,0 +1,19 @@
-- +goose Up
-- +goose StatementBegin
CREATE TYPE MSGLOG_RESULT_FORMAT AS ENUM ('plain', 'markdown', 'terminal');
ALTER TABLE msglogs ADD COLUMN result_format MSGLOG_RESULT_FORMAT NULL DEFAULT 'plain';
UPDATE msglogs SET result_format = 'plain';
ALTER TABLE msglogs ALTER COLUMN result_format SET NOT NULL;
CREATE INDEX msglogs_result_format_idx ON msglogs(result_format);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE msglogs DROP COLUMN result_format;
DROP TYPE MSGLOG_RESULT_FORMAT;
-- +goose StatementEnd
@@ -0,0 +1,11 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE flows ADD COLUMN trace_id TEXT NULL;
CREATE INDEX flows_trace_id_idx ON flows(trace_id);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE flows DROP COLUMN trace_id;
-- +goose StatementEnd
@@ -0,0 +1,138 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE msgchains ALTER COLUMN type DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN executor DROP DEFAULT;
CREATE TYPE MSGCHAIN_TYPE_NEW AS ENUM (
'primary_agent',
'reporter',
'generator',
'refiner',
'reflector',
'enricher',
'adviser',
'coder',
'memorist',
'searcher',
'installer',
'pentester',
'summarizer',
'tool_call_fixer'
);
ALTER TABLE msgchains
ALTER COLUMN type TYPE MSGCHAIN_TYPE_NEW USING type::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE agentlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE vecstorelogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE searchlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
DROP TYPE MSGCHAIN_TYPE;
ALTER TYPE MSGCHAIN_TYPE_NEW RENAME TO MSGCHAIN_TYPE;
ALTER TABLE msgchains
ALTER COLUMN type SET NOT NULL,
ALTER COLUMN type SET DEFAULT 'primary_agent';
ALTER TABLE agentlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE vecstorelogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE searchlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM msgchains WHERE type = 'tool_call_fixer';
ALTER TABLE msgchains ALTER COLUMN type DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN executor DROP DEFAULT;
CREATE TYPE MSGCHAIN_TYPE_NEW AS ENUM (
'primary_agent',
'reporter',
'generator',
'refiner',
'reflector',
'enricher',
'adviser',
'coder',
'memorist',
'searcher',
'installer',
'pentester',
'summarizer'
);
ALTER TABLE msgchains
ALTER COLUMN type TYPE MSGCHAIN_TYPE_NEW USING type::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE agentlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE vecstorelogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE searchlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
DROP TYPE MSGCHAIN_TYPE;
ALTER TYPE MSGCHAIN_TYPE_NEW RENAME TO MSGCHAIN_TYPE;
ALTER TABLE msgchains
ALTER COLUMN type SET NOT NULL,
ALTER COLUMN type SET DEFAULT 'primary_agent';
ALTER TABLE agentlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE vecstorelogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE searchlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
-- +goose StatementEnd
@@ -0,0 +1,50 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE searchlogs ALTER COLUMN engine DROP DEFAULT;
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser',
'duckduckgo',
'perplexity'
);
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING engine::text::SEARCHENGINE_TYPE_NEW;
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE searchlogs ALTER COLUMN engine DROP DEFAULT;
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser'
);
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING
CASE
WHEN engine::text = 'duckduckgo' THEN 'google'::text
WHEN engine::text = 'perplexity' THEN 'browser'::text
ELSE engine::text
END::SEARCHENGINE_TYPE_NEW;
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,298 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'assistants.admin'),
(1, 'assistants.create'),
(1, 'assistants.delete'),
(1, 'assistants.edit'),
(1, 'assistants.view'),
(1, 'assistants.subscribe'),
(1, 'assistantlogs.admin'),
(1, 'assistantlogs.view'),
(1, 'assistantlogs.subscribe'),
(2, 'assistants.create'),
(2, 'assistants.delete'),
(2, 'assistants.edit'),
(2, 'assistants.view'),
(2, 'assistants.subscribe'),
(2, 'assistantlogs.view'),
(2, 'assistantlogs.subscribe');
ALTER TABLE msgchains ALTER COLUMN type DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN executor DROP DEFAULT;
CREATE TYPE MSGCHAIN_TYPE_NEW AS ENUM (
'primary_agent',
'reporter',
'generator',
'refiner',
'reflector',
'enricher',
'adviser',
'coder',
'memorist',
'searcher',
'installer',
'pentester',
'summarizer',
'tool_call_fixer',
'assistant'
);
ALTER TABLE msgchains
ALTER COLUMN type TYPE MSGCHAIN_TYPE_NEW USING type::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE agentlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE vecstorelogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
ALTER TABLE searchlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING initiator::text::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING executor::text::MSGCHAIN_TYPE_NEW;
DROP TYPE MSGCHAIN_TYPE;
ALTER TYPE MSGCHAIN_TYPE_NEW RENAME TO MSGCHAIN_TYPE;
ALTER TABLE msgchains
ALTER COLUMN type SET NOT NULL,
ALTER COLUMN type SET DEFAULT 'primary_agent';
ALTER TABLE agentlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE vecstorelogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE searchlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
CREATE TYPE MSGLOG_TYPE_NEW AS ENUM (
'answer',
'report',
'thoughts',
'browser',
'terminal',
'file',
'search',
'advice',
'ask',
'input',
'done'
);
ALTER TABLE msglogs
ALTER COLUMN type TYPE MSGLOG_TYPE_NEW USING type::text::MSGLOG_TYPE_NEW;
DROP TYPE MSGLOG_TYPE;
ALTER TYPE MSGLOG_TYPE_NEW RENAME TO MSGLOG_TYPE;
ALTER TABLE msglogs
ALTER COLUMN type SET NOT NULL;
CREATE TYPE ASSISTANT_STATUS AS ENUM ('created','running','waiting','finished','failed');
CREATE TABLE assistants (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
status ASSISTANT_STATUS NOT NULL DEFAULT 'created',
title TEXT NOT NULL DEFAULT 'untitled',
model TEXT NOT NULL,
model_provider TEXT NOT NULL,
language TEXT NOT NULL,
functions JSON NOT NULL DEFAULT '{}',
prompts JSON NOT NULL,
trace_id TEXT NULL,
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
use_agents BOOLEAN NOT NULL DEFAULT FALSE,
msgchain_id BIGINT NULL REFERENCES msgchains(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ NULL
);
CREATE INDEX assistants_status_idx ON assistants(status);
CREATE INDEX assistants_title_idx ON assistants(title);
CREATE INDEX assistants_model_provider_idx ON assistants(model_provider);
CREATE INDEX assistants_trace_id_idx ON assistants(trace_id);
CREATE INDEX assistants_flow_id_idx ON assistants(flow_id);
CREATE INDEX assistants_msgchain_id_idx ON assistants(msgchain_id);
CREATE TABLE assistantlogs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
type MSGLOG_TYPE NOT NULL,
message TEXT NOT NULL,
result TEXT NOT NULL DEFAULT '',
result_format MSGLOG_RESULT_FORMAT NOT NULL DEFAULT 'plain',
flow_id BIGINT NOT NULL REFERENCES flows(id) ON DELETE CASCADE,
assistant_id BIGINT NOT NULL REFERENCES assistants(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX assistantlogs_type_idx ON assistantlogs(type);
CREATE INDEX assistantlogs_message_idx ON assistantlogs(message);
CREATE INDEX assistantlogs_result_format_idx ON assistantlogs(result_format);
CREATE INDEX assistantlogs_flow_id_idx ON assistantlogs(flow_id);
CREATE INDEX assistantlogs_assistant_id_idx ON assistantlogs(assistant_id);
CREATE OR REPLACE TRIGGER update_assistants_modified
BEFORE UPDATE ON assistants
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE assistants;
DROP TABLE assistantlogs;
DROP TYPE ASSISTANT_STATUS;
DELETE FROM privileges WHERE name IN (
'assistants.admin',
'assistants.create',
'assistants.delete',
'assistants.edit',
'assistants.view',
'assistants.subscribe',
'assistantlogs.admin',
'assistantlogs.view',
'assistantlogs.subscribe'
);
DELETE FROM msgchains WHERE type = 'assistant';
ALTER TABLE msgchains ALTER COLUMN type DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE agentlogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE vecstorelogs ALTER COLUMN executor DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN initiator DROP DEFAULT;
ALTER TABLE searchlogs ALTER COLUMN executor DROP DEFAULT;
CREATE TYPE MSGCHAIN_TYPE_NEW AS ENUM (
'primary_agent',
'reporter',
'generator',
'refiner',
'reflector',
'enricher',
'adviser',
'coder',
'memorist',
'searcher',
'installer',
'pentester',
'summarizer',
'tool_call_fixer'
);
ALTER TABLE msgchains
ALTER COLUMN type TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN type::text = 'assistant' THEN 'primary_agent'::text
ELSE type::text
END::MSGCHAIN_TYPE_NEW;
ALTER TABLE agentlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN initiator::text = 'assistant' THEN 'primary_agent'::text
ELSE initiator::text
END::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN executor::text = 'assistant' THEN 'primary_agent'::text
ELSE executor::text
END::MSGCHAIN_TYPE_NEW;
ALTER TABLE vecstorelogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN initiator::text = 'assistant' THEN 'primary_agent'::text
ELSE initiator::text
END::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN executor::text = 'assistant' THEN 'primary_agent'::text
ELSE executor::text
END::MSGCHAIN_TYPE_NEW;
ALTER TABLE searchlogs
ALTER COLUMN initiator TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN initiator::text = 'assistant' THEN 'primary_agent'::text
ELSE initiator::text
END::MSGCHAIN_TYPE_NEW,
ALTER COLUMN executor TYPE MSGCHAIN_TYPE_NEW USING
CASE
WHEN executor::text = 'assistant' THEN 'primary_agent'::text
ELSE executor::text
END::MSGCHAIN_TYPE_NEW;
DROP TYPE MSGCHAIN_TYPE;
ALTER TYPE MSGCHAIN_TYPE_NEW RENAME TO MSGCHAIN_TYPE;
ALTER TABLE msgchains
ALTER COLUMN type SET NOT NULL,
ALTER COLUMN type SET DEFAULT 'primary_agent';
ALTER TABLE agentlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE vecstorelogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
ALTER TABLE searchlogs
ALTER COLUMN initiator SET NOT NULL,
ALTER COLUMN initiator SET DEFAULT 'primary_agent',
ALTER COLUMN executor SET NOT NULL,
ALTER COLUMN executor SET DEFAULT 'primary_agent';
DELETE FROM msglogs WHERE type = 'answer' OR type = 'report';
CREATE TYPE MSGLOG_TYPE_NEW AS ENUM (
'thoughts',
'browser',
'terminal',
'file',
'search',
'advice',
'ask',
'input',
'done'
);
ALTER TABLE msglogs
ALTER COLUMN type TYPE MSGLOG_TYPE_NEW USING type::text::MSGLOG_TYPE_NEW;
DROP TYPE MSGLOG_TYPE;
ALTER TYPE MSGLOG_TYPE_NEW RENAME TO MSGLOG_TYPE;
ALTER TABLE msglogs
ALTER COLUMN type SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE subtasks ADD COLUMN context TEXT NULL DEFAULT '';
UPDATE subtasks SET context = '';
ALTER TABLE subtasks ALTER COLUMN context SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE subtasks DROP COLUMN context;
-- +goose StatementEnd
@@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE msglogs ADD COLUMN thinking TEXT NULL;
ALTER TABLE assistantlogs ADD COLUMN thinking TEXT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE msglogs DROP COLUMN thinking;
ALTER TABLE assistantlogs DROP COLUMN thinking;
-- +goose StatementEnd
@@ -0,0 +1,27 @@
-- +goose Up
-- +goose StatementBegin
CREATE EXTENSION IF NOT EXISTS pg_trgm;
DROP INDEX IF EXISTS assistantlogs_message_idx;
CREATE INDEX assistantlogs_message_idx ON assistantlogs USING GIN (message gin_trgm_ops);
CREATE INDEX assistantlogs_result_idx ON assistantlogs USING GIN (result gin_trgm_ops);
CREATE INDEX assistantlogs_thinking_idx ON assistantlogs USING GIN (thinking gin_trgm_ops);
DROP INDEX IF EXISTS msglogs_message_idx;
CREATE INDEX msglogs_message_idx ON msglogs USING GIN (message gin_trgm_ops);
CREATE INDEX msglogs_result_idx ON msglogs USING GIN (result gin_trgm_ops);
CREATE INDEX msglogs_thinking_idx ON msglogs USING GIN (thinking gin_trgm_ops);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP INDEX IF EXISTS assistantlogs_message_idx;
DROP INDEX IF EXISTS assistantlogs_result_idx;
DROP INDEX IF EXISTS assistantlogs_thinking_idx;
CREATE INDEX assistantlogs_message_idx ON assistantlogs(message);
DROP INDEX IF EXISTS msglogs_message_idx;
DROP INDEX IF EXISTS msglogs_result_idx;
DROP INDEX IF EXISTS msglogs_thinking_idx;
CREATE INDEX msglogs_message_idx ON msglogs(message);
-- +goose StatementEnd
@@ -0,0 +1,15 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'settings.admin'),
(1, 'settings.view'),
(2, 'settings.view');
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE name IN (
'settings.admin',
'settings.view'
);
-- +goose StatementEnd
@@ -0,0 +1,209 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'settings.providers.admin'),
(1, 'settings.providers.view'),
(1, 'settings.providers.edit'),
(1, 'settings.providers.subscribe'),
(1, 'settings.prompts.admin'),
(1, 'settings.prompts.view'),
(1, 'settings.prompts.edit'),
(2, 'settings.providers.view'),
(2, 'settings.providers.edit'),
(2, 'settings.providers.subscribe'),
(2, 'settings.prompts.view'),
(2, 'settings.prompts.edit');
-- Replace old prompt permissions with new settings-namespaced ones
DELETE FROM privileges WHERE name IN (
'prompts.view',
'prompts.edit'
);
-- Move prompts from flow/assistant to separate table and load them each time from the database
ALTER TABLE flows DROP COLUMN prompts;
ALTER TABLE assistants DROP COLUMN prompts;
CREATE TYPE PROVIDER_TYPE AS ENUM (
'openai',
'anthropic',
'gemini',
'bedrock',
'ollama',
'custom'
);
CREATE TABLE providers (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type PROVIDER_TYPE NOT NULL,
name TEXT NOT NULL,
config JSON NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ NULL
);
CREATE INDEX providers_user_id_idx ON providers(user_id);
CREATE INDEX providers_type_idx ON providers(type);
CREATE INDEX providers_name_user_id_idx ON providers(name, user_id);
CREATE UNIQUE INDEX providers_name_user_id_unique ON providers(name, user_id) WHERE deleted_at IS NULL;
-- Add model providers type column and separate name from type
ALTER TABLE flows ADD COLUMN model_provider_type PROVIDER_TYPE NULL;
UPDATE flows SET model_provider_type = model_provider::PROVIDER_TYPE;
ALTER TABLE flows ALTER COLUMN model_provider_type SET NOT NULL;
CREATE INDEX flows_model_provider_type_idx ON flows(model_provider_type);
DROP INDEX IF EXISTS flows_model_provider_idx;
ALTER TABLE flows RENAME COLUMN model_provider TO model_provider_name;
CREATE INDEX flows_model_provider_name_idx ON flows(model_provider_name);
ALTER TABLE assistants ADD COLUMN model_provider_type PROVIDER_TYPE NULL;
UPDATE assistants SET model_provider_type = model_provider::PROVIDER_TYPE;
ALTER TABLE assistants ALTER COLUMN model_provider_type SET NOT NULL;
CREATE INDEX assistants_model_provider_type_idx ON assistants(model_provider_type);
DROP INDEX IF EXISTS assistants_model_provider_idx;
ALTER TABLE assistants RENAME COLUMN model_provider TO model_provider_name;
CREATE INDEX assistants_model_provider_name_idx ON assistants(model_provider_name);
-- ENUM values correspond to template files in backend/pkg/templates/prompts/
CREATE TYPE PROMPT_TYPE AS ENUM (
'primary_agent',
'assistant',
'pentester',
'question_pentester',
'coder',
'question_coder',
'installer',
'question_installer',
'searcher',
'question_searcher',
'memorist',
'question_memorist',
'adviser',
'question_adviser',
'generator',
'subtasks_generator',
'refiner',
'subtasks_refiner',
'reporter',
'task_reporter',
'reflector',
'question_reflector',
'enricher',
'question_enricher',
'toolcall_fixer',
'input_toolcall_fixer',
'summarizer',
'image_chooser',
'language_chooser',
'flow_descriptor',
'task_descriptor',
'execution_logs',
'full_execution_context',
'short_execution_context'
);
-- Validate existing prompt types are compatible with new ENUM before migration
DO $$
DECLARE
invalid_types TEXT[];
BEGIN
SELECT ARRAY_AGG(DISTINCT type) INTO invalid_types
FROM prompts
WHERE type::TEXT NOT IN (
'execution_logs', 'full_execution_context', 'short_execution_context',
'question_enricher', 'question_adviser', 'question_coder', 'question_installer',
'question_memorist', 'question_pentester', 'question_searcher', 'question_reflector',
'input_toolcall_fixer', 'assistant', 'primary_agent', 'flow_descriptor',
'task_descriptor', 'image_chooser', 'language_chooser', 'task_reporter',
'toolcall_fixer', 'reporter', 'subtasks_generator', 'generator',
'subtasks_refiner', 'refiner', 'enricher', 'reflector', 'adviser',
'coder', 'installer', 'pentester', 'memorist', 'searcher', 'summarizer'
);
IF array_length(invalid_types, 1) > 0 THEN
RAISE EXCEPTION 'Found invalid prompt types that cannot be converted to ENUM: %',
array_to_string(invalid_types, ', ');
END IF;
END$$;
DROP INDEX IF EXISTS prompts_type_idx;
DROP INDEX IF EXISTS prompts_prompt_idx;
ALTER TABLE prompts
ALTER COLUMN type TYPE PROMPT_TYPE USING type::text::PROMPT_TYPE;
CREATE INDEX prompts_type_idx ON prompts(type);
ALTER TABLE prompts
ADD COLUMN created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP;
CREATE OR REPLACE TRIGGER update_providers_modified
BEFORE UPDATE ON providers
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
CREATE OR REPLACE TRIGGER update_prompts_modified
BEFORE UPDATE ON prompts
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE flows DROP COLUMN model_provider_type;
ALTER TABLE assistants DROP COLUMN model_provider_type;
ALTER TABLE flows RENAME COLUMN model_provider_name TO model_provider;
ALTER TABLE assistants RENAME COLUMN model_provider_name TO model_provider;
-- Delete unsupported model providers
DROP INDEX IF EXISTS flows_model_provider_name_idx;
DROP INDEX IF EXISTS assistants_model_provider_name_idx;
DELETE FROM flows WHERE model_provider NOT IN ('openai', 'anthropic', 'custom');
DELETE FROM assistants WHERE model_provider NOT IN ('openai', 'anthropic', 'custom');
CREATE INDEX flows_model_provider_idx ON flows(model_provider);
CREATE INDEX assistants_model_provider_idx ON assistants(model_provider);
DROP TABLE providers;
DROP TYPE PROVIDER_TYPE;
DELETE FROM privileges WHERE name IN (
'settings.providers.admin',
'settings.providers.view',
'settings.providers.edit',
'settings.providers.subscribe',
'settings.prompts.admin',
'settings.prompts.view',
'settings.prompts.edit'
);
INSERT INTO privileges (role_id, name) VALUES
(1, 'prompts.view'),
(1, 'prompts.edit'),
(2, 'prompts.view');
-- Convert prompts.type back to TEXT while preserving user data
DROP INDEX IF EXISTS prompts_type_idx;
ALTER TABLE prompts
ALTER COLUMN type TYPE TEXT USING type::text;
CREATE INDEX prompts_type_idx ON prompts(type);
CREATE INDEX prompts_prompt_idx ON prompts(prompt);
DROP TRIGGER IF EXISTS update_prompts_modified ON prompts;
ALTER TABLE prompts DROP COLUMN created_at;
ALTER TABLE prompts DROP COLUMN updated_at;
DROP TYPE PROMPT_TYPE;
-- Restore prompts to flows/assistants
ALTER TABLE flows ADD COLUMN prompts JSON NULL;
ALTER TABLE assistants ADD COLUMN prompts JSON NULL;
UPDATE flows SET prompts = '{}';
UPDATE assistants SET prompts = '{}';
ALTER TABLE flows ALTER COLUMN prompts SET NOT NULL;
ALTER TABLE assistants ALTER COLUMN prompts SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,50 @@
-- +goose Up
-- +goose StatementBegin
-- Add searxng to the searchengine_type enum
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser',
'duckduckgo',
'perplexity',
'searxng'
);
-- Update the searchlogs table to use the new enum type
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING engine::text::SEARCHENGINE_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
-- Set the column as NOT NULL
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Revert the changes by removing searxng from the enum
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser',
'duckduckgo',
'perplexity'
);
-- Update the searchlogs table to use the new enum type
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING engine::text::SEARCHENGINE_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
-- Set the column as NOT NULL
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
DROP INDEX IF EXISTS tasks_input_idx;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
CREATE INDEX tasks_input_idx ON tasks(input);
-- +goose StatementEnd
@@ -0,0 +1,11 @@
-- +goose Up
-- +goose StatementBegin
DROP INDEX IF EXISTS tasks_result_idx;
DROP INDEX IF EXISTS subtasks_result_idx;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
CREATE INDEX tasks_result_idx ON tasks(result);
CREATE INDEX subtasks_result_idx ON subtasks(result);
-- +goose StatementEnd
@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
DROP INDEX IF EXISTS subtasks_description_idx;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
CREATE INDEX subtasks_description_idx ON subtasks(description);
-- +goose StatementEnd
@@ -0,0 +1,125 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE flows ADD COLUMN tool_call_id_template TEXT NULL;
ALTER TABLE assistants ADD COLUMN tool_call_id_template TEXT NULL;
UPDATE flows SET tool_call_id_template = 'call_{r:24:x}';
UPDATE assistants SET tool_call_id_template = 'call_{r:24:x}';
ALTER TABLE flows ALTER COLUMN tool_call_id_template SET NOT NULL;
ALTER TABLE assistants ALTER COLUMN tool_call_id_template SET NOT NULL;
CREATE INDEX flows_tool_call_id_template_idx ON flows(tool_call_id_template) WHERE tool_call_id_template IS NOT NULL;
CREATE INDEX assistants_tool_call_id_template_idx ON assistants(tool_call_id_template) WHERE tool_call_id_template IS NOT NULL;
-- Add new prompt types for tool call ID detection
CREATE TYPE PROMPT_TYPE_NEW AS ENUM (
'primary_agent',
'assistant',
'pentester',
'question_pentester',
'coder',
'question_coder',
'installer',
'question_installer',
'searcher',
'question_searcher',
'memorist',
'question_memorist',
'adviser',
'question_adviser',
'generator',
'subtasks_generator',
'refiner',
'subtasks_refiner',
'reporter',
'task_reporter',
'reflector',
'question_reflector',
'enricher',
'question_enricher',
'toolcall_fixer',
'input_toolcall_fixer',
'summarizer',
'image_chooser',
'language_chooser',
'flow_descriptor',
'task_descriptor',
'execution_logs',
'full_execution_context',
'short_execution_context',
'tool_call_id_collector',
'tool_call_id_detector'
);
-- Update the searchlogs table to use the new enum type
ALTER TABLE prompts
ALTER COLUMN type TYPE PROMPT_TYPE_NEW USING type::text::PROMPT_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROMPT_TYPE;
ALTER TYPE PROMPT_TYPE_NEW RENAME TO PROMPT_TYPE;
-- Set the column as NOT NULL
ALTER TABLE prompts
ALTER COLUMN type SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP INDEX IF EXISTS flows_tool_call_id_template_idx;
DROP INDEX IF EXISTS assistants_tool_call_id_template_idx;
ALTER TABLE flows DROP COLUMN IF EXISTS tool_call_id_template;
ALTER TABLE assistants DROP COLUMN IF EXISTS tool_call_id_template;
-- Revert the changes by removing tool call ID collector and detector from the enum
CREATE TYPE PROMPT_TYPE_NEW AS ENUM (
'primary_agent',
'assistant',
'pentester',
'question_pentester',
'coder',
'question_coder',
'installer',
'question_installer',
'searcher',
'question_searcher',
'memorist',
'question_memorist',
'adviser',
'question_adviser',
'generator',
'subtasks_generator',
'refiner',
'subtasks_refiner',
'reporter',
'task_reporter',
'reflector',
'question_reflector',
'enricher',
'question_enricher',
'toolcall_fixer',
'input_toolcall_fixer',
'summarizer',
'image_chooser',
'language_chooser',
'flow_descriptor',
'task_descriptor',
'execution_logs',
'full_execution_context',
'short_execution_context'
);
-- Update the prompts table to use the new enum type
ALTER TABLE prompts
ALTER COLUMN type TYPE PROMPT_TYPE_NEW USING type::text::PROMPT_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROMPT_TYPE;
ALTER TYPE PROMPT_TYPE_NEW RENAME TO PROMPT_TYPE;
-- Set the column as NOT NULL
ALTER TABLE prompts
ALTER COLUMN type SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,258 @@
-- +goose Up
-- +goose StatementBegin
-- Add usage tracking fields to msgchains
ALTER TABLE msgchains ADD COLUMN usage_cache_in BIGINT NOT NULL DEFAULT 0;
ALTER TABLE msgchains ADD COLUMN usage_cache_out BIGINT NOT NULL DEFAULT 0;
ALTER TABLE msgchains ADD COLUMN usage_cost_in DOUBLE PRECISION NOT NULL DEFAULT 0.0;
ALTER TABLE msgchains ADD COLUMN usage_cost_out DOUBLE PRECISION NOT NULL DEFAULT 0.0;
-- Add duration tracking to msgchains (nullable first)
ALTER TABLE msgchains ADD COLUMN duration_seconds DOUBLE PRECISION NULL;
-- Calculate duration for existing msgchains records
UPDATE msgchains
SET duration_seconds = EXTRACT(EPOCH FROM (updated_at - created_at))
WHERE updated_at > created_at;
-- Set remaining NULL values to 0.0
UPDATE msgchains
SET duration_seconds = 0.0
WHERE duration_seconds IS NULL;
-- Make column NOT NULL with default
ALTER TABLE msgchains ALTER COLUMN duration_seconds SET NOT NULL;
ALTER TABLE msgchains ALTER COLUMN duration_seconds SET DEFAULT 0.0;
-- Add duration tracking to toolcalls (nullable first)
ALTER TABLE toolcalls ADD COLUMN duration_seconds DOUBLE PRECISION NULL;
-- Calculate duration for existing toolcalls records (finished and failed only)
UPDATE toolcalls
SET duration_seconds = EXTRACT(EPOCH FROM (updated_at - created_at))
WHERE updated_at > created_at AND status IN ('finished', 'failed');
-- Set remaining NULL values to 0.0
UPDATE toolcalls
SET duration_seconds = 0.0
WHERE duration_seconds IS NULL;
-- Make column NOT NULL with default
ALTER TABLE toolcalls ALTER COLUMN duration_seconds SET NOT NULL;
ALTER TABLE toolcalls ALTER COLUMN duration_seconds SET DEFAULT 0.0;
-- Add task and subtask references to termlogs for better hierarchical tracking
ALTER TABLE termlogs ADD COLUMN flow_id BIGINT NULL REFERENCES flows(id) ON DELETE CASCADE;
ALTER TABLE termlogs ADD COLUMN task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE;
ALTER TABLE termlogs ADD COLUMN subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE;
-- Fill flow_id from related containers
UPDATE termlogs tl
SET flow_id = c.flow_id
FROM containers c
WHERE tl.container_id = c.id AND tl.flow_id IS NULL;
-- For any remaining NULL flow_id (shouldn't happen due to CASCADE, but just in case)
-- Fill with the first available flow_id
UPDATE termlogs
SET flow_id = (SELECT id FROM flows ORDER BY id LIMIT 1)
WHERE flow_id IS NULL;
-- Delete orphaned records if any still have NULL flow_id (no flows exist)
-- This shouldn't happen in practice due to CASCADE DELETE
DELETE FROM termlogs WHERE flow_id IS NULL;
-- Now make flow_id NOT NULL
ALTER TABLE termlogs ALTER COLUMN flow_id SET NOT NULL;
-- Add task and subtask references to screenshots for better hierarchical tracking
-- Note: flow_id already exists as NOT NULL in screenshots table
ALTER TABLE screenshots ADD COLUMN task_id BIGINT NULL REFERENCES tasks(id) ON DELETE CASCADE;
ALTER TABLE screenshots ADD COLUMN subtask_id BIGINT NULL REFERENCES subtasks(id) ON DELETE CASCADE;
-- Create indexes for termlogs foreign keys
CREATE INDEX termlogs_flow_id_idx ON termlogs(flow_id);
CREATE INDEX termlogs_task_id_idx ON termlogs(task_id);
CREATE INDEX termlogs_subtask_id_idx ON termlogs(subtask_id);
-- Create indexes for screenshots foreign keys
CREATE INDEX screenshots_task_id_idx ON screenshots(task_id);
CREATE INDEX screenshots_subtask_id_idx ON screenshots(subtask_id);
-- Index for soft delete filtering on flows (used in all analytics queries)
-- Using partial index because we mostly query non-deleted flows
CREATE INDEX flows_deleted_at_idx ON flows(deleted_at) WHERE deleted_at IS NULL;
-- Index for time-based analytics queries
CREATE INDEX msgchains_created_at_idx ON msgchains(created_at);
-- Index for grouping by model provider
CREATE INDEX msgchains_model_provider_idx ON msgchains(model_provider);
-- Index for grouping by model
CREATE INDEX msgchains_model_idx ON msgchains(model);
-- Composite index for queries that group by both model and provider
CREATE INDEX msgchains_model_provider_composite_idx ON msgchains(model, model_provider);
-- Composite index for time-based queries with flow filtering
-- This helps queries that filter by created_at AND join with flows
CREATE INDEX msgchains_created_at_flow_id_idx ON msgchains(created_at, flow_id);
-- Composite index for type-based analytics with flow filtering
CREATE INDEX msgchains_type_flow_id_idx ON msgchains(type, flow_id);
-- ==================== Toolcalls Analytics Indexes ====================
-- Index for time-based toolcalls analytics queries
CREATE INDEX toolcalls_created_at_idx ON toolcalls(created_at);
-- Index for updated_at to help with duration calculations
CREATE INDEX toolcalls_updated_at_idx ON toolcalls(updated_at);
-- Composite index for time-based queries with flow filtering
CREATE INDEX toolcalls_created_at_flow_id_idx ON toolcalls(created_at, flow_id);
-- Composite index for function-based analytics with flow filtering
CREATE INDEX toolcalls_name_flow_id_idx ON toolcalls(name, flow_id);
-- Composite index for status and timestamps (for duration calculations)
CREATE INDEX toolcalls_status_updated_at_idx ON toolcalls(status, updated_at);
-- ==================== Flows Analytics Indexes ====================
-- Index for time-based flows analytics queries
CREATE INDEX flows_created_at_idx ON flows(created_at) WHERE deleted_at IS NULL;
-- Index for tasks time-based analytics
CREATE INDEX tasks_created_at_idx ON tasks(created_at);
-- Index for subtasks time-based analytics
CREATE INDEX subtasks_created_at_idx ON subtasks(created_at);
-- Composite index for tasks with flow filtering
CREATE INDEX tasks_flow_id_created_at_idx ON tasks(flow_id, created_at);
-- Composite index for subtasks with task filtering
CREATE INDEX subtasks_task_id_created_at_idx ON subtasks(task_id, created_at);
-- Add usage privileges
INSERT INTO privileges (role_id, name) VALUES
(1, 'usage.admin'),
(1, 'usage.view'),
(2, 'usage.view')
ON CONFLICT DO NOTHING;
-- ==================== Assistants Analytics Indexes ====================
-- Partial index for soft delete filtering (used in almost all assistants queries)
CREATE INDEX assistants_deleted_at_idx ON assistants(deleted_at) WHERE deleted_at IS NULL;
-- Index for time-based queries and sorting
CREATE INDEX assistants_created_at_idx ON assistants(created_at);
-- Composite index for flow-scoped queries with soft delete filter
-- Optimizes: SELECT ... FROM assistants WHERE flow_id = $1 AND deleted_at IS NULL
CREATE INDEX assistants_flow_id_deleted_at_idx ON assistants(flow_id, deleted_at) WHERE deleted_at IS NULL;
-- Composite index for temporal analytics queries
-- Optimizes: GetFlowsStatsByDay* queries that join assistants with DATE(created_at) condition
CREATE INDEX assistants_flow_id_created_at_idx ON assistants(flow_id, created_at) WHERE deleted_at IS NULL;
-- ==================== Additional Analytics Indexes ====================
-- Composite index for subtasks filtering by task and status
-- Optimizes: GetTaskPlannedSubtasks, GetTaskCompletedSubtasks, analytics calculations
CREATE INDEX subtasks_task_id_status_idx ON subtasks(task_id, status);
-- Composite index for toolcalls filtering by flow and status
-- Optimizes: Analytics queries counting finished/failed toolcalls per flow
CREATE INDEX toolcalls_flow_id_status_idx ON toolcalls(flow_id, status);
-- Composite index for msgchains type-based analytics with hierarchy
-- Optimizes: Queries searching for specific msgchain types at task/subtask level
CREATE INDEX msgchains_type_task_id_subtask_id_idx ON msgchains(type, task_id, subtask_id);
-- Composite index for tasks with flow and status filtering
-- Optimizes: Flow-scoped task queries with status filtering
CREATE INDEX tasks_flow_id_status_idx ON tasks(flow_id, status);
-- Composite index for subtasks with status filtering (extended version)
-- Optimizes: Subtask analytics excluding created/waiting subtasks
CREATE INDEX subtasks_status_created_at_idx ON subtasks(status, created_at);
-- Composite index for toolcalls analytics by name and status
-- Optimizes: GetToolcallsStatsByFunction queries (filtering by status)
CREATE INDEX toolcalls_name_status_idx ON toolcalls(name, status);
-- Composite index for msgchains analytics by type and created_at
-- Optimizes: Time-based analytics grouped by msgchain type
CREATE INDEX msgchains_type_created_at_idx ON msgchains(type, created_at);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Drop termlogs indexes and columns
DROP INDEX IF EXISTS termlogs_flow_id_idx;
DROP INDEX IF EXISTS termlogs_task_id_idx;
DROP INDEX IF EXISTS termlogs_subtask_id_idx;
ALTER TABLE termlogs DROP COLUMN flow_id;
ALTER TABLE termlogs DROP COLUMN task_id;
ALTER TABLE termlogs DROP COLUMN subtask_id;
-- Drop screenshots indexes and columns
DROP INDEX IF EXISTS screenshots_task_id_idx;
DROP INDEX IF EXISTS screenshots_subtask_id_idx;
ALTER TABLE screenshots DROP COLUMN task_id;
ALTER TABLE screenshots DROP COLUMN subtask_id;
-- Drop msgchains usage tracking columns
ALTER TABLE msgchains DROP COLUMN usage_cache_in;
ALTER TABLE msgchains DROP COLUMN usage_cache_out;
ALTER TABLE msgchains DROP COLUMN usage_cost_in;
ALTER TABLE msgchains DROP COLUMN usage_cost_out;
ALTER TABLE msgchains DROP COLUMN duration_seconds;
-- Drop toolcalls duration tracking column
ALTER TABLE toolcalls DROP COLUMN duration_seconds;
-- Drop indexes
DROP INDEX IF EXISTS flows_deleted_at_idx;
DROP INDEX IF EXISTS msgchains_created_at_idx;
DROP INDEX IF EXISTS msgchains_model_provider_idx;
DROP INDEX IF EXISTS msgchains_model_idx;
DROP INDEX IF EXISTS msgchains_model_provider_composite_idx;
DROP INDEX IF EXISTS msgchains_created_at_flow_id_idx;
DROP INDEX IF EXISTS msgchains_type_flow_id_idx;
-- Drop toolcalls analytics indexes
DROP INDEX IF EXISTS toolcalls_created_at_idx;
DROP INDEX IF EXISTS toolcalls_updated_at_idx;
DROP INDEX IF EXISTS toolcalls_created_at_flow_id_idx;
DROP INDEX IF EXISTS toolcalls_name_flow_id_idx;
DROP INDEX IF EXISTS toolcalls_status_updated_at_idx;
-- Drop flows analytics indexes
DROP INDEX IF EXISTS flows_created_at_idx;
DROP INDEX IF EXISTS tasks_created_at_idx;
DROP INDEX IF EXISTS subtasks_created_at_idx;
DROP INDEX IF EXISTS tasks_flow_id_created_at_idx;
DROP INDEX IF EXISTS subtasks_task_id_created_at_idx;
-- Drop usage privileges
DELETE FROM privileges WHERE name IN ('usage.admin', 'usage.view');
-- Drop assistants analytics indexes
DROP INDEX IF EXISTS assistants_deleted_at_idx;
DROP INDEX IF EXISTS assistants_created_at_idx;
DROP INDEX IF EXISTS assistants_flow_id_deleted_at_idx;
DROP INDEX IF EXISTS assistants_flow_id_created_at_idx;
-- Drop additional analytics indexes
DROP INDEX IF EXISTS subtasks_task_id_status_idx;
DROP INDEX IF EXISTS toolcalls_flow_id_status_idx;
DROP INDEX IF EXISTS msgchains_type_task_id_subtask_id_idx;
DROP INDEX IF EXISTS tasks_flow_id_status_idx;
DROP INDEX IF EXISTS subtasks_status_created_at_idx;
DROP INDEX IF EXISTS toolcalls_name_status_idx;
DROP INDEX IF EXISTS msgchains_type_created_at_idx;
-- +goose StatementEnd
@@ -0,0 +1,67 @@
-- +goose Up
-- +goose StatementBegin
CREATE TYPE TOKEN_STATUS AS ENUM ('active', 'revoked');
CREATE TABLE api_tokens (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
token_id TEXT NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id BIGINT NOT NULL REFERENCES roles(id),
name TEXT NULL,
ttl BIGINT NOT NULL,
status TOKEN_STATUS NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ NULL,
CONSTRAINT api_tokens_token_id_unique UNIQUE (token_id)
);
-- Partial unique index for name per user (only when name is not null and not deleted)
CREATE UNIQUE INDEX api_tokens_name_user_unique_idx ON api_tokens(name, user_id)
WHERE name IS NOT NULL AND deleted_at IS NULL;
CREATE INDEX api_tokens_token_id_idx ON api_tokens(token_id);
CREATE INDEX api_tokens_user_id_idx ON api_tokens(user_id);
CREATE INDEX api_tokens_status_idx ON api_tokens(status);
CREATE INDEX api_tokens_deleted_at_idx ON api_tokens(deleted_at);
CREATE TRIGGER update_api_tokens_modified
BEFORE UPDATE ON api_tokens
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- Add privileges for Admin role (role_id = 1)
INSERT INTO privileges (role_id, name) VALUES
(1, 'settings.tokens.admin'),
(1, 'settings.tokens.create'),
(1, 'settings.tokens.view'),
(1, 'settings.tokens.edit'),
(1, 'settings.tokens.delete'),
(1, 'settings.tokens.subscribe')
ON CONFLICT DO NOTHING;
-- Add privileges for User role (role_id = 2)
INSERT INTO privileges (role_id, name) VALUES
(2, 'settings.tokens.create'),
(2, 'settings.tokens.view'),
(2, 'settings.tokens.edit'),
(2, 'settings.tokens.delete'),
(2, 'settings.tokens.subscribe')
ON CONFLICT DO NOTHING;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE name IN (
'settings.tokens.create',
'settings.tokens.view',
'settings.tokens.edit',
'settings.tokens.delete',
'settings.tokens.admin',
'settings.tokens.subscribe'
);
DROP INDEX IF EXISTS api_tokens_name_user_unique_idx;
DROP TABLE IF EXISTS api_tokens;
DROP TYPE IF EXISTS TOKEN_STATUS;
-- +goose StatementEnd
@@ -0,0 +1,44 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'settings.user.admin'),
(1, 'settings.user.view'),
(1, 'settings.user.edit'),
(1, 'settings.user.subscribe'),
(2, 'settings.user.view'),
(2, 'settings.user.edit'),
(2, 'settings.user.subscribe');
CREATE TABLE user_preferences (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
preferences JSONB NOT NULL DEFAULT '{"favoriteFlows": []}'::JSONB,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT user_preferences_user_id_unique UNIQUE (user_id)
);
CREATE INDEX user_preferences_user_id_idx ON user_preferences(user_id);
CREATE INDEX user_preferences_preferences_idx ON user_preferences USING GIN (preferences);
INSERT INTO user_preferences (user_id, preferences)
SELECT id, '{"favoriteFlows": []}'::JSONB FROM users
ON CONFLICT DO NOTHING;
CREATE OR REPLACE TRIGGER update_user_preferences_modified
BEFORE UPDATE ON user_preferences
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE user_preferences;
DELETE FROM privileges WHERE name IN (
'settings.user.admin',
'settings.user.view',
'settings.user.edit',
'settings.user.subscribe'
);
-- +goose StatementEnd
@@ -0,0 +1,52 @@
-- +goose Up
-- +goose StatementBegin
-- Add sploitus to the searchengine_type enum
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser',
'duckduckgo',
'perplexity',
'searxng',
'sploitus'
);
-- Update the searchlogs table to use the new enum type
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING engine::text::SEARCHENGINE_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
-- Ensure NOT NULL constraint is preserved
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Revert the changes by removing sploitus from the enum
CREATE TYPE SEARCHENGINE_TYPE_NEW AS ENUM (
'google',
'tavily',
'traversaal',
'browser',
'duckduckgo',
'perplexity',
'searxng'
);
-- Update the searchlogs table to use the reverted enum type
ALTER TABLE searchlogs
ALTER COLUMN engine TYPE SEARCHENGINE_TYPE_NEW USING engine::text::SEARCHENGINE_TYPE_NEW;
-- Drop the new type and rename the reverted one
DROP TYPE SEARCHENGINE_TYPE;
ALTER TYPE SEARCHENGINE_TYPE_NEW RENAME TO SEARCHENGINE_TYPE;
-- Ensure NOT NULL constraint is preserved
ALTER TABLE searchlogs
ALTER COLUMN engine SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,82 @@
-- +goose Up
-- +goose StatementBegin
-- Add Chinese AI providers to the provider_type enum
CREATE TYPE PROVIDER_TYPE_NEW AS ENUM (
'openai',
'anthropic',
'gemini',
'bedrock',
'ollama',
'custom',
'deepseek',
'glm',
'kimi',
'qwen'
);
-- Update columns to use the new enum type
ALTER TABLE providers
ALTER COLUMN type TYPE PROVIDER_TYPE_NEW USING type::text::PROVIDER_TYPE_NEW;
ALTER TABLE flows
ALTER COLUMN model_provider_type TYPE PROVIDER_TYPE_NEW USING model_provider_type::text::PROVIDER_TYPE_NEW;
ALTER TABLE assistants
ALTER COLUMN model_provider_type TYPE PROVIDER_TYPE_NEW USING model_provider_type::text::PROVIDER_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROVIDER_TYPE;
ALTER TYPE PROVIDER_TYPE_NEW RENAME TO PROVIDER_TYPE;
-- Ensure NOT NULL constraints are preserved
ALTER TABLE providers
ALTER COLUMN type SET NOT NULL;
ALTER TABLE flows
ALTER COLUMN model_provider_type SET NOT NULL;
ALTER TABLE assistants
ALTER COLUMN model_provider_type SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Delete providers using new types before reverting the enum
DELETE FROM providers WHERE type IN ('deepseek', 'glm', 'kimi', 'qwen');
DELETE FROM flows WHERE model_provider_type IN ('deepseek', 'glm', 'kimi', 'qwen');
DELETE FROM assistants WHERE model_provider_type IN ('deepseek', 'glm', 'kimi', 'qwen');
-- Create new enum type without the Chinese AI providers
CREATE TYPE PROVIDER_TYPE_NEW AS ENUM (
'openai',
'anthropic',
'gemini',
'bedrock',
'ollama',
'custom'
);
-- Update columns to use the new enum type
ALTER TABLE providers
ALTER COLUMN type TYPE PROVIDER_TYPE_NEW USING type::text::PROVIDER_TYPE_NEW;
ALTER TABLE flows
ALTER COLUMN model_provider_type TYPE PROVIDER_TYPE_NEW USING model_provider_type::text::PROVIDER_TYPE_NEW;
ALTER TABLE assistants
ALTER COLUMN model_provider_type TYPE PROVIDER_TYPE_NEW USING model_provider_type::text::PROVIDER_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROVIDER_TYPE;
ALTER TYPE PROVIDER_TYPE_NEW RENAME TO PROVIDER_TYPE;
-- Ensure NOT NULL constraints are preserved
ALTER TABLE providers
ALTER COLUMN type SET NOT NULL;
ALTER TABLE flows
ALTER COLUMN model_provider_type SET NOT NULL;
ALTER TABLE assistants
ALTER COLUMN model_provider_type SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,114 @@
-- +goose Up
-- +goose StatementBegin
-- Add new prompt types for agent supervision
CREATE TYPE PROMPT_TYPE_NEW AS ENUM (
'primary_agent',
'assistant',
'pentester',
'question_pentester',
'coder',
'question_coder',
'installer',
'question_installer',
'searcher',
'question_searcher',
'memorist',
'question_memorist',
'adviser',
'question_adviser',
'generator',
'subtasks_generator',
'refiner',
'subtasks_refiner',
'reporter',
'task_reporter',
'reflector',
'question_reflector',
'enricher',
'question_enricher',
'toolcall_fixer',
'input_toolcall_fixer',
'summarizer',
'image_chooser',
'language_chooser',
'flow_descriptor',
'task_descriptor',
'execution_logs',
'full_execution_context',
'short_execution_context',
'tool_call_id_collector',
'tool_call_id_detector',
'question_execution_monitor',
'question_task_planner',
'task_assignment_wrapper'
);
-- Update the searchlogs table to use the new enum type
ALTER TABLE prompts
ALTER COLUMN type TYPE PROMPT_TYPE_NEW USING type::text::PROMPT_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROMPT_TYPE;
ALTER TYPE PROMPT_TYPE_NEW RENAME TO PROMPT_TYPE;
-- Set the column as NOT NULL
ALTER TABLE prompts
ALTER COLUMN type SET NOT NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Revert the changes by removing agent supervision prompt types from the enum
CREATE TYPE PROMPT_TYPE_NEW AS ENUM (
'primary_agent',
'assistant',
'pentester',
'question_pentester',
'coder',
'question_coder',
'installer',
'question_installer',
'searcher',
'question_searcher',
'memorist',
'question_memorist',
'adviser',
'question_adviser',
'generator',
'subtasks_generator',
'refiner',
'subtasks_refiner',
'reporter',
'task_reporter',
'reflector',
'question_reflector',
'enricher',
'question_enricher',
'toolcall_fixer',
'input_toolcall_fixer',
'summarizer',
'image_chooser',
'language_chooser',
'flow_descriptor',
'task_descriptor',
'execution_logs',
'full_execution_context',
'short_execution_context',
'tool_call_id_collector',
'tool_call_id_detector'
);
-- Update the prompts table to use the new enum type
ALTER TABLE prompts
ALTER COLUMN type TYPE PROMPT_TYPE_NEW USING type::text::PROMPT_TYPE_NEW;
-- Drop the old type and rename the new one
DROP TYPE PROMPT_TYPE;
ALTER TYPE PROMPT_TYPE_NEW RENAME TO PROMPT_TYPE;
-- Set the column as NOT NULL
ALTER TABLE prompts
ALTER COLUMN type SET NOT NULL;
-- +goose StatementEnd
@@ -0,0 +1,49 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'templates.admin'),
(1, 'templates.create'),
(1, 'templates.view'),
(1, 'templates.edit'),
(1, 'templates.delete'),
(1, 'templates.subscribe'),
(2, 'templates.create'),
(2, 'templates.view'),
(2, 'templates.edit'),
(2, 'templates.delete'),
(2, 'templates.subscribe')
ON CONFLICT DO NOTHING;
CREATE TABLE flow_templates (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
text TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT flow_templates_title_not_empty CHECK (length(trim(title)) > 0),
CONSTRAINT flow_templates_text_not_empty CHECK (length(trim(text)) > 0)
);
CREATE INDEX flow_templates_user_id_idx ON flow_templates(user_id);
CREATE INDEX flow_templates_created_at_idx ON flow_templates(created_at DESC);
CREATE OR REPLACE TRIGGER update_flow_templates_modified
BEFORE UPDATE ON flow_templates
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS flow_templates;
DELETE FROM privileges WHERE name IN (
'templates.admin',
'templates.create',
'templates.view',
'templates.edit',
'templates.delete',
'templates.subscribe'
);
-- +goose StatementEnd
@@ -0,0 +1,85 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'flow_files.admin'),
(1, 'flow_files.view'),
(1, 'flow_files.upload'),
(1, 'flow_files.edit'),
(1, 'flow_files.delete'),
(1, 'flow_files.download'),
(1, 'flow_files.subscribe'),
(2, 'flow_files.view'),
(2, 'flow_files.upload'),
(2, 'flow_files.edit'),
(2, 'flow_files.delete'),
(2, 'flow_files.download'),
(2, 'flow_files.subscribe')
ON CONFLICT DO NOTHING;
INSERT INTO privileges (role_id, name) VALUES
(1, 'resources.admin'),
(1, 'resources.view'),
(1, 'resources.upload'),
(1, 'resources.edit'),
(1, 'resources.delete'),
(1, 'resources.download'),
(1, 'resources.subscribe'),
(2, 'resources.view'),
(2, 'resources.upload'),
(2, 'resources.edit'),
(2, 'resources.delete'),
(2, 'resources.download'),
(2, 'resources.subscribe')
ON CONFLICT DO NOTHING;
CREATE TABLE user_resources (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
hash TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL,
path TEXT NOT NULL,
size BIGINT NOT NULL DEFAULT 0,
is_dir BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT user_resources_user_path_unique UNIQUE (user_id, path),
CONSTRAINT user_resources_name_not_empty CHECK (length(trim(name)) > 0),
CONSTRAINT user_resources_path_not_empty CHECK (length(trim(path)) > 0)
);
CREATE INDEX user_resources_user_id_idx ON user_resources(user_id);
CREATE INDEX user_resources_hash_idx ON user_resources(hash) WHERE hash != '';
CREATE INDEX user_resources_path_idx ON user_resources USING btree (path text_pattern_ops);
CREATE INDEX user_resources_user_path_idx ON user_resources(user_id, path text_pattern_ops);
CREATE INDEX user_resources_updated_at_idx ON user_resources(updated_at DESC);
CREATE OR REPLACE TRIGGER update_user_resources_modified
BEFORE UPDATE ON user_resources
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS user_resources;
DELETE FROM privileges WHERE name IN (
'flow_files.admin',
'flow_files.view',
'flow_files.upload',
'flow_files.edit',
'flow_files.delete',
'flow_files.download',
'flow_files.subscribe'
);
DELETE FROM privileges WHERE name IN (
'resources.admin',
'resources.view',
'resources.upload',
'resources.edit',
'resources.delete',
'resources.download',
'resources.subscribe'
);
-- +goose StatementEnd
@@ -0,0 +1,108 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'knowledge.admin'),
(1, 'knowledge.view'),
(1, 'knowledge.create'),
(1, 'knowledge.edit'),
(1, 'knowledge.delete'),
(1, 'knowledge.search'),
(1, 'knowledge.subscribe'),
(2, 'knowledge.view'),
(2, 'knowledge.create'),
(2, 'knowledge.edit'),
(2, 'knowledge.delete'),
(2, 'knowledge.search'),
(2, 'knowledge.subscribe')
ON CONFLICT DO NOTHING;
-- Ensure the vector extension is present (langchaingo also creates it, but migrations
-- run before any flow starts, so we need it here for the backfill UPDATE below).
CREATE EXTENSION IF NOT EXISTS vector;
-- Logical namespace for embeddings.
-- DDL kept identical to what langchaingo's createCollectionTableIfNotExists generates
-- so IF NOT EXISTS is a true no-op when the table already exists.
CREATE TABLE IF NOT EXISTS langchain_pg_collection (
name varchar,
cmetadata json,
"uuid" uuid NOT NULL,
UNIQUE (name),
PRIMARY KEY ("uuid")
);
-- Physical storage for chunked text, vector embeddings and per-document metadata.
-- DDL kept identical to what langchaingo's createEmbeddingTableIfNotExists generates.
CREATE TABLE IF NOT EXISTS langchain_pg_embedding (
collection_id uuid,
embedding vector,
document varchar,
cmetadata json,
"uuid" uuid NOT NULL,
CONSTRAINT langchain_pg_embedding_collection_id_fkey
FOREIGN KEY (collection_id)
REFERENCES langchain_pg_collection ("uuid") ON DELETE CASCADE,
PRIMARY KEY ("uuid")
);
CREATE INDEX IF NOT EXISTS langchain_pg_embedding_collection_id
ON langchain_pg_embedding (collection_id);
-- Step 1: Backfill user_id from flows for documents whose flow still exists.
-- Documents whose flow_id has no matching row are left with user_id NULL.
UPDATE langchain_pg_embedding e
SET cmetadata = (e.cmetadata::jsonb || jsonb_build_object('user_id', f.user_id))::json
FROM langchain_pg_collection c, flows f
WHERE e.collection_id = c.uuid
AND c.name = 'langchain'
AND (e.cmetadata ->> 'flow_id') IS NOT NULL
AND (e.cmetadata ->> 'user_id') IS NULL
AND f.id = (e.cmetadata ->> 'flow_id')::bigint;
-- Step 2: For any remaining documents still missing user_id (no flow_id, deleted
-- flow, or manually created before user tracking), default to user_id = 1.
UPDATE langchain_pg_embedding e
SET cmetadata = (e.cmetadata::jsonb || jsonb_build_object('user_id', 1))::json
FROM langchain_pg_collection c
WHERE e.collection_id = c.uuid
AND c.name = 'langchain'
AND (e.cmetadata ->> 'user_id') IS NULL;
-- Purge accumulated memory documents from the vector store that belong to flows
-- which have already been soft-deleted (deleted_at IS NOT NULL) or no longer exist
-- in the flows table at all.
--
-- These documents are never queried again after their flow is gone, so keeping them
-- only wastes storage and degrades vector search performance.
--
-- The flows table uses soft deletes:
-- UPDATE flows SET deleted_at = CURRENT_TIMESTAMP WHERE id = $1
-- so the condition covers both hard-missing rows and soft-deleted ones.
DELETE FROM langchain_pg_embedding e
USING langchain_pg_collection c
WHERE e.collection_id = c.uuid
AND c.name = 'langchain'
AND COALESCE(e.cmetadata ->> 'doc_type', '') = 'memory'
AND (e.cmetadata ->> 'flow_id') IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM flows f
WHERE f.id = (e.cmetadata ->> 'flow_id')::bigint
AND f.deleted_at IS NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE name LIKE 'knowledge.%';
-- The langchain tables are intentionally NOT dropped in Down:
-- they hold production knowledge data managed by the pgvector store.
-- The deleted memory documents cannot be recovered (the embedding data is gone).
-- Down migration is intentionally a no-op.
-- +goose StatementEnd
@@ -0,0 +1,16 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'anonymize.call'),
(2, 'anonymize.call')
ON CONFLICT DO NOTHING;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE name = 'anonymize.call';
-- +goose StatementEnd
@@ -0,0 +1,17 @@
-- +goose Up
-- +goose StatementBegin
INSERT INTO privileges (role_id, name) VALUES
(1, 'toolcalls.admin'),
(1, 'toolcalls.view'),
(2, 'toolcalls.view')
ON CONFLICT DO NOTHING;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DELETE FROM privileges WHERE name IN ('toolcalls.admin', 'toolcalls.view');
-- +goose StatementEnd