-- ============================================================================
-- 睿池 ReachAI — 首次上线统一初始化脚本
-- 数据库：reach_ai（五服务第一阶段共用同一库；旧 ai-agent-service module 已删除）
--
-- 本脚本是当前唯一 SQL 基线，已合并历史服务目录和历史 upgrade 脚本中的表、
-- 补列、补索引、种子数据和必要清理结果。根目录 sql/ 保留 initV2.sql、
-- README.md 与当前仍需应用的 upgrade 脚本；后续 schema/索引/种子变化仍需
-- 新增当次 upgrade 脚本，确认已并入基线且开发/测试库不再需要后再清理。
--
-- 幂等设计：
--   - 建库 / 建表统一 IF NOT EXISTS；
--   - 列 / 索引增加走 information_schema 先判后执行的存储过程；
--   - sideEffect 回填 UPDATE 写明白了"仅覆盖 NULL/空/WRITE"，重复跑不会覆盖人工修正值。
--
-- 执行方式：
--   mysql -uroot -p < sql/initV2.sql
--
-- 首次上线后，常规业务建议通过应用的 Liquibase/Flyway 管理后续增量，
-- 本脚本仍可重复执行用于"对齐基线"，但生产变更请先备份。
-- ============================================================================

CREATE DATABASE IF NOT EXISTS `reach_ai`
    DEFAULT CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

USE `reach_ai`;

-- ----------------------------------------------------------------------------
-- 共用工具过程：add_col_if_absent / add_idx_if_absent
--   - MySQL 5.7 不支持 IF NOT EXISTS 语法在 ALTER TABLE ADD COLUMN / INDEX 上，
--     统一封成两个存储过程，避免"首次跑和二次跑行为不一致"。
-- ----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS add_col_if_absent;
DROP PROCEDURE IF EXISTS add_idx_if_absent;
DROP PROCEDURE IF EXISTS add_unique_idx_if_absent;

DELIMITER $$

CREATE PROCEDURE add_col_if_absent(
    IN p_table VARCHAR(64),
    IN p_column VARCHAR(64),
    IN p_definition TEXT
)
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = DATABASE()
          AND table_name   = p_table
          AND column_name  = p_column
    ) THEN
        SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END$$

CREATE PROCEDURE add_idx_if_absent(
    IN p_table VARCHAR(64),
    IN p_index VARCHAR(64),
    IN p_columns VARCHAR(255)
)
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.statistics
        WHERE table_schema = DATABASE()
          AND table_name   = p_table
          AND index_name   = p_index
    ) THEN
        SET @sql = CONCAT('CREATE INDEX `', p_index, '` ON `', p_table, '` (', p_columns, ')');
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END$$

CREATE PROCEDURE add_unique_idx_if_absent(
    IN p_table VARCHAR(64),
    IN p_index VARCHAR(64),
    IN p_columns VARCHAR(255)
)
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.statistics
        WHERE table_schema = DATABASE()
          AND table_name   = p_table
          AND index_name   = p_index
    ) THEN
        SET @sql = CONCAT('CREATE UNIQUE INDEX `', p_index, '` ON `', p_table, '` (', p_columns, ')');
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END IF;
END$$

DELIMITER ;


-- ============================================================================
-- 零、模型中心 V2（当前归属 reachai-model-service：发布目录、可执行实例与官方来源日同步证据）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `model_template` (
    `id`                        VARCHAR(64)   NOT NULL PRIMARY KEY,
    `name`                      VARCHAR(128)  NOT NULL COMMENT 'Template display name',
    `provider`                  VARCHAR(64)   NOT NULL COMMENT 'Provider key, e.g. openai/tongyi/deepseek',
    `model_type`                VARCHAR(32)   NOT NULL COMMENT 'LLM / EMBEDDING / RERANKER',
    `model_name`                VARCHAR(128)  NOT NULL COMMENT 'Default upstream model name',
    `protocol`                  VARCHAR(32)   NOT NULL DEFAULT 'OPENAI_COMPATIBLE' COMMENT 'Only OPENAI_COMPATIBLE in V2 phase 1',
    `connection_defaults_json`  MEDIUMTEXT    DEFAULT NULL COMMENT 'Non-secret connection defaults (baseUrl/paths/authHeader/authPrefix)',
    `credential_schema_json`    MEDIUMTEXT    DEFAULT NULL COMMENT 'Credential field schema; never stores real secrets',
    `default_options_json`      MEDIUMTEXT    DEFAULT NULL COMMENT 'Default model options JSON',
    `params_schema_json`        MEDIUMTEXT    DEFAULT NULL COMMENT 'UI parameter schema JSON',
    `capabilities_json`         MEDIUMTEXT    DEFAULT NULL COMMENT 'Capability metadata JSON',
    `source_key`                VARCHAR(96)   DEFAULT NULL COMMENT 'Last official catalog source key',
    `lifecycle_status`          VARCHAR(24)   NOT NULL DEFAULT 'UNKNOWN' COMMENT 'UNKNOWN / PREVIEW / ACTIVE / DEPRECATED / RETIRED',
    `recommendation_status`     VARCHAR(24)   NOT NULL DEFAULT 'UNASSESSED' COMMENT 'UNASSESSED / EVAL_VERIFIED / MANUAL',
    `recommendation_tier`       VARCHAR(24)   DEFAULT NULL COMMENT 'Optional governed recommendation tier; never set by catalog AI alone',
    `recommendation_reason`     VARCHAR(1000) DEFAULT NULL COMMENT 'EvalOps or manual recommendation evidence summary',
    `official_positioning`      VARCHAR(512)  DEFAULT NULL COMMENT 'Factual positioning extracted from official source',
    `released_at`               DATE          DEFAULT NULL,
    `deprecated_at`             DATE          DEFAULT NULL,
    `retire_at`                 DATE          DEFAULT NULL,
    `replacement_model_name`    VARCHAR(128)  DEFAULT NULL,
    `last_seen_at`              DATETIME      DEFAULT NULL COMMENT 'Last time exact model id appeared in a successful source snapshot',
    `last_verified_at`          DATETIME      DEFAULT NULL COMMENT 'Last successful official-source verification',
    `source_url`                VARCHAR(1000) DEFAULT NULL,
    `source_revision`           CHAR(64)      DEFAULT NULL COMMENT 'Official normalized source SHA-256',
    `sync_managed`              TINYINT(1)    NOT NULL DEFAULT 0 COMMENT '1=created by catalog sync; 0=bootstrap/manual template',
    `icon_key`                  VARCHAR(64)   DEFAULT NULL COMMENT 'Local icon key',
    `enabled`                   TINYINT(1)    NOT NULL DEFAULT 1 COMMENT '1=enabled in catalog',
    `sort_order`                INT           NOT NULL DEFAULT 0 COMMENT 'Catalog sort order ascending',
    `remark`                    VARCHAR(512)  DEFAULT NULL,
    `created_at`                DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `uk_model_template_catalog_key` (`provider`, `model_type`, `model_name`),
    KEY `idx_model_template_provider` (`provider`),
    KEY `idx_model_template_type` (`model_type`),
    KEY `idx_model_template_enabled_sort` (`enabled`, `sort_order`),
    KEY `idx_model_template_lifecycle` (`lifecycle_status`, `enabled`),
    KEY `idx_model_template_verified` (`last_verified_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Platform model catalog templates (no secrets)';

CREATE TABLE IF NOT EXISTS `model_instance` (
    `id`                       VARCHAR(64)   NOT NULL PRIMARY KEY,
    `name`                     VARCHAR(128)  NOT NULL COMMENT 'Human-readable model instance name',
    `provider`                 VARCHAR(64)   NOT NULL COMMENT 'Provider key',
    `model_type`               VARCHAR(32)   NOT NULL COMMENT 'LLM / EMBEDDING / RERANKER',
    `model_name`               VARCHAR(128)  NOT NULL COMMENT 'Upstream model name',
    `protocol`                 VARCHAR(32)   NOT NULL DEFAULT 'OPENAI_COMPATIBLE' COMMENT 'Only OPENAI_COMPATIBLE in V2 phase 1',
    `project_code`             VARCHAR(64)   DEFAULT NULL COMMENT 'NULL = global; non-null reserved for future project scope',
    `project_scope_key`        VARCHAR(64)   AS (IFNULL(`project_code`, '')) STORED COMMENT 'Generated unique-scope key from project_code',
    `connection_config_json`   MEDIUMTEXT    DEFAULT NULL COMMENT 'Encrypted full connection config JSON (aesgcm:)',
    `default_options_json`     MEDIUMTEXT    DEFAULT NULL COMMENT 'Default runtime options JSON',
    `params_schema_json`       MEDIUMTEXT    DEFAULT NULL COMMENT 'UI parameter schema JSON',
    `status`                   VARCHAR(32)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / DISABLED / ARCHIVED',
    `last_test_status`         VARCHAR(32)   NOT NULL DEFAULT 'UNKNOWN' COMMENT 'UNKNOWN / SUCCESS / FAILED',
    `last_test_at`             DATETIME      DEFAULT NULL,
    `last_test_latency_ms`     BIGINT        DEFAULT NULL,
    `last_test_error`          VARCHAR(1024) DEFAULT NULL COMMENT 'Truncated sanitized test error',
    `remark`                   VARCHAR(512)  DEFAULT NULL,
    `created_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `uk_model_instance_name_scope` (`name`, `project_scope_key`),
    KEY `idx_model_instance_provider` (`provider`),
    KEY `idx_model_instance_type` (`model_type`),
    KEY `idx_model_instance_status` (`status`),
    KEY `idx_model_instance_project` (`project_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Executable model instances bound by stable id (no template_id)';

CREATE TABLE IF NOT EXISTS `model_catalog_source` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `source_key`            VARCHAR(96)   NOT NULL,
    `provider`              VARCHAR(64)   NOT NULL,
    `name`                  VARCHAR(128)  NOT NULL,
    `source_kind`           VARCHAR(24)   NOT NULL COMMENT 'OFFICIAL_PAGE / STRUCTURED_API',
    `source_url`            VARCHAR(1000) NOT NULL,
    `allowed_host`          VARCHAR(255)  NOT NULL COMMENT 'Must also pass the code-owned official host allowlist',
    `content_format`        VARCHAR(16)   NOT NULL COMMENT 'HTML / MARKDOWN / JSON / TEXT',
    `auth_type`             VARCHAR(32)   NOT NULL DEFAULT 'NONE' COMMENT 'NONE or a code-owned provider credential mapping; never a secret',
    `trust_level`           VARCHAR(24)   NOT NULL DEFAULT 'OFFICIAL',
    `enabled`               TINYINT(1)    NOT NULL DEFAULT 1,
    `sort_order`            INT           NOT NULL DEFAULT 0,
    `last_http_etag`        VARCHAR(512)  DEFAULT NULL,
    `last_http_modified`    VARCHAR(128)  DEFAULT NULL,
    `last_content_sha256`   CHAR(64)      DEFAULT NULL,
    `last_checked_at`       DATETIME      DEFAULT NULL,
    `last_success_at`       DATETIME      DEFAULT NULL,
    `last_error_code`       VARCHAR(96)   DEFAULT NULL,
    `last_error_message`    VARCHAR(2000) DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_model_catalog_source_key` (`source_key`),
    KEY `idx_model_catalog_source_poll` (`enabled`, `sort_order`, `id`),
    KEY `idx_model_catalog_source_provider` (`provider`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Official model catalog source metadata; contains no credentials';

CREATE TABLE IF NOT EXISTS `model_catalog_setting` (
    `id`                    TINYINT       NOT NULL COMMENT 'Singleton row; always 1',
    `auto_sync_enabled`     TINYINT(1)    NOT NULL DEFAULT 0 COMMENT '0=manual only; 1=create automatic daily slots',
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Model catalog synchronization settings';

INSERT INTO `model_catalog_setting` (`id`, `auto_sync_enabled`)
VALUES (1, 0)
ON DUPLICATE KEY UPDATE `id` = VALUES(`id`);

CREATE TABLE IF NOT EXISTS `model_catalog_sync_run` (
    `id`                  BIGINT        NOT NULL AUTO_INCREMENT,
    `source_id`           BIGINT        NOT NULL,
    `business_date`       DATE          NOT NULL COMMENT 'Daily idempotency slot in configured business time zone',
    `trigger_type`        VARCHAR(24)   NOT NULL COMMENT 'STARTUP / DAILY / MANUAL',
    `status`              VARCHAR(24)   NOT NULL COMMENT 'PENDING / RETRY / LEASED / RUNNING / SUCCESS / NO_CHANGE / DEAD',
    `attempt_count`       INT           NOT NULL DEFAULT 0,
    `max_attempts`        INT           NOT NULL DEFAULT 3,
    `available_at`        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `lease_owner`         VARCHAR(128)  DEFAULT NULL,
    `lease_token`         VARCHAR(64)   DEFAULT NULL,
    `leased_until`        DATETIME      DEFAULT NULL,
    `http_status`         INT           DEFAULT NULL,
    `content_sha256`      CHAR(64)      DEFAULT NULL,
    `snapshot_id`         BIGINT        DEFAULT NULL,
    `analysis_status`     VARCHAR(32)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / ANALYZING / DETERMINISTIC / AI_SUCCESS / SKIPPED_UNCHANGED',
    `candidate_count`     INT           NOT NULL DEFAULT 0,
    `published_count`     INT           NOT NULL DEFAULT 0,
    `review_count`        INT           NOT NULL DEFAULT 0,
    `last_error_code`     VARCHAR(96)   DEFAULT NULL,
    `last_error_message`  VARCHAR(2000) DEFAULT NULL,
    `started_at`          DATETIME      DEFAULT NULL,
    `completed_at`        DATETIME      DEFAULT NULL,
    `created_at`          DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`          DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_model_catalog_sync_day` (`source_id`, `business_date`),
    KEY `idx_model_catalog_sync_poll` (`status`, `available_at`, `id`),
    KEY `idx_model_catalog_sync_lease` (`status`, `leased_until`, `id`),
    KEY `idx_model_catalog_sync_history` (`source_id`, `business_date`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='One leased model catalog synchronization slot per official source and business day';

CREATE TABLE IF NOT EXISTS `model_catalog_snapshot` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `source_id`             BIGINT        NOT NULL,
    `source_url`            VARCHAR(1000) NOT NULL,
    `content_sha256`        CHAR(64)      NOT NULL COMMENT 'SHA-256 of normalized public source content',
    `content_type`          VARCHAR(255)  DEFAULT NULL,
    `content_size_bytes`    INT           NOT NULL,
    `normalized_content`    MEDIUMTEXT    NOT NULL COMMENT 'Public untrusted source normalized to text/JSON; scripts are never executed',
    `response_headers_json` TEXT          DEFAULT NULL COMMENT 'Allowlisted ETag/Last-Modified/content headers only',
    `analysis_json`         MEDIUMTEXT    DEFAULT NULL COMMENT 'Strict analyzer output; candidate evidence, not direct instructions',
    `parser_version`        VARCHAR(64)   NOT NULL,
    `fetched_at`            DATETIME      NOT NULL,
    `analyzed_at`           DATETIME      DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_model_catalog_snapshot_hash` (`source_id`, `content_sha256`),
    KEY `idx_model_catalog_snapshot_time` (`source_id`, `fetched_at`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Immutable normalized official model source snapshot and bounded AI analysis';

CREATE TABLE IF NOT EXISTS `model_catalog_change` (
    `id`                    BIGINT         NOT NULL AUTO_INCREMENT,
    `run_id`                BIGINT         NOT NULL,
    `snapshot_id`           BIGINT         NOT NULL,
    `source_id`             BIGINT         NOT NULL,
    `provider`              VARCHAR(64)    NOT NULL,
    `model_name`            VARCHAR(128)   NOT NULL,
    `model_type`            VARCHAR(32)    NOT NULL,
    `change_type`           VARCHAR(32)    NOT NULL COMMENT 'DISCOVERED / METADATA_UPDATED / LIFECYCLE_CHANGED / OBSERVED',
    `proposed_json`         MEDIUMTEXT     NOT NULL,
    `evidence_json`         MEDIUMTEXT     NOT NULL COMMENT 'Source URL/hash/excerpt; no credentials',
    `confidence`            DECIMAL(6,4)   NOT NULL,
    `validation_status`     VARCHAR(24)    NOT NULL COMMENT 'VALID / REJECTED',
    `publish_status`        VARCHAR(24)    NOT NULL COMMENT 'PUBLISHED / NO_CHANGE / REVIEW_REQUIRED',
    `published_template_id` VARCHAR(64)    DEFAULT NULL,
    `review_reason`         VARCHAR(1000)  DEFAULT NULL,
    `published_at`          DATETIME       DEFAULT NULL,
    `created_at`            DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_model_catalog_change_candidate` (`run_id`, `provider`, `model_name`, `model_type`),
    KEY `idx_model_catalog_change_review` (`publish_status`, `created_at`, `id`),
    KEY `idx_model_catalog_change_template` (`published_template_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Validated model catalog candidate diff and publication evidence';

INSERT INTO `model_catalog_source`
(`source_key`, `provider`, `name`, `source_kind`, `source_url`, `allowed_host`,
 `content_format`, `auth_type`, `trust_level`, `enabled`, `sort_order`)
VALUES
('openai-models', 'openai', 'OpenAI 模型目录', 'OFFICIAL_PAGE',
 'https://developers.openai.com/api/docs/models/all.md', 'developers.openai.com',
 'MARKDOWN', 'NONE', 'OFFICIAL', 1, 10),
('openai-deprecations', 'openai', 'OpenAI 模型弃用公告', 'OFFICIAL_PAGE',
 'https://developers.openai.com/api/docs/deprecations.md', 'developers.openai.com',
 'MARKDOWN', 'NONE', 'OFFICIAL', 1, 20),
('tongyi-releases', 'tongyi', '阿里云百炼模型上架与更新', 'OFFICIAL_PAGE',
 'https://help.aliyun.com/zh/model-studio/newly-released-models', 'help.aliyun.com',
 'HTML', 'NONE', 'OFFICIAL', 1, 110),
('tongyi-retirements', 'tongyi', '阿里云百炼模型下线机制', 'OFFICIAL_PAGE',
 'https://help.aliyun.com/zh/model-studio/model-depreciation', 'help.aliyun.com',
 'HTML', 'NONE', 'OFFICIAL', 1, 120),
('anthropic-models', 'anthropic', 'Anthropic Claude 模型目录', 'OFFICIAL_PAGE',
 'https://platform.claude.com/docs/en/models/overview', 'platform.claude.com',
 'HTML', 'NONE', 'OFFICIAL', 1, 210),
('anthropic-deprecations', 'anthropic', 'Anthropic Claude 模型弃用公告', 'OFFICIAL_PAGE',
 'https://platform.claude.com/docs/en/about-claude/model-deprecations', 'platform.claude.com',
 'HTML', 'NONE', 'OFFICIAL', 1, 220),
('gemini-models', 'gemini', 'Google Gemini 模型目录', 'OFFICIAL_PAGE',
 'https://ai.google.dev/gemini-api/docs/models', 'ai.google.dev',
 'HTML', 'NONE', 'OFFICIAL', 1, 310),
('deepseek-models', 'deepseek', 'DeepSeek 模型目录', 'OFFICIAL_PAGE',
 'https://api-docs.deepseek.com/api/list-models/', 'api-docs.deepseek.com',
 'HTML', 'NONE', 'OFFICIAL', 1, 410)
ON DUPLICATE KEY UPDATE
    `provider` = VALUES(`provider`),
    `name` = VALUES(`name`),
    `source_kind` = VALUES(`source_kind`),
    `source_url` = VALUES(`source_url`),
    `allowed_host` = VALUES(`allowed_host`),
    `content_format` = VALUES(`content_format`),
    `auth_type` = VALUES(`auth_type`),
    `trust_level` = VALUES(`trust_level`),
    `sort_order` = VALUES(`sort_order`),
    `updated_at` = CURRENT_TIMESTAMP;

-- Catalog seeds live only in model_template. Fresh DBs may have zero model_instance rows.
INSERT IGNORE INTO `model_template`
(`id`, `name`, `provider`, `model_type`, `model_name`, `protocol`,
 `connection_defaults_json`, `credential_schema_json`, `default_options_json`, `params_schema_json`,
 `capabilities_json`, `icon_key`, `enabled`, `sort_order`, `remark`)
VALUES
-- OpenAI
('tpl-openai-gpt-5-2', 'OpenAI GPT-5.2', 'openai', 'LLM', 'gpt-5.2', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.openai.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'openai', 1, 10,
 'OpenAI frontier LLM template.'),
('tpl-openai-gpt-4-1-mini', 'OpenAI GPT-4.1 Mini', 'openai', 'LLM', 'gpt-4.1-mini', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.openai.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'openai', 1, 20,
 'OpenAI small general LLM template.'),
('tpl-openai-embedding-3-large', 'OpenAI Embedding 3 Large', 'openai', 'EMBEDDING', 'text-embedding-3-large', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.openai.com/v1","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":3072}', 'openai', 1, 30,
 'OpenAI high-quality text embedding template.'),
('tpl-openai-embedding-3-small', 'OpenAI Embedding 3 Small', 'openai', 'EMBEDDING', 'text-embedding-3-small', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.openai.com/v1","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":1536}', 'openai', 1, 40,
 'OpenAI cost-effective text embedding template.'),

-- Alibaba Cloud Model Studio / DashScope
('tpl-tongyi-qwen3-max', '通义千问 Qwen3-Max', 'tongyi', 'LLM', 'qwen3-max', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'tongyi', 1, 110,
 'Alibaba Cloud flagship Qwen LLM template.'),
('tpl-tongyi-qwen-plus', '通义千问 Qwen Plus', 'tongyi', 'LLM', 'qwen-plus', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'tongyi', 1, 120,
 'Alibaba Cloud balanced Qwen LLM template.'),
('tpl-tongyi-embedding-v4', '通义文本向量 V4', 'tongyi', 'EMBEDDING', 'text-embedding-v4', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://dashscope.aliyuncs.com/compatible-mode/v1","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":1024}', 'tongyi', 1, 130,
 'Alibaba Cloud text embedding template.'),

-- Anthropic
('tpl-anthropic-opus-4-7', 'Claude Opus 4.7', 'anthropic', 'LLM', 'claude-opus-4-7', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.anthropic.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"openaiCompatibleLayer":true,"supportsStreaming":true}', 'anthropic', 1, 210,
 'Anthropic Claude via official OpenAI-compatible endpoint. Not full Messages API parity; tool calling and advanced features depend on Anthropic OpenAI compat layer limits.'),
('tpl-anthropic-sonnet-4-6', 'Claude Sonnet 4.6', 'anthropic', 'LLM', 'claude-sonnet-4-6', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.anthropic.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"openaiCompatibleLayer":true,"supportsStreaming":true}', 'anthropic', 1, 220,
 'Anthropic Claude via official OpenAI-compatible endpoint. Not full Messages API parity; tool calling and advanced features depend on Anthropic OpenAI compat layer limits.'),
('tpl-anthropic-haiku-4-5', 'Claude Haiku 4.5', 'anthropic', 'LLM', 'claude-haiku-4-5', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.anthropic.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"openaiCompatibleLayer":true,"supportsStreaming":true}', 'anthropic', 1, 230,
 'Anthropic Claude via official OpenAI-compatible endpoint. Not full Messages API parity; tool calling and advanced features depend on Anthropic OpenAI compat layer limits.'),

-- Google Gemini
('tpl-gemini-2-5-pro', 'Gemini 2.5 Pro', 'gemini', 'LLM', 'gemini-2.5-pro', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://generativelanguage.googleapis.com/v1beta/openai","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'gemini', 1, 310,
 'Google Gemini advanced reasoning template.'),
('tpl-gemini-2-5-flash', 'Gemini 2.5 Flash', 'gemini', 'LLM', 'gemini-2.5-flash', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://generativelanguage.googleapis.com/v1beta/openai","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'gemini', 1, 320,
 'Google Gemini price-performance template.'),
('tpl-gemini-embedding', 'Gemini Embedding', 'gemini', 'EMBEDDING', 'gemini-embedding-001', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://generativelanguage.googleapis.com/v1beta/openai","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":3072}', 'gemini', 1, 330,
 'Google Gemini embedding template.'),

-- DeepSeek
('tpl-deepseek-v4-pro', 'DeepSeek V4 Pro', 'deepseek', 'LLM', 'deepseek-v4-pro', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.deepseek.com","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"max_tokens":16384,"thinking":{"type":"enabled"},"reasoning_effort":"high"}', '[]',
 '{"supportsTools":true,"supportsStreaming":true,"supportsReasoning":true}', 'deepseek', 1, 410,
 'DeepSeek reasoning-capable LLM template.'),
('tpl-deepseek-v4-flash', 'DeepSeek V4 Flash', 'deepseek', 'LLM', 'deepseek-v4-flash', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.deepseek.com","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":8192,"thinking":{"type":"disabled"}}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'deepseek', 1, 420,
 'DeepSeek fast LLM template.'),

-- Moonshot / Kimi
('tpl-kimi-k2-6', 'Kimi K2.6', 'kimi', 'LLM', 'kimi-k2.6', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.moonshot.ai/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'kimi', 1, 510,
 'Moonshot current Kimi family template; verify account model access.'),
('tpl-kimi-moonshot-128k', 'Moonshot v1 128K', 'kimi', 'LLM', 'moonshot-v1-128k', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.moonshot.ai/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'kimi', 1, 520,
 'Moonshot long-context text generation template.'),

-- SiliconFlow
('tpl-siliconflow-qwen3-32b', '硅基流动 Qwen3 32B', 'siliconflow', 'LLM', 'Qwen/Qwen3-32B', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.siliconflow.cn/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'siliconflow', 1, 610,
 'SiliconFlow open model LLM template.'),
('tpl-siliconflow-deepseek-v3-1', '硅基流动 DeepSeek V3.1', 'siliconflow', 'LLM', 'deepseek-ai/DeepSeek-V3.1', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.siliconflow.cn/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'siliconflow', 1, 620,
 'SiliconFlow DeepSeek hosted LLM template.'),
('tpl-siliconflow-bge-m3', '硅基流动 BGE-M3 向量', 'siliconflow', 'EMBEDDING', 'BAAI/bge-m3', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.siliconflow.cn/v1","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":1024}', 'siliconflow', 1, 630,
 'SiliconFlow embedding template.'),
('tpl-siliconflow-bge-reranker', '硅基流动 BGE 重排', 'siliconflow', 'RERANKER', 'BAAI/bge-reranker-v2-m3', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.siliconflow.cn/v1","rerankPath":"/rerank","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsRerank":true}', 'siliconflow', 1, 640,
 'SiliconFlow reranker template.'),

-- Azure OpenAI
('tpl-azure-gpt-4-1', 'Azure OpenAI GPT-4.1', 'azure-openai', 'LLM', 'gpt-4.1', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://{resource}.openai.azure.com/openai/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'azure-openai', 1, 710,
 'Azure OpenAI deployment template; replace model_name with your deployment if needed.'),
('tpl-azure-embedding-3-large', 'Azure OpenAI Embedding 3 Large', 'azure-openai', 'EMBEDDING', 'text-embedding-3-large', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://{resource}.openai.azure.com/openai/v1","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":3072}', 'azure-openai', 1, 720,
 'Azure OpenAI embedding template; replace model_name with your deployment if needed.'),

-- Tencent Hunyuan
('tpl-hunyuan-turbos', '腾讯混元 TurboS', 'tencent-hunyuan', 'LLM', 'hunyuan-turbos', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.hunyuan.cloud.tencent.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'tencent-hunyuan', 1, 810,
 'Tencent Hunyuan high-speed LLM template. Verify exact endpoint and model access.'),
('tpl-hunyuan-lite', '腾讯混元 Lite', 'tencent-hunyuan', 'LLM', 'hunyuan-lite', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://api.hunyuan.cloud.tencent.com/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'tencent-hunyuan', 1, 820,
 'Tencent Hunyuan lightweight LLM template. Verify exact endpoint and model access.'),

-- Baidu Qianfan
('tpl-qianfan-ernie-4-5-turbo', '千帆 ERNIE 4.5 Turbo', 'qianfan', 'LLM', 'ernie-4.5-turbo-128k', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://qianfan.baidubce.com/v2","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'qianfan', 1, 910,
 'Baidu Qianfan ERNIE template. Verify exact account endpoint and model access.'),
('tpl-qianfan-embedding-v1', '千帆 Embedding V1', 'qianfan', 'EMBEDDING', 'embedding-v1', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://qianfan.baidubce.com/v2","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":384}', 'qianfan', 1, 920,
 'Baidu Qianfan embedding template. Verify exact account endpoint and model access.'),

-- Volcano Engine / Doubao
('tpl-doubao-seed-1-6', '火山方舟 Doubao Seed 1.6', 'volcengine', 'LLM', 'doubao-seed-1-6', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://ark.cn-beijing.volces.com/api/v3","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true}', 'volcengine', 1, 1010,
 'Volcano Engine Ark Doubao LLM template. Replace with your endpoint model id if needed.'),
('tpl-doubao-embedding-large', '火山方舟 Doubao Embedding Large', 'volcengine', 'EMBEDDING', 'doubao-embedding-large-text-240915', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"https://ark.cn-beijing.volces.com/api/v3","embeddingPath":"/embeddings","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":true,"secret":true}]',
 '{}', '[]',
 '{"supportsEmbedding":true,"expectedDimension":2048}', 'volcengine', 1, 1020,
 'Volcano Engine Ark embedding template. Replace with your endpoint model id if needed.'),

-- Local / self-hosted
('tpl-ollama-qwen3-32b', 'Ollama Qwen3 32B', 'ollama', 'LLM', 'qwen3:32b', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"http://localhost:11434/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":false,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true,"local":true}', 'ollama', 1, 1110,
 'Local Ollama template; enable only when local model is pulled.'),
('tpl-vllm-qwen3-32b', 'vLLM Qwen3 32B', 'vllm', 'LLM', 'Qwen/Qwen3-32B', 'OPENAI_COMPATIBLE',
 '{"baseUrl":"http://localhost:8000/v1","chatPath":"/chat/completions","authHeader":"Authorization","authPrefix":"Bearer "}',
 '[{"key":"apiKey","label":"API Key","required":false,"secret":true}]',
 '{"temperature":0.7,"max_tokens":4096}', '[]',
 '{"supportsTools":true,"supportsStreaming":true,"local":true}', 'vllm', 1, 1120,
 'Self-hosted vLLM template; set baseUrl/modelName to deployed model.');


-- ============================================================================
-- 一、知识库模块（当前归属 reachai-knowledge-service，历史 v1 + v2）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `knowledge_base` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name`            VARCHAR(128) NOT NULL                COMMENT '知识库名称',
    `code`            VARCHAR(64)  NOT NULL                COMMENT '知识库编码（对应 Milvus collection 名称）',
    `description`     VARCHAR(512) DEFAULT NULL            COMMENT '描述',
    `embedding_model_instance_id` VARCHAR(64) DEFAULT NULL COMMENT 'Embedding model instance id',
    `rerank_model_instance_id` VARCHAR(64) DEFAULT NULL COMMENT 'Rerank model instance id',
    `llm_model_instance_id` VARCHAR(64) DEFAULT NULL COMMENT 'LLM model instance id for answer generation',
    `dimension`       INT          DEFAULT 1536            COMMENT '向量维度',
    `chunk_size`      INT          DEFAULT 500             COMMENT 'knowledge_chunk 切分大小（字符数）',
    `chunk_overlap`   INT          DEFAULT 50              COMMENT 'knowledge_chunk 重叠大小（字符数）',
    `split_type`      VARCHAR(32)  DEFAULT 'FIXED'         COMMENT '切分策略: FIXED / PARAGRAPH / SEMANTIC',
    `status`          TINYINT      DEFAULT 1               COMMENT '状态: 0-禁用 1-启用',
    `create_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库';

CREATE TABLE IF NOT EXISTS `knowledge_file_info` (
    `id`                BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `file_id`           VARCHAR(128) NOT NULL                COMMENT '文件业务ID（对外暴露）',
    `knowledge_base_id` BIGINT       NOT NULL                COMMENT '所属知识库ID',
    `file_name`         VARCHAR(256) DEFAULT NULL            COMMENT '文件名称',
    `file_type`         VARCHAR(32)  DEFAULT NULL            COMMENT '文件类型',
    `file_size`         BIGINT       DEFAULT 0               COMMENT '文件大小（字节）',
    `chunk_count`       INT          DEFAULT 0               COMMENT 'knowledge_chunk 数量',
    `status`            TINYINT      DEFAULT 0               COMMENT '状态: 0-处理中 1-已完成 2-失败',
    `raw_text`          LONGTEXT     DEFAULT NULL            COMMENT '清洗后的检索文本快照；重新解析必须使用 source_object_key',
    `source_object_key` VARCHAR(512) DEFAULT NULL            COMMENT '上传原件的对象存储 key',
    `source_content_type` VARCHAR(128) DEFAULT NULL          COMMENT '上传时声明的 MIME',
    `source_sha256`     CHAR(64)     DEFAULT NULL            COMMENT '上传原件 SHA-256',
    `parse_provider`    VARCHAR(32)  DEFAULT NULL            COMMENT 'JAVA_FAST / DOCLING',
    `parse_provider_version` VARCHAR(64) DEFAULT NULL        COMMENT '解析 Provider 版本',
    `parse_artifact_object_key` VARCHAR(512) DEFAULT NULL    COMMENT '结构化解析 JSON 的对象存储 key',
    `import_job_id`     VARCHAR(64)  DEFAULT NULL            COMMENT '产生文件记录的导入任务',
    `create_time`       DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`       DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_knowledge_file_file_id` (`file_id`),
    KEY `idx_kb_id` (`knowledge_base_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件信息';

CREATE TABLE IF NOT EXISTS `knowledge_document_import_job` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `job_id`                VARCHAR(64)  NOT NULL                COMMENT '对外导入任务ID',
    `file_id`               VARCHAR(128) NOT NULL                COMMENT '完成后对应 knowledge_file_info.file_id',
    `replace_file_id`       VARCHAR(128) DEFAULT NULL            COMMENT '完成后被替换的旧文件ID',
    `knowledge_base_id`     BIGINT       NOT NULL                COMMENT '所属知识库ID',
    `knowledge_base_code`   VARCHAR(64)  NOT NULL                COMMENT '提交时知识库编码快照',
    `tenant_id`             VARCHAR(96)  NOT NULL DEFAULT 'default' COMMENT 'Control 已验证租户快照',
    `created_by_actor_id`   VARCHAR(128) DEFAULT NULL            COMMENT 'Control 已验证任务创建用户',
    `workspace_id`          VARCHAR(64)  NOT NULL DEFAULT 'default' COMMENT '知识库 workspace 授权快照',
    `project_code`          VARCHAR(64)  DEFAULT NULL            COMMENT '知识库 project 授权快照',
    `resource_scope`        VARCHAR(20)  NOT NULL DEFAULT 'WORKSPACE' COMMENT 'SHARED / WORKSPACE / PROJECT 授权快照',
    `file_name`             VARCHAR(256) NOT NULL                COMMENT '原始文件名',
    `file_type`             VARCHAR(32)  NOT NULL                COMMENT '严格检测后的格式',
    `content_type`          VARCHAR(128) DEFAULT NULL            COMMENT '上传时声明 MIME',
    `file_size`             BIGINT       NOT NULL DEFAULT 0      COMMENT '原件字节数',
    `source_object_key`     VARCHAR(512) NOT NULL                COMMENT '原件对象存储 key',
    `source_sha256`         CHAR(64)     DEFAULT NULL            COMMENT '原件 SHA-256',
    `provider_type`         VARCHAR(32)  NOT NULL                COMMENT 'JAVA_FAST / DOCLING',
    `provider_version`      VARCHAR(64)  DEFAULT NULL            COMMENT '实际解析版本',
    `parse_artifact_object_key` VARCHAR(512) DEFAULT NULL        COMMENT '结构化解析 JSON 工件 key',
    `status`                VARCHAR(32)  NOT NULL                COMMENT 'QUEUED/PARSING/PARSED/INDEXING/COMPLETED/RETRY_WAIT/FAILED/CANCELLED',
    `stage`                 VARCHAR(32)  NOT NULL                COMMENT '当前阶段',
    `chunk_strategy`        VARCHAR(32)  NOT NULL DEFAULT 'fixed_length' COMMENT '切分策略',
    `chunk_size`            INT          NOT NULL DEFAULT 500    COMMENT '切分大小',
    `chunk_overlap`         INT          NOT NULL DEFAULT 50     COMMENT '切分重叠',
    `extra_params_json`     MEDIUMTEXT   DEFAULT NULL            COMMENT '非秘密扩展处理参数',
    `auto_commit`           TINYINT(1)   NOT NULL DEFAULT 0      COMMENT '解析成功后是否自动向量入库',
    `attempt_count`         INT          NOT NULL DEFAULT 0      COMMENT '同一 Provider 的解析尝试数',
    `max_attempts`          INT          NOT NULL DEFAULT 3      COMMENT '最大解析尝试数',
    `error_code`            VARCHAR(64)  DEFAULT NULL            COMMENT '稳定错误码',
    `error_message`         VARCHAR(1024) DEFAULT NULL           COMMENT '脱敏且截断的错误详情',
    `next_attempt_at`       DATETIME     DEFAULT NULL            COMMENT '重试可执行时间',
    `lease_owner`           VARCHAR(128) DEFAULT NULL            COMMENT '短租约 worker',
    `lease_until`           DATETIME     DEFAULT NULL            COMMENT '短租约截止时间',
    `parsed_at`             DATETIME     DEFAULT NULL            COMMENT '结构化结果持久化时间',
    `completed_at`          DATETIME     DEFAULT NULL            COMMENT '索引完成时间',
    `create_time`           DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `update_time`           DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_knowledge_document_import_job_id` (`job_id`),
    KEY `idx_knowledge_document_import_status_retry` (`status`, `next_attempt_at`),
    KEY `idx_knowledge_document_import_autocommit` (`status`, `auto_commit`, `parsed_at`),
    UNIQUE KEY `uk_knowledge_document_import_file_id` (`file_id`),
    KEY `idx_knowledge_document_import_kb` (`knowledge_base_id`, `create_time`),
    KEY `idx_knowledge_document_import_actor` (`tenant_id`, `created_by_actor_id`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识文档原件、解析工件和入库任务状态';

CREATE TABLE IF NOT EXISTS `knowledge_chunk` (
    `id`                BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
    `file_id`           VARCHAR(128)  NOT NULL                COMMENT '所属文件ID',
    `knowledge_base_id` BIGINT        NOT NULL                COMMENT '所属知识库ID',
    `content`           TEXT          NOT NULL                COMMENT '文本内容',
    `chunk_index`       INT           DEFAULT 0               COMMENT 'knowledge_chunk 在文件内的序号',
    `vector_id`         VARCHAR(256)  DEFAULT NULL            COMMENT 'Milvus 中的向量 ID',
    `collection_name`   VARCHAR(64)   DEFAULT NULL            COMMENT '关联的 collection 名称',
    `element_type`      VARCHAR(32)   DEFAULT NULL            COMMENT '结构化解析元素类型',
    `section_path`      VARCHAR(1024) DEFAULT NULL            COMMENT '元素标题路径',
    `source_locator_json` MEDIUMTEXT  DEFAULT NULL            COMMENT '页/幻灯片/单元格/坐标来源锚点',
    `create_time`       DATETIME      DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_knowledge_chunk_file_index` (`file_id`, `chunk_index`),
    KEY `idx_kb_id` (`knowledge_base_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文本块';

CREATE TABLE IF NOT EXISTS `knowledge_user_file_permission` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `user_id`         VARCHAR(128) NOT NULL                COMMENT '用户ID',
    `file_id`         VARCHAR(128) NOT NULL                COMMENT '文件业务ID',
    `permission_type` VARCHAR(16)  DEFAULT 'read'          COMMENT '权限类型: read / write / admin',
    `create_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_user_file` (`user_id`, `file_id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户文件权限';

-- Knowledge operations upgrade: enterprise scope, retrieval policy, direct return and rerank switches.
CALL add_col_if_absent('knowledge_base', 'workspace_id', 'VARCHAR(64) NOT NULL DEFAULT ''default'' COMMENT ''workspace isolation key'' AFTER `description`');
CALL add_col_if_absent('knowledge_base', 'project_code', 'VARCHAR(64) DEFAULT NULL COMMENT ''owning ReachAI project code'' AFTER `workspace_id`');
CALL add_col_if_absent('knowledge_base', 'scope', 'VARCHAR(20) NOT NULL DEFAULT ''WORKSPACE'' COMMENT ''SHARED / WORKSPACE / PROJECT'' AFTER `project_code`');
CALL add_col_if_absent('knowledge_base', 'search_mode', 'VARCHAR(20) NOT NULL DEFAULT ''hybrid'' COMMENT ''vector / keyword / hybrid'' AFTER `split_type`');
CALL add_col_if_absent('knowledge_base', 'top_k', 'INT NOT NULL DEFAULT 5 COMMENT ''default retrieval topK'' AFTER `search_mode`');
CALL add_col_if_absent('knowledge_base', 'similarity_threshold', 'FLOAT NOT NULL DEFAULT 0.5 COMMENT ''default retrieval threshold'' AFTER `top_k`');
CALL add_col_if_absent('knowledge_base', 'direct_return_enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''enable direct paragraph return'' AFTER `similarity_threshold`');
CALL add_col_if_absent('knowledge_base', 'direct_return_threshold', 'FLOAT NOT NULL DEFAULT 0.9 COMMENT ''direct return threshold'' AFTER `direct_return_enabled`');
CALL add_col_if_absent('knowledge_base', 'rerank_enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''enable rerank'' AFTER `direct_return_threshold`');
CALL add_col_if_absent('knowledge_base', 'vector_weight', 'FLOAT NOT NULL DEFAULT 0.7 COMMENT ''hybrid vector weight'' AFTER `rerank_enabled`');
CALL add_col_if_absent('knowledge_base', 'keyword_weight', 'FLOAT NOT NULL DEFAULT 0.3 COMMENT ''hybrid keyword weight'' AFTER `vector_weight`');
CALL add_col_if_absent('knowledge_chunk', 'title', 'VARCHAR(256) DEFAULT NULL COMMENT ''paragraph title'' AFTER `content`');
CALL add_col_if_absent('knowledge_chunk', 'hit_count', 'INT NOT NULL DEFAULT 0 COMMENT ''retrieval hit count'' AFTER `collection_name`');
CALL add_col_if_absent('knowledge_chunk', 'enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''paragraph enabled flag'' AFTER `hit_count`');
CALL add_col_if_absent('knowledge_file_info', 'source_object_key', 'VARCHAR(512) DEFAULT NULL COMMENT ''上传原件的对象存储 key'' AFTER `raw_text`');
CALL add_col_if_absent('knowledge_file_info', 'source_content_type', 'VARCHAR(128) DEFAULT NULL COMMENT ''上传时声明的 MIME'' AFTER `source_object_key`');
CALL add_col_if_absent('knowledge_file_info', 'source_sha256', 'CHAR(64) DEFAULT NULL COMMENT ''上传原件 SHA-256'' AFTER `source_content_type`');
CALL add_col_if_absent('knowledge_file_info', 'parse_provider', 'VARCHAR(32) DEFAULT NULL COMMENT ''JAVA_FAST / DOCLING'' AFTER `source_sha256`');
CALL add_col_if_absent('knowledge_file_info', 'parse_provider_version', 'VARCHAR(64) DEFAULT NULL COMMENT ''解析 Provider 版本'' AFTER `parse_provider`');
CALL add_col_if_absent('knowledge_file_info', 'parse_artifact_object_key', 'VARCHAR(512) DEFAULT NULL COMMENT ''结构化解析 JSON 的对象存储 key'' AFTER `parse_provider_version`');
CALL add_col_if_absent('knowledge_file_info', 'import_job_id', 'VARCHAR(64) DEFAULT NULL COMMENT ''产生文件记录的导入任务'' AFTER `parse_artifact_object_key`');
CALL add_col_if_absent('knowledge_chunk', 'element_type', 'VARCHAR(32) DEFAULT NULL COMMENT ''结构化解析元素类型'' AFTER `collection_name`');
CALL add_col_if_absent('knowledge_chunk', 'section_path', 'VARCHAR(1024) DEFAULT NULL COMMENT ''元素标题路径'' AFTER `element_type`');
CALL add_col_if_absent('knowledge_chunk', 'source_locator_json', 'MEDIUMTEXT DEFAULT NULL COMMENT ''页/幻灯片/单元格/坐标来源锚点'' AFTER `section_path`');
CALL add_idx_if_absent('knowledge_base', 'idx_kb_workspace_scope', '`workspace_id`, `scope`');
CALL add_idx_if_absent('knowledge_base', 'idx_kb_project_code', '`project_code`');
CALL add_idx_if_absent('knowledge_chunk', 'idx_kb_enabled', '`knowledge_base_id`, `enabled`');
CALL add_idx_if_absent('knowledge_chunk', 'idx_kb_hit_count', '`knowledge_base_id`, `hit_count`');
CALL add_unique_idx_if_absent('knowledge_file_info', 'uk_knowledge_file_file_id', '`file_id`');
CALL add_unique_idx_if_absent('knowledge_document_import_job', 'uk_knowledge_document_import_file_id', '`file_id`');
CALL add_unique_idx_if_absent('knowledge_chunk', 'uk_knowledge_chunk_file_index', '`file_id`, `chunk_index`');
CALL add_idx_if_absent('knowledge_chunk', 'idx_kb_created', '`knowledge_base_id`, `create_time`');
CALL add_idx_if_absent('knowledge_file_info', 'idx_kb_file_created', '`knowledge_base_id`, `create_time`');
CALL add_idx_if_absent('knowledge_file_info', 'idx_knowledge_file_import_job', '`import_job_id`');

CREATE TABLE IF NOT EXISTS `knowledge_tag` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `knowledge_base_id` BIGINT NOT NULL,
    `target_type` VARCHAR(32) NOT NULL DEFAULT 'KNOWLEDGE',
    `target_id` VARCHAR(128) DEFAULT NULL,
    `tag_key` VARCHAR(64) NOT NULL,
    `tag_value` VARCHAR(128) NOT NULL,
    `tag_group` VARCHAR(64) NOT NULL DEFAULT '默认',
    `color` VARCHAR(32) NOT NULL DEFAULT '#409EFF',
    `description` VARCHAR(512) DEFAULT NULL,
    `parent_id` BIGINT DEFAULT NULL,
    `sort_order` INT NOT NULL DEFAULT 0,
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_kb_target` (`knowledge_base_id`, `target_type`, `target_id`),
    KEY `idx_tag` (`tag_key`, `tag_value`),
    KEY `idx_kb_tag_library` (`knowledge_base_id`, `tag_group`, `tag_key`, `tag_value`),
    KEY `idx_kb_target_tag` (`knowledge_base_id`, `target_type`, `tag_key`, `tag_value`),
    KEY `idx_parent` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='knowledge operation tags';

CALL add_col_if_absent('knowledge_tag', 'tag_group', 'VARCHAR(64) NOT NULL DEFAULT ''默认'' COMMENT ''tag group'' AFTER `tag_value`');
CALL add_col_if_absent('knowledge_tag', 'color', 'VARCHAR(32) NOT NULL DEFAULT ''#409EFF'' COMMENT ''display color'' AFTER `tag_group`');
CALL add_col_if_absent('knowledge_tag', 'description', 'VARCHAR(512) DEFAULT NULL COMMENT ''tag description'' AFTER `color`');
CALL add_col_if_absent('knowledge_tag', 'parent_id', 'BIGINT DEFAULT NULL COMMENT ''parent tag id'' AFTER `description`');
CALL add_col_if_absent('knowledge_tag', 'sort_order', 'INT NOT NULL DEFAULT 0 COMMENT ''display order'' AFTER `parent_id`');
CALL add_idx_if_absent('knowledge_tag', 'idx_kb_tag_library', '`knowledge_base_id`, `tag_group`, `tag_key`, `tag_value`');
CALL add_idx_if_absent('knowledge_tag', 'idx_kb_target_tag', '`knowledge_base_id`, `target_type`, `tag_key`, `tag_value`');
CALL add_idx_if_absent('knowledge_tag', 'idx_parent', '`parent_id`');

CREATE TABLE IF NOT EXISTS `knowledge_question` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `knowledge_base_id` BIGINT NOT NULL,
    `chunk_id` BIGINT DEFAULT NULL,
    `question` VARCHAR(512) NOT NULL,
    `hit_count` INT NOT NULL DEFAULT 0,
    `source` VARCHAR(32) NOT NULL DEFAULT 'MANUAL',
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_kb_chunk` (`knowledge_base_id`, `chunk_id`),
    KEY `idx_question` (`question`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='knowledge question to paragraph mapping';

CREATE TABLE IF NOT EXISTS `knowledge_hit_log` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `knowledge_base_id` BIGINT NOT NULL,
    `chunk_id` BIGINT DEFAULT NULL,
    `query_text` VARCHAR(1024) NOT NULL,
    `search_mode` VARCHAR(20) DEFAULT NULL,
    `score` FLOAT DEFAULT NULL,
    `direct_return` TINYINT(1) NOT NULL DEFAULT 0,
    `trace_id` VARCHAR(128) DEFAULT NULL,
    `user_id` VARCHAR(128) DEFAULT NULL,
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_kb_time` (`knowledge_base_id`, `create_time`),
    KEY `idx_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='knowledge retrieval hit log';

CALL add_idx_if_absent('knowledge_hit_log', 'idx_chunk_time', '`chunk_id`, `create_time`');
CALL add_idx_if_absent('knowledge_hit_log', 'idx_kb_score_time', '`knowledge_base_id`, `score`, `create_time`');


-- ============================================================================
-- 二、业务语义索引模块（历史 v3）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `knowledge_business_index` (
    `id`              BIGINT       AUTO_INCREMENT PRIMARY KEY,
    `project_id`      BIGINT       DEFAULT NULL COMMENT '所属项目',
    `project_code`    VARCHAR(96)  DEFAULT NULL COMMENT '所属项目编码',
    `environment`     VARCHAR(32)  DEFAULT NULL COMMENT '环境',
    `tenant_id`       VARCHAR(96)  DEFAULT NULL COMMENT '租户',
    `index_code`      VARCHAR(64)  NOT NULL COMMENT '索引编码，唯一标识，对应 Milvus Collection 名称',
    `index_name`      VARCHAR(128) NOT NULL COMMENT '索引显示名称',
    `source_system`   VARCHAR(64)  NOT NULL COMMENT '来源系统标识，如 material_system、contract_system',
    `text_template`   TEXT         NOT NULL COMMENT '文本拼接模板，如：物资名称：{name}，规格：{spec}',
    `field_schema`    JSON         NOT NULL COMMENT '字段定义 JSON，描述模板中各占位符对应的字段名、标签、类型、是否必填等',
    `embedding_model_instance_id` VARCHAR(64) DEFAULT NULL COMMENT 'Embedding model instance id',
    `dimension`       INT          NOT NULL DEFAULT 1536 COMMENT '向量维度，需与 Embedding 模型输出一致',
    `chunk_size`      INT          NOT NULL DEFAULT 500 COMMENT '附件切分大小（字符数）',
    `chunk_overlap`   INT          NOT NULL DEFAULT 50 COMMENT '附件切分重叠（字符数）',
    `split_type`      VARCHAR(32)  NOT NULL DEFAULT 'FIXED' COMMENT '附件切分策略: FIXED / PARAGRAPH / SEMANTIC',
    `status`          VARCHAR(16)  NOT NULL DEFAULT 'ACTIVE' COMMENT '状态: ACTIVE-启用 / INACTIVE-停用',
    `remark`          VARCHAR(512) DEFAULT NULL COMMENT '备注说明',
    `agent_memory_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否允许生成非权威 Agent 业务记忆引用',
    `resolver_capability_key` VARCHAR(128) DEFAULT NULL COMMENT '按当前业务身份回源并重新鉴权的 Capability key',
    `create_time`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `update_time`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `uk_index_code` (`index_code`),
    KEY `idx_biz_agent_memory` (`tenant_id`, `project_code`, `agent_memory_enabled`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务语义索引注册表';

CREATE TABLE IF NOT EXISTS `knowledge_business_index_record` (
    `id`              BIGINT       AUTO_INCREMENT PRIMARY KEY,
    `index_code`      VARCHAR(64)  NOT NULL COMMENT '所属索引编码',
    `biz_id`          VARCHAR(128) NOT NULL COMMENT '业务主键（由业务系统定义，如合同编号、物资编号）',
    `biz_type`        VARCHAR(64)  DEFAULT NULL COMMENT '业务子类型（可选，业务系统自定义分类）',
    `search_text`     TEXT         NOT NULL COMMENT '由模板渲染生成的索引文本',
    `fields_json`     JSON         DEFAULT NULL COMMENT '业务系统推送的原始字段（便于模板变更后重建索引）',
    `metadata_json`   JSON         DEFAULT NULL COMMENT '元数据（搜索结果中回显的摘要信息，不参与语义搜索）',
    `source_version`  VARCHAR(128) DEFAULT NULL COMMENT '业务源版本；Agent 回源时用于新鲜度校验',
    `source_updated_at` DATETIME   DEFAULT NULL COMMENT '业务源更新时间；不替代 source_version',
    `owner_user_id`   VARCHAR(64)  DEFAULT NULL COMMENT '数据所有者用户 ID（用于权限过滤）',
    `owner_org_id`    VARCHAR(64)  DEFAULT NULL COMMENT '数据所属组织 ID（用于权限过滤）',
    `vector_id`       VARCHAR(128) DEFAULT NULL COMMENT '主记录在 Milvus 中的向量 ID',
    `has_attachment`  TINYINT(1)   NOT NULL DEFAULT 0 COMMENT '是否包含附件：0-无 1-有',
    `status`          VARCHAR(16)  NOT NULL DEFAULT 'ACTIVE' COMMENT '状态: ACTIVE-正常 / DELETED-已删除',
    `create_time`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `update_time`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY `uk_index_biz` (`index_code`, `biz_id`),
    INDEX `idx_owner_org`  (`index_code`, `owner_org_id`),
    INDEX `idx_owner_user` (`index_code`, `owner_user_id`),
    INDEX `idx_biz_source_version` (`index_code`, `biz_id`, `source_version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务索引记录表';

CREATE TABLE IF NOT EXISTS `knowledge_business_index_attachment` (
    `id`              BIGINT       AUTO_INCREMENT PRIMARY KEY,
    `index_code`      VARCHAR(64)  NOT NULL COMMENT '所属索引编码',
    `biz_id`          VARCHAR(128) NOT NULL COMMENT '关联的业务主键',
    `record_id`       BIGINT       NOT NULL COMMENT '关联 knowledge_business_index_record.id',
    `file_name`       VARCHAR(256) NOT NULL COMMENT '附件原始文件名',
    `file_type`       VARCHAR(32)  DEFAULT NULL COMMENT '文件类型（pdf / docx / txt 等）',
    `raw_text`        MEDIUMTEXT   DEFAULT NULL COMMENT '附件解析后的完整原始文本（用于重建索引）',
    `chunk_index`     INT          NOT NULL COMMENT '切分序号（从 0 开始）',
    `chunk_content`   TEXT         NOT NULL COMMENT '切分后的文本片段',
    `vector_id`       VARCHAR(128) DEFAULT NULL COMMENT '该 Chunk 在 Milvus 中的向量 ID',
    `status`          VARCHAR(16)  NOT NULL DEFAULT 'ACTIVE',
    `create_time`     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    INDEX `idx_biz`    (`index_code`, `biz_id`),
    INDEX `idx_record` (`record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='附件索引表（Chunk 级别）';


-- ============================================================================
-- 三、Tool 扫描模块：项目 / 模块 / 扫描工具（历史 v1 + v5 + v6 + v7）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_scan_project` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name`          VARCHAR(128) NOT NULL                COMMENT '项目名称',
    `base_url`      VARCHAR(256) NOT NULL                COMMENT '项目域名',
    `context_path`  VARCHAR(128) NOT NULL DEFAULT ''     COMMENT '公共路径前缀',
    `scan_path`     VARCHAR(512) NOT NULL                COMMENT '磁盘扫描目录',
    `scan_type`     VARCHAR(32)  NOT NULL                COMMENT '扫描方式: openapi/controller/auto',
    `spec_file`     VARCHAR(256) DEFAULT NULL            COMMENT 'OpenAPI 规范文件相对路径',
    `tool_count`    INT          NOT NULL DEFAULT 0      COMMENT '扫描发现的接口数',
    `status`        VARCHAR(32)  NOT NULL DEFAULT 'created' COMMENT '状态: created/scanning/scanned/failed',
    `error_message` TEXT         DEFAULT NULL            COMMENT '失败原因',
    `auth_type`          VARCHAR(32)  NOT NULL DEFAULT 'none' COMMENT '鉴权: none / api_key',
    `auth_api_key_in`    VARCHAR(16)  DEFAULT NULL         COMMENT 'api_key 时: header / query',
    `auth_api_key_name`  VARCHAR(128) DEFAULT NULL         COMMENT 'API Key 参数名',
    `auth_api_key_value` TEXT         DEFAULT NULL         COMMENT 'API Key 参数值',
    `scan_settings`    JSON         DEFAULT NULL          COMMENT '扫描项 JSON 配置',
    `last_scanned_at`  DATETIME     DEFAULT NULL         COMMENT '上次成功扫描时间（增量基线）',
    `create_time`   DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`   DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_scan_project_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='扫描项目表';

-- 已存在、且建表时尚无鉴权列的旧库：由下列调用补齐；新库建表已含四列，此处为幂等 no-op
CALL add_col_if_absent('capability_scan_project', 'auth_type',          'VARCHAR(32) NOT NULL DEFAULT ''none'' COMMENT ''鉴权: none / api_key'' AFTER `error_message`');
CALL add_col_if_absent('capability_scan_project', 'auth_api_key_in',    'VARCHAR(16) DEFAULT NULL COMMENT ''api_key 时: header / query'' AFTER `auth_type`');
CALL add_col_if_absent('capability_scan_project', 'auth_api_key_name',  'VARCHAR(128) DEFAULT NULL COMMENT ''API Key 参数名'' AFTER `auth_api_key_in`');
CALL add_col_if_absent('capability_scan_project', 'auth_api_key_value', 'TEXT DEFAULT NULL COMMENT ''API Key 参数值'' AFTER `auth_api_key_name`');
CALL add_col_if_absent('capability_scan_project', 'scan_settings',    'JSON DEFAULT NULL COMMENT ''扫描项 JSON 配置'' AFTER `auth_api_key_value`');
CALL add_col_if_absent('capability_scan_project', 'last_scanned_at', 'DATETIME DEFAULT NULL COMMENT ''上次成功扫描时间（增量基线）'' AFTER `scan_settings`');

CREATE TABLE IF NOT EXISTS `capability_scan_module` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `project_id`      BIGINT       NOT NULL                COMMENT '所属扫描项目',
    `name`            VARCHAR(128) NOT NULL                COMMENT '模块唯一名，默认=Controller 类名',
    `display_name`    VARCHAR(256) DEFAULT NULL            COMMENT '用户可编辑显示名',
    `source_classes`  TEXT         DEFAULT NULL            COMMENT '合并后聚合的 Controller 类名 JSON 数组',
    `create_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_scan_module_project_name` (`project_id`, `name`),
    KEY `idx_scan_module_project` (`project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='扫描项目模块表';

CREATE TABLE IF NOT EXISTS `capability_scan_project_tool` (
    `id`                  BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `project_id`          BIGINT       NOT NULL                COMMENT '扫描项目 ID',
    `module_id`           BIGINT       DEFAULT NULL            COMMENT '扫描模块 ID',
    `name`                VARCHAR(128) NOT NULL                COMMENT '项目内工具名（snake_case）',
    `title`               VARCHAR(192) NOT NULL                COMMENT '用户可读的简短工具名称',
    `description`         TEXT         NOT NULL                COMMENT '描述',
    `parameters_json`     TEXT         DEFAULT NULL            COMMENT '参数定义 JSON',
    `source`              VARCHAR(32)  NOT NULL DEFAULT 'scanner' COMMENT '来源: scanner',
    `source_location`     VARCHAR(512) DEFAULT NULL            COMMENT '来源定位',
    `http_method`         VARCHAR(8)   DEFAULT NULL            COMMENT 'HTTP 方法',
    `base_url`            VARCHAR(256) DEFAULT NULL            COMMENT '目标服务基础地址',
    `context_path`        VARCHAR(128) DEFAULT NULL            COMMENT '服务公共前缀',
    `endpoint_path`       VARCHAR(256) DEFAULT NULL            COMMENT '接口路径',
    `request_body_type`   VARCHAR(256) DEFAULT NULL            COMMENT '请求体类型',
    `response_type`       VARCHAR(256) DEFAULT NULL            COMMENT '响应类型',
    `ai_description`      VARCHAR(1024) DEFAULT NULL           COMMENT 'AI 摘要（冗余）',
    `capability_metadata_json` MEDIUMTEXT DEFAULT NULL         COMMENT '@ReachCapability 能力声明元数据 JSON',
    `sensitive_data_json` TEXT         DEFAULT NULL            COMMENT '敏感数据扫描结果 JSON',
    `enabled`             TINYINT      NOT NULL DEFAULT 0      COMMENT '是否启用',
    `global_tool_definition_id` BIGINT DEFAULT NULL            COMMENT '已注册为全局 capability_tool_definition.id，未注册为 NULL',
    `create_time`         DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`         DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project_tool_name` (`project_id`, `name`),
    KEY `idx_project_id` (`project_id`),
    KEY `idx_module_id`  (`module_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='扫描项目接口（未注册为全局 Tool 前）';

CALL add_col_if_absent('capability_scan_project_tool', 'title', 'VARCHAR(192) DEFAULT NULL COMMENT ''用户可读的简短工具名称'' AFTER `name`');
UPDATE `capability_scan_project_tool` SET `title` = `name` WHERE `title` IS NULL OR TRIM(`title`) = '';
ALTER TABLE `capability_scan_project_tool` MODIFY COLUMN `title` VARCHAR(192) NOT NULL COMMENT '用户可读的简短工具名称';
CALL add_col_if_absent('capability_scan_project_tool', 'capability_metadata_json', 'MEDIUMTEXT DEFAULT NULL COMMENT ''@ReachCapability 能力声明元数据 JSON'' AFTER `ai_description`');
CALL add_col_if_absent('capability_scan_project_tool', 'sensitive_data_json', 'TEXT DEFAULT NULL COMMENT ''敏感数据扫描结果 JSON'' AFTER `capability_metadata_json`');
CALL add_col_if_absent('capability_scan_project_tool', 'removed_from_source', 'TINYINT NOT NULL DEFAULT 0 COMMENT ''1=扫描或 SDK 源中已无此接口（墓碑行，可能仍关联全局 Tool）'' AFTER `global_tool_definition_id`');
CALL add_col_if_absent('capability_scan_project_tool', 'removed_at', 'DATETIME DEFAULT NULL COMMENT ''标记为从源移除的时间'' AFTER `removed_from_source`');

CREATE TABLE IF NOT EXISTS `capability_semantic_doc` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `level`           VARCHAR(16)  NOT NULL                COMMENT '层级: project / module / tool',
    `project_id`      BIGINT       DEFAULT NULL            COMMENT '归属项目 ID',
    `module_id`       BIGINT       DEFAULT NULL            COMMENT '归属模块 ID（level=module/tool 时有值）',
    `tool_id`         BIGINT       DEFAULT NULL            COMMENT '归属工具 ID（level=tool 时有值）',
    `content_md`      MEDIUMTEXT   DEFAULT NULL            COMMENT 'LLM 生成/人工编辑后的 Markdown 文档',
    `prompt_version`  VARCHAR(32)  DEFAULT NULL            COMMENT '生成使用的 prompt 版本',
    `model_name`      VARCHAR(64)  DEFAULT NULL            COMMENT '生成使用的模型名',
    `token_usage`     INT          NOT NULL DEFAULT 0      COMMENT '单次生成消耗的 total_tokens',
    `status`          VARCHAR(16)  NOT NULL DEFAULT 'draft' COMMENT '状态: draft / generated / edited',
    `create_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`     DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_semantic_doc_ref`      (`level`, `project_id`, `module_id`, `tool_id`),
    KEY        `idx_semantic_doc_project` (`project_id`),
    KEY        `idx_semantic_doc_module`  (`module_id`),
    KEY        `idx_semantic_doc_tool`    (`tool_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='三层语义文档表';


-- ============================================================================
-- 四、Tool 能力表
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_tool_definition` (
    `id`                  BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name`                VARCHAR(128)  NOT NULL                COMMENT '能力唯一标识 (snake_case)',
    `title`               VARCHAR(192)  NOT NULL                COMMENT '用户可读的简短工具名称',
    `description`         TEXT          NOT NULL                COMMENT '能力描述',
    `ai_description`      MEDIUMTEXT    DEFAULT NULL            COMMENT 'LLM 生成的业务语义描述（Agent 运行时优先使用）',
    `capability_metadata_json` MEDIUMTEXT DEFAULT NULL          COMMENT '@ReachCapability 能力声明元数据 JSON',
    `parameters_json`     TEXT          DEFAULT NULL            COMMENT '参数定义 JSON',
    `source`              VARCHAR(32)   NOT NULL DEFAULT 'manual' COMMENT '来源: code/scanner/manual',
    `source_location`     VARCHAR(512)  DEFAULT NULL            COMMENT '来源详情',
    `http_method`         VARCHAR(8)    DEFAULT NULL            COMMENT 'HTTP 方法',
    `base_url`            VARCHAR(256)  DEFAULT NULL            COMMENT '目标服务基础地址',
    `context_path`        VARCHAR(128)  DEFAULT NULL            COMMENT '服务公共前缀',
    `endpoint_path`       VARCHAR(256)  DEFAULT NULL            COMMENT '接口路径 (不含 contextPath)',
    `request_body_type`   VARCHAR(256)  DEFAULT NULL            COMMENT '请求体类型',
    `response_type`       VARCHAR(256)  DEFAULT NULL            COMMENT '响应类型',
    `project_id`          BIGINT        DEFAULT NULL            COMMENT '关联的扫描项目 ID',
    `module_id`           BIGINT        DEFAULT NULL            COMMENT '所属模块（capability_scan_module.id）',
    `enabled`             TINYINT       NOT NULL DEFAULT 1      COMMENT '是否启用',
    `side_effect`         VARCHAR(24)   NOT NULL DEFAULT 'WRITE' COMMENT '副作用等级: NONE / READ_ONLY / IDEMPOTENT_WRITE / WRITE / IRREVERSIBLE',
    `create_time`         DATETIME      DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`         DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_name`                  (`name`),
    KEY        `idx_project_id`           (`project_id`),
    KEY        `idx_tool_module_id`       (`module_id`),
    KEY        `idx_enabled`              (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tool 能力表';

-- 兼容老库：如果 capability_tool_definition 已存在但缺少当前基线列，这里补齐（CREATE TABLE IF NOT EXISTS 不会重建）
CALL add_col_if_absent('capability_tool_definition', 'title',            'VARCHAR(192) DEFAULT NULL COMMENT ''用户可读的简短工具名称'' AFTER `name`');
UPDATE `capability_tool_definition` SET `title` = `name` WHERE `title` IS NULL OR TRIM(`title`) = '';
ALTER TABLE `capability_tool_definition` MODIFY COLUMN `title` VARCHAR(192) NOT NULL COMMENT '用户可读的简短工具名称';
CALL add_col_if_absent('capability_tool_definition', 'ai_description',   'MEDIUMTEXT DEFAULT NULL COMMENT ''LLM 生成的业务语义描述'' AFTER `description`');
CALL add_col_if_absent('capability_tool_definition', 'capability_metadata_json', 'MEDIUMTEXT DEFAULT NULL COMMENT ''@ReachCapability 能力声明元数据 JSON'' AFTER `ai_description`');
CALL add_col_if_absent('capability_tool_definition', 'project_id',       'BIGINT DEFAULT NULL COMMENT ''关联的扫描项目 ID'' AFTER `response_type`');
CALL add_col_if_absent('capability_tool_definition', 'module_id',        'BIGINT DEFAULT NULL COMMENT ''所属模块'' AFTER `project_id`');
CALL add_col_if_absent('capability_tool_definition', 'side_effect',      'VARCHAR(24) NOT NULL DEFAULT ''WRITE'' COMMENT ''副作用等级'' AFTER `enabled`');
CALL add_idx_if_absent('capability_tool_definition', 'idx_project_id',           'project_id');
CALL add_idx_if_absent('capability_tool_definition', 'idx_tool_module_id',       'module_id');
CALL add_idx_if_absent('capability_tool_definition', 'idx_enabled', 'enabled');


-- ============================================================================
-- 五、Runtime 根运行事实与执行子事件
-- ============================================================================

CREATE TABLE IF NOT EXISTS `runtime_internal_auth_nonce` (
    `nonce`      VARCHAR(128) NOT NULL                COMMENT 'Control→Runtime HMAC nonce',
    `caller`     VARCHAR(96)  NOT NULL                COMMENT '调用方服务 ID',
    `created_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '写入时间',
    PRIMARY KEY (`nonce`),
    KEY `idx_runtime_internal_auth_nonce_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime internal service auth nonce anti-replay store';

CREATE TABLE IF NOT EXISTS `runtime_conversation_session` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `tenant_id`               VARCHAR(96)  NOT NULL COMMENT '经 Control HMAC 认证的租户；旧协议固定为 default',
    `session_id`              VARCHAR(128) NOT NULL COMMENT '对外会话 ID',
    `user_id`                 VARCHAR(128) NOT NULL COMMENT '经 Control HMAC 认证的 Runtime 用户 ID',
    `agent_id`                VARCHAR(64)  NOT NULL COMMENT '会话绑定 Agent ID',
    `agent_config_version_id` BIGINT       DEFAULT NULL COMMENT '最近使用的 Agent 配置版本',
    `project_code`            VARCHAR(96)  DEFAULT NULL COMMENT 'Agent 所属项目编码快照',
    `identity_source`         VARCHAR(32)  NOT NULL COMMENT 'AGENT / EMBED_SESSION',
    `state_user_key`          VARCHAR(80)  NOT NULL COMMENT 'AgentScope 状态用户键（哈希后）',
    `state_session_key`       VARCHAR(160) NOT NULL COMMENT 'AgentScope 状态会话键（Agent + session 哈希）',
    `status`                  VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / CLEARING / CLEARED / EXPIRED / PURGING',
    `event_count`             INT          NOT NULL DEFAULT 0 COMMENT '已写入事件序号上界',
    `turn_lease_owner`        VARCHAR(64)  DEFAULT NULL COMMENT '跨 Runtime 实例的当前 turn 租约持有者',
    `turn_lease_expires_at`   DATETIME     DEFAULT NULL COMMENT 'turn 租约过期时间',
    `last_turn_at`            DATETIME     DEFAULT NULL COMMENT '最近一次完成或挂起的 turn',
    `cleared_at`              DATETIME     DEFAULT NULL COMMENT '会话清空时间',
    `legal_hold`              TINYINT(1)   NOT NULL DEFAULT 0 COMMENT '1 表示合规保全，禁止清空和保留期擦除',
    `legal_hold_reason_code`  VARCHAR(64)  DEFAULT NULL COMMENT '机器可读的保全原因码，不保存说明正文',
    `legal_hold_reference`    VARCHAR(128) DEFAULT NULL COMMENT '外部案件/审批引用，不保存个人记忆正文',
    `legal_hold_set_at`       DATETIME     DEFAULT NULL COMMENT '最近设置保全时间',
    `legal_hold_set_by_hash`  CHAR(64)     DEFAULT NULL COMMENT '设置者平台用户 ID 的单向摘要',
    `lifecycle_owner`         VARCHAR(64)  DEFAULT NULL COMMENT 'CLEARING/PURGING 生命周期租约持有者',
    `lifecycle_lease_expires_at` DATETIME  DEFAULT NULL COMMENT '生命周期租约过期时间，支持崩溃恢复',
    `lifecycle_reason_code`   VARCHAR(64)  DEFAULT NULL COMMENT 'USER_CLEAR / RETENTION_* / 管理员原因码',
    `lifecycle_previous_status` VARCHAR(24) DEFAULT NULL COMMENT '生命周期操作前状态，仅用于无正文审计',
    `created_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_conversation_tenant_session` (`tenant_id`, `session_id`),
    UNIQUE KEY `uk_runtime_conversation_state_slot` (`state_user_key`, `state_session_key`),
    KEY `idx_runtime_conversation_user_time` (`tenant_id`, `user_id`, `last_turn_at`),
    KEY `idx_runtime_conversation_agent_time` (`agent_id`, `last_turn_at`),
    KEY `idx_runtime_conversation_lease` (`turn_lease_expires_at`),
    KEY `idx_runtime_conversation_retention` (`legal_hold`, `status`, `updated_at`),
    KEY `idx_runtime_conversation_cleared_retention` (`legal_hold`, `status`, `cleared_at`),
    KEY `idx_runtime_conversation_lifecycle_lease` (`status`, `lifecycle_lease_expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime Agent 企业会话状态目录与所有权边界';

CREATE TABLE IF NOT EXISTS `runtime_conversation_event` (
    `id`                      BIGINT      NOT NULL AUTO_INCREMENT COMMENT '主键',
    `conversation_session_id` BIGINT      NOT NULL COMMENT 'runtime_conversation_session.id',
    `sequence_no`             INT         NOT NULL COMMENT '会话内单调事件序号',
    `trace_id`                VARCHAR(64) DEFAULT NULL COMMENT '关联 Runtime traceId',
    `turn_id`                 VARCHAR(128) NOT NULL COMMENT '同一 trace 内的用户 turn 唯一 ID，用于幂等写入',
    `event_type`              VARCHAR(24) NOT NULL COMMENT 'MESSAGE / WAITING / FAILED',
    `role`                    VARCHAR(24) NOT NULL COMMENT 'user / assistant / system / tool',
    `content`                 MEDIUMTEXT  DEFAULT NULL COMMENT '完整但按平台上限截断的会话内容；受企业留存策略治理',
    `content_sha256`          CHAR(64)    NOT NULL COMMENT '内容完整性与去重摘要',
    `payload_json`            MEDIUMTEXT  DEFAULT NULL COMMENT '不含 secret/token 的结构化结果元数据',
    `created_at`              DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_conversation_event_sequence` (`conversation_session_id`, `sequence_no`),
    UNIQUE KEY `uk_runtime_conversation_event_turn_role` (`conversation_session_id`, `turn_id`, `role`),
    KEY `idx_runtime_conversation_event_trace` (`trace_id`),
    KEY `idx_runtime_conversation_event_time` (`conversation_session_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime Agent 完整会话事件账本；不作为长期个人记忆事实源';

CREATE TABLE IF NOT EXISTS `runtime_session_retention_policy` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `tenant_id`               VARCHAR(96)  NOT NULL COMMENT '租户；无记录时使用 Runtime 安全默认值',
    `active_retention_days`   INT          NOT NULL COMMENT 'ACTIVE 会话未活动保留天数，范围 1..3650',
    `cleared_retention_hours` INT          NOT NULL COMMENT 'CLEARED 会话保留小时数，范围 1..87600',
    `updated_by_hash`         CHAR(64)     NOT NULL COMMENT '最后修改者平台用户 ID 的单向摘要',
    `created_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_session_retention_tenant` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime 租户级会话保留策略；Control 仅通过内部 API 管理';

CREATE TABLE IF NOT EXISTS `runtime_session_retention_audit` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `tenant_id`               VARCHAR(96)  NOT NULL COMMENT '租户',
    `session_id_hash`         CHAR(64)     DEFAULT NULL COMMENT 'tenant + sessionId 的单向摘要',
    `conversation_session_id` BIGINT       DEFAULT NULL COMMENT '操作时的会话行 ID；擦除后仅作审计引用',
    `event_type`              VARCHAR(64)  NOT NULL COMMENT 'POLICY / HOLD / CLEAR / PURGE 生命周期事件',
    `actor_type`              VARCHAR(32)  NOT NULL COMMENT 'SYSTEM / PLATFORM_USER / RUNTIME_USER',
    `actor_id_hash`           CHAR(64)     DEFAULT NULL COMMENT '操作人 ID 单向摘要；SYSTEM 为空',
    `reason_code`             VARCHAR(64)  DEFAULT NULL COMMENT '机器可读原因码，不保存会话正文',
    `reference_id`            VARCHAR(128) DEFAULT NULL COMMENT '外部审批/案件引用',
    `previous_status`         VARCHAR(24)  DEFAULT NULL,
    `result_status`           VARCHAR(24)  DEFAULT NULL,
    `failure_code`            VARCHAR(128) DEFAULT NULL COMMENT '失败异常类型，不保存异常消息或正文',
    `created_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_runtime_session_retention_audit_tenant` (`tenant_id`, `created_at`),
    KEY `idx_runtime_session_retention_audit_session` (`session_id_hash`, `created_at`),
    KEY `idx_runtime_session_retention_audit_event` (`event_type`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime 会话保留、保全和擦除的无正文审计';

CREATE TABLE IF NOT EXISTS `runtime_tool_result_artifact` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `artifact_ref`          CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT '仅供模型回读的不可猜测引用 tra_<uuid>',
    `owner_scope_hash`      CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT 'Runtime 状态用户键的单向作用域摘要',
    `session_scope_hash`    CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT 'Runtime 状态用户+会话键的单向作用域摘要',
    `agent_name`            VARCHAR(128) NOT NULL COMMENT '产生结果的 Agent keySlug 快照',
    `trace_id`              VARCHAR(64)  DEFAULT NULL COMMENT '关联 Runtime traceId',
    `tool_call_id`          VARCHAR(128) NOT NULL COMMENT 'AgentScope Tool call ID',
    `tool_name`             VARCHAR(128) DEFAULT NULL COMMENT 'Tool 名称快照',
    `content_hmac_sha256`   CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT '密钥化正文完整性与去重摘要',
    `active_slot`           TINYINT UNSIGNED DEFAULT 1 COMMENT 'ACTIVE 去重槽；密文擦除时置空，允许未来重新卸载',
    `content_chars`         INT UNSIGNED NOT NULL COMMENT '明文 UTF-16 字符数',
    `content_bytes`         INT UNSIGNED NOT NULL COMMENT '明文 UTF-8 字节数',
    `encryption_key_id`     VARCHAR(64)  NOT NULL COMMENT '企业密钥版本标识，不保存密钥',
    `encryption_nonce`      VARBINARY(12) DEFAULT NULL COMMENT 'AES-GCM 96-bit nonce；擦除后为空',
    `content_ciphertext`    LONGBLOB      DEFAULT NULL COMMENT 'AES-256-GCM 密文；过期/清空后擦除',
    `status`                VARCHAR(16)  NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / EXPIRED / DELETED',
    `expires_at`            DATETIME     NOT NULL COMMENT '密文强制过期时间',
    `created_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`            DATETIME     DEFAULT NULL COMMENT '密文擦除时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_tool_result_artifact_ref` (`artifact_ref`),
    UNIQUE KEY `uk_runtime_tool_result_active_dedupe`
        (`owner_scope_hash`, `session_scope_hash`, `tool_call_id`, `content_hmac_sha256`, `active_slot`),
    KEY `idx_runtime_tool_result_expiry` (`status`, `expires_at`),
    KEY `idx_runtime_tool_result_scope` (`owner_scope_hash`, `session_scope_hash`, `status`),
    KEY `idx_runtime_tool_result_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime 大 Tool 结果短期加密工件；不是个人长期记忆';

CREATE TABLE IF NOT EXISTS `runtime_run` (
    `id`                      BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
    `trace_id`                VARCHAR(64)   NOT NULL                COMMENT '一次外部入口执行的全局唯一 Trace ID',
    `run_type`                VARCHAR(32)   NOT NULL                COMMENT '根运行类型：AGENT / WORKFLOW / MANAGED_EXECUTION',
    `entry_type`              VARCHAR(32)   NOT NULL                COMMENT '入口：DEBUG / EMBED / GATEWAY / API / EVAL / REPLAY / AUTOMATION / WORKFLOW_STUDIO / A2A / AI_CODING_TASK / AGENT_DELEGATION / OPERATOR',
    `status`                  VARCHAR(24)   NOT NULL DEFAULT 'RUNNING' COMMENT 'RUNNING / SUSPENDED / COMPLETED / FAILED / CANCELLED / TIMED_OUT',
    `suspension_reason`       VARCHAR(32)   DEFAULT NULL            COMMENT '暂停原因: USER_INPUT / APPROVAL；非暂停状态为空',
    `project_id`              BIGINT        DEFAULT NULL            COMMENT '所属项目 ID',
    `project_code`            VARCHAR(96)   DEFAULT NULL            COMMENT '所属项目编码',
    `tenant_id`               VARCHAR(96)   DEFAULT NULL            COMMENT '租户',
    `app_id`                  VARCHAR(96)   DEFAULT NULL            COMMENT '嵌入式业务应用 ID',
    `session_id`              VARCHAR(128)  DEFAULT NULL            COMMENT '会话 ID',
    `user_id`                 VARCHAR(128)  DEFAULT NULL            COMMENT 'Runtime 用户 ID',
    `external_user_id`        VARCHAR(128)  DEFAULT NULL            COMMENT '业务系统内用户 ID',
    `global_user_id`          VARCHAR(128)  DEFAULT NULL            COMMENT '跨系统稳定用户 ID',
    `page_instance_id`        VARCHAR(128)  DEFAULT NULL            COMMENT '业务前端页面实例 ID',
    `origin`                  VARCHAR(512)  DEFAULT NULL            COMMENT '请求来源或浏览器 Origin',
    `agent_id`                VARCHAR(64)   DEFAULT NULL            COMMENT 'Agent ID；独立 Workflow 运行可为空',
    `agent_key_slug`          VARCHAR(128)  DEFAULT NULL            COMMENT 'Agent 稳定 keySlug 快照',
    `agent_name`              VARCHAR(128)  DEFAULT NULL            COMMENT 'Agent 名称快照',
    `agent_config_version_id` BIGINT        DEFAULT NULL            COMMENT '本次执行固定的 Agent 配置版本 ID',
    `agent_config_version`    INT           DEFAULT NULL            COMMENT '本次执行固定的 Agent 配置版本号',
    `workflow_id`             VARCHAR(64)   DEFAULT NULL            COMMENT '根 Workflow ID；Agent 根运行可为空',
    `workflow_key_slug`       VARCHAR(128)  DEFAULT NULL            COMMENT '根 Workflow 稳定 keySlug 快照',
    `workflow_name`           VARCHAR(160)  DEFAULT NULL            COMMENT '根 Workflow 名称快照',
    `workflow_version_id`     BIGINT        DEFAULT NULL            COMMENT '根 Workflow 发布版本 ID',
    `workflow_version`        VARCHAR(32)   DEFAULT NULL            COMMENT '根 Workflow 发布版本号',
    `runtime_type`            VARCHAR(32)   DEFAULT NULL            COMMENT '运行协议身份：AGENTSCOPE / GRAPH_SPEC / CODEX_HARNESS',
    `root_span_id`            VARCHAR(64)   DEFAULT NULL            COMMENT '根 Span ID',
    `input_summary`           MEDIUMTEXT    DEFAULT NULL            COMMENT '脱敏、截断后的输入摘要',
    `output_summary`          MEDIUMTEXT    DEFAULT NULL            COMMENT '脱敏、截断后的输出摘要',
    `error_code`              VARCHAR(128)  DEFAULT NULL            COMMENT '根运行错误码',
    `error_message`           TEXT          DEFAULT NULL            COMMENT '根运行错误信息',
    `latency_ms`              INT           DEFAULT NULL            COMMENT '根运行总耗时毫秒',
    `token_cost`              INT           NOT NULL DEFAULT 0      COMMENT '根运行累计 Token 消耗',
    `plan_count`              INT           NOT NULL DEFAULT 0      COMMENT 'PLAN 事件数',
    `replan_count`            INT           NOT NULL DEFAULT 0      COMMENT 'REPLAN 事件数',
    `workflow_call_count`     INT           NOT NULL DEFAULT 0      COMMENT 'Workflow-as-Tool 调用数',
    `tool_call_count`         INT           NOT NULL DEFAULT 0      COMMENT '底层 Tool / Capability 调用数',
    `guard_deny_count`        INT           NOT NULL DEFAULT 0      COMMENT 'Guard 拒绝数',
    `approval_count`          INT           NOT NULL DEFAULT 0      COMMENT '人工审批请求数',
    `replay_of_trace_id`      VARCHAR(64)   DEFAULT NULL            COMMENT '回放来源 Trace ID',
    `snapshot_json`           MEDIUMTEXT    DEFAULT NULL            COMMENT '历史 Agent 配置、Workflow 工具目录及发布版本快照',
    `metadata_json`           MEDIUMTEXT    DEFAULT NULL            COMMENT '扩展运行元数据 JSON',
    `started_at`              DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间',
    `ended_at`                DATETIME      DEFAULT NULL            COMMENT '结束时间',
    `created_at`              DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updated_at`              DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_run_trace` (`trace_id`),
    KEY `idx_runtime_run_project_time` (`project_id`, `started_at`),
    KEY `idx_runtime_run_project_code_time` (`project_code`, `started_at`),
    KEY `idx_runtime_run_status_time` (`status`, `started_at`),
    KEY `idx_runtime_run_agent_version_time` (`agent_id`, `agent_config_version_id`, `started_at`),
    KEY `idx_runtime_run_workflow_version_time` (`workflow_id`, `workflow_version_id`, `started_at`),
    KEY `idx_runtime_run_entry_time` (`entry_type`, `started_at`),
    KEY `idx_runtime_run_user_time` (`user_id`, `started_at`),
    KEY `idx_runtime_run_replay` (`replay_of_trace_id`, `started_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='RunOps 根运行事实；每次用户或外部入口执行一行';

CREATE TABLE IF NOT EXISTS `runtime_tool_call_log` (
    `id`                   BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `trace_id`             VARCHAR(64)  NOT NULL                COMMENT '一次 Agent 执行的 trace id',
    `session_id`           VARCHAR(64)  DEFAULT NULL            COMMENT '会话 ID',
    `user_id`              VARCHAR(64)  DEFAULT NULL            COMMENT '用户 ID',
    `agent_name`           VARCHAR(128) DEFAULT NULL            COMMENT '触发 tool 的 Agent 名',
    `intent_type`          VARCHAR(64)  DEFAULT NULL            COMMENT '意图类型',
    `tool_name`            VARCHAR(128) NOT NULL                COMMENT '被调用的 Tool',
    `args_json`            TEXT         DEFAULT NULL            COMMENT '调用入参 JSON',
    `result_summary`       MEDIUMTEXT   DEFAULT NULL            COMMENT '结果摘要（按 result-max-chars 截断）',
    `success`              TINYINT      NOT NULL DEFAULT 1      COMMENT '是否成功',
    `error_code`           VARCHAR(64)  DEFAULT NULL            COMMENT '失败时的错误码/异常类',
    `elapsed_ms`           INT          DEFAULT NULL            COMMENT '耗时毫秒',
    `token_cost`           INT          DEFAULT NULL            COMMENT '本次调用消耗 token',
    `retrieval_trace_json` MEDIUMTEXT   DEFAULT NULL            COMMENT '召回 top-K + 分数 + 选中项 JSON',
    `create_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`),
    KEY `idx_trace_id`         (`trace_id`),
    KEY `idx_session`          (`session_id`),
    KEY `idx_tool_time`        (`tool_name`, `create_time`),
    KEY `idx_create_time`      (`create_time`),
    KEY `idx_user_create_time` (`user_id`,     `create_time`),
    KEY `idx_intent_create`    (`intent_type`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime Tool 调用子事件审计日志';

-- 兼容老库：加 Phase 2.0.1 新增的三个索引
CALL add_idx_if_absent('runtime_tool_call_log', 'idx_create_time',      'create_time');
CALL add_idx_if_absent('runtime_tool_call_log', 'idx_user_create_time', 'user_id, create_time');
CALL add_idx_if_absent('runtime_tool_call_log', 'idx_intent_create',    'intent_type, create_time');

CREATE TABLE IF NOT EXISTS `runtime_trace_span` (
    `id`                BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
    `trace_id`          VARCHAR(64)  NOT NULL                COMMENT '关联 runtime_run.trace_id',
    `span_id`           VARCHAR(64)  NOT NULL                COMMENT 'Span ID',
    `parent_span_id`    VARCHAR(64)  DEFAULT NULL            COMMENT '父 Span ID',
    `span_type`         VARCHAR(32)  NOT NULL                COMMENT 'SUPERVISOR/PLAN/REPLAN/WORKFLOW_TOOL/GRAPH_NODE/LLM_CALL/TOOL_CALL 等',
    `runtime_type`      VARCHAR(32)  DEFAULT NULL            COMMENT 'Runtime 类型',
    `agent_id`          VARCHAR(64)  DEFAULT NULL            COMMENT 'Agent ID；独立 Workflow Span 可为空',
    `agent_name`        VARCHAR(128) DEFAULT NULL            COMMENT 'Agent 名称快照',
    `node_id`           VARCHAR(128) DEFAULT NULL            COMMENT 'GraphSpec 节点 ID',
    `tool_name`         VARCHAR(256) DEFAULT NULL            COMMENT 'Workflow-as-Tool 或底层 Tool / Capability 名称',
    `model_instance_id` VARCHAR(64)  DEFAULT NULL            COMMENT '模型实例 ID',
    `project_code`      VARCHAR(96)  DEFAULT NULL            COMMENT '项目编码',
    `tenant_id`         VARCHAR(96)  DEFAULT NULL            COMMENT '租户',
    `app_id`            VARCHAR(96)  DEFAULT NULL            COMMENT '嵌入式业务应用 ID',
    `external_user_id`  VARCHAR(128) DEFAULT NULL            COMMENT '业务系统内用户 ID',
    `global_user_id`    VARCHAR(128) DEFAULT NULL            COMMENT '跨系统稳定用户 ID',
    `page_instance_id`  VARCHAR(128) DEFAULT NULL            COMMENT '业务前端页面实例 ID',
    `status`            VARCHAR(16)  NOT NULL DEFAULT 'SUCCESS' COMMENT 'RUNNING / SUCCESS / FAILED / ERROR / WAITING_USER / WAITING_APPROVAL',
    `input_summary`     MEDIUMTEXT   DEFAULT NULL            COMMENT '输入摘要',
    `output_summary`    MEDIUMTEXT   DEFAULT NULL            COMMENT '输出摘要',
    `metadata_json`     MEDIUMTEXT   DEFAULT NULL            COMMENT '结构化元数据 JSON',
    `error_code`        VARCHAR(128) DEFAULT NULL            COMMENT '错误码',
    `error_message`     TEXT         DEFAULT NULL            COMMENT '错误信息',
    `latency_ms`        INT          DEFAULT NULL            COMMENT '耗时毫秒',
    `token_cost`        INT          DEFAULT NULL            COMMENT 'Token 消耗',
    `started_at`        DATETIME     DEFAULT NULL            COMMENT '开始时间',
    `ended_at`          DATETIME     DEFAULT NULL            COMMENT '结束时间',
    `created_at`        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_trace_span` (`trace_id`, `span_id`),
    KEY `idx_trace` (`trace_id`, `created_at`),
    KEY `idx_parent` (`trace_id`, `parent_span_id`),
    KEY `idx_trace_object` (`trace_id`, `agent_id`, `node_id`, `created_at`),
    KEY `idx_trace_embed_user` (`app_id`, `external_user_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime 通用执行 Trace Span 子事件';


-- ============================================================================
-- 六、Runtime Interaction 会话表见文末 runtime_interaction_session
--     Supervisor 策略确认写入 source_type=SUPERVISOR_POLICY，不再使用已退役的 Skill 交互表
-- ============================================================================


-- ============================================================================
-- 七、sideEffect 回填（历史 tool 数据对齐）
--   - 仅覆盖 side_effect 为 NULL/空/WRITE 的记录，避免推翻人工校准值
--   - 规则与 SideEffectInferrer 对齐
-- ============================================================================

UPDATE `capability_tool_definition`
SET `side_effect` = CASE
  WHEN UPPER(IFNULL(`http_method`, '')) = 'DELETE'
      OR LOWER(IFNULL(`endpoint_path`, '')) REGEXP 'delete|drop|purge|remove|refund|cancel|void|destroy|erase'
        THEN 'IRREVERSIBLE'
    WHEN UPPER(IFNULL(`http_method`, '')) IN ('GET', 'HEAD', 'OPTIONS')
      OR LOWER(SUBSTRING_INDEX(TRIM(BOTH '/' FROM IFNULL(`endpoint_path`, '')), '/', -1))
         REGEXP '^(query|search|list|get|fetch|describe|find|view|show|lookup|count|exists)'
        THEN 'READ_ONLY'
    WHEN UPPER(IFNULL(`http_method`, '')) = 'PUT'
      OR LOWER(IFNULL(`endpoint_path`, '')) REGEXP 'upsert|idempotent|merge'
        THEN 'IDEMPOTENT_WRITE'
    WHEN UPPER(IFNULL(`http_method`, '')) IN ('POST', 'PATCH')
        THEN 'WRITE'
    ELSE 'WRITE'
END
WHERE (`side_effect` IS NULL OR TRIM(`side_effect`) = '' OR UPPER(`side_effect`) = 'WRITE');


-- ============================================================================
-- 七.五、ReachAI Agent / Workflow 主模型（runtime_agent 为唯一 Agent 主表）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `runtime_agent` (
    `id`                 VARCHAR(32)  NOT NULL,
    `project_id`         BIGINT       DEFAULT NULL,
    `project_code`       VARCHAR(96)  DEFAULT NULL,
    `key_slug`           VARCHAR(128) NOT NULL,
    `name`               VARCHAR(128) NOT NULL,
    `description`        VARCHAR(512) DEFAULT NULL,
    `visibility`         VARCHAR(32)  NOT NULL DEFAULT 'PROJECT',
    `active_config_version_id` BIGINT DEFAULT NULL COMMENT '当前发布的 Agent 配置版本 ID',
    `allowed_roles_json` TEXT         DEFAULT NULL,
    `enabled`            TINYINT(1)   NOT NULL DEFAULT 1,
    `created_at`         DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`         DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_ai_agent_key_slug` (`key_slug`),
    KEY `idx_ai_agent_project` (`project_id`, `enabled`),
    KEY `idx_ai_agent_project_code` (`project_code`, `enabled`),
    KEY `idx_runtime_agent_active_config` (`active_config_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ReachAI Agent 聚合根与稳定入口身份';

CREATE TABLE IF NOT EXISTS `runtime_agent_config_version` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT,
    `agent_id`              VARCHAR(32)  NOT NULL COMMENT '关联 runtime_agent.id',
    `version_no`            INT          NOT NULL COMMENT 'Agent 内递增配置版本号',
    `status`                VARCHAR(16)  NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT / ACTIVE / ARCHIVED',
    `runtime_type`          VARCHAR(32)  NOT NULL DEFAULT 'AGENTSCOPE' COMMENT 'Supervisor Runtime 类型',
    `system_prompt`         MEDIUMTEXT   DEFAULT NULL,
    `model_instance_id`     VARCHAR(64)  DEFAULT NULL,
    `max_plan_steps`        INT          NOT NULL DEFAULT 6,
    `max_workflow_calls`    INT          NOT NULL DEFAULT 4,
    `max_replans`           INT          NOT NULL DEFAULT 2,
    `total_timeout_ms`      INT          NOT NULL DEFAULT 300000,
    `workflow_timeout_ms`   INT          NOT NULL DEFAULT 180000,
    `page_bridge_timeout_ms` INT         NOT NULL DEFAULT 30000,
    `parallel_read_only`    TINYINT(1)   NOT NULL DEFAULT 1,
    `policy_profile`        VARCHAR(64)  NOT NULL DEFAULT 'DEV_ALLOW_ALL',
    `tool_catalog_mode`     VARCHAR(32)  NOT NULL DEFAULT 'ALLOW_LIST',
    `config_json`           MEDIUMTEXT   DEFAULT NULL COMMENT '扩展配置；不得替代上方稳定字段',
    `published_by`          VARCHAR(64)  DEFAULT NULL,
    `published_at`          DATETIME     DEFAULT NULL,
    `created_at`            DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_agent_config_version` (`agent_id`, `version_no`),
    KEY `idx_runtime_agent_config_status` (`agent_id`, `status`, `version_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Supervisor 不可变发布配置与草稿版本';

CREATE TABLE IF NOT EXISTS `runtime_agent_workflow_tool` (
    `id`                         BIGINT       NOT NULL AUTO_INCREMENT,
    `agent_id`                   VARCHAR(32)  NOT NULL COMMENT '关联 runtime_agent.id',
    `agent_config_version_id`    BIGINT       NOT NULL COMMENT '关联 runtime_agent_config_version.id',
    `workflow_id`                VARCHAR(32)  NOT NULL COMMENT '关联 runtime_workflow.id',
    `workflow_version_id`        BIGINT       NOT NULL COMMENT '发布配置固定的 runtime_workflow_version.id',
    `tool_name`                  VARCHAR(128) NOT NULL COMMENT '暴露给 Supervisor 的稳定 Tool 名称',
    `description_override`       VARCHAR(1000) DEFAULT NULL,
    `input_schema_override_json` MEDIUMTEXT   DEFAULT NULL,
    `output_schema_override_json` MEDIUMTEXT  DEFAULT NULL,
    `risk_level`                 VARCHAR(24)  NOT NULL DEFAULT 'READ' COMMENT 'READ / WRITE / PAGE_ACTION / IRREVERSIBLE',
    `permission_key`             VARCHAR(160) DEFAULT NULL,
    `read_only`                  TINYINT(1)   NOT NULL DEFAULT 1,
    `enabled`                    TINYINT(1)   NOT NULL DEFAULT 1,
    `priority`                   INT          NOT NULL DEFAULT 0,
    `created_at`                 DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                 DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_agent_version_workflow_tool` (`agent_config_version_id`, `workflow_id`),
    UNIQUE KEY `uk_runtime_agent_version_tool_name` (`agent_config_version_id`, `tool_name`),
    KEY `idx_runtime_agent_workflow_tool_agent` (`agent_id`, `agent_config_version_id`, `enabled`),
    KEY `idx_runtime_agent_workflow_tool_workflow` (`workflow_id`, `workflow_version_id`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent 配置版本固定 Workflow 发布版本的 Workflow-as-Tool 显式白名单';

CREATE TABLE IF NOT EXISTS `runtime_agent_skill_binding` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT,
    `agent_id`                VARCHAR(32)  NOT NULL COMMENT '关联 runtime_agent.id',
    `agent_config_version_id` BIGINT       NOT NULL COMMENT '关联 runtime_agent_config_version.id；随配置版本不可变',
    `skill_id`                BIGINT       NOT NULL COMMENT 'Control Skill 稳定身份 ID 的逻辑引用；Runtime 不直读 Control 表',
    `skill_version_id`        BIGINT       NOT NULL COMMENT 'Control Skill 不可变版本 ID 的逻辑引用',
    `publisher`               VARCHAR(64)  NOT NULL,
    `standard_name`           VARCHAR(64)  NOT NULL COMMENT 'Agent Skills 标准 name',
    `display_name`            VARCHAR(128) DEFAULT NULL,
    `visibility`              VARCHAR(24)  NOT NULL COMMENT 'Control 证明的 PRIVATE / PROJECT / SHARED / PUBLIC 范围快照',
    `project_code`            VARCHAR(128) DEFAULT NULL COMMENT 'PROJECT Skill 的绑定范围快照',
    `version`                 VARCHAR(64)  NOT NULL,
    `source_sha256`           CHAR(64)     NOT NULL COMMENT '原始 ZIP SHA-256；运行时下载后必须复核',
    `content_tree_sha256`     CHAR(64)     NOT NULL COMMENT '规范化文件树 SHA-256',
    `source_root`             VARCHAR(512) NOT NULL DEFAULT '' COMMENT 'ZIP 内 SKILL.md 所在相对目录前缀',
    `package_manifest_json`   MEDIUMTEXT   NOT NULL COMMENT '发布时固化的包清单快照',
    `risk_report_json`        MEDIUMTEXT   DEFAULT NULL COMMENT '发布时固化的风险报告快照',
    `has_scripts`             TINYINT(1)   NOT NULL DEFAULT 0,
    `activation_mode`         VARCHAR(24)  NOT NULL DEFAULT 'MODEL_SELECTED' COMMENT 'MODEL_SELECTED / ALWAYS / EXPLICIT',
    `script_policy`           VARCHAR(24)  NOT NULL DEFAULT 'DENY' COMMENT 'DENY / SANDBOX_REVIEWED；默认禁止脚本执行',
    `required`                TINYINT(1)   NOT NULL DEFAULT 0,
    `enabled`                 TINYINT(1)   NOT NULL DEFAULT 1,
    `priority`                INT          NOT NULL DEFAULT 0,
    `created_at`              DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`              DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_agent_config_skill` (`agent_config_version_id`, `publisher`, `standard_name`),
    UNIQUE KEY `uk_runtime_agent_config_skill_version` (`agent_config_version_id`, `skill_version_id`),
    KEY `idx_runtime_agent_skill_binding_agent` (`agent_id`, `agent_config_version_id`, `enabled`, `priority`),
    KEY `idx_runtime_agent_skill_binding_digest` (`source_sha256`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent 发布配置固定的标准 Agent Skill 版本快照；不是 Capability 或 Tool';

CREATE TABLE IF NOT EXISTS `runtime_workflow` (
    `id`                           VARCHAR(32)  NOT NULL,
    `project_id`                   BIGINT       DEFAULT NULL,
    `project_code`                 VARCHAR(96)  DEFAULT NULL,
    `key_slug`                     VARCHAR(128) NOT NULL,
    `name`                         VARCHAR(160) NOT NULL,
    `description`                  VARCHAR(512) DEFAULT NULL,
    `workflow_kind`                VARCHAR(32)  NOT NULL DEFAULT 'GENERAL' COMMENT '业务形态: GENERAL / PAGE_ASSISTANT',
    `execution_engine`             VARCHAR(32)  NOT NULL DEFAULT 'GRAPH_SPEC' COMMENT '稳定执行引擎协议标识',
    `graph_spec_json`              MEDIUMTEXT   DEFAULT NULL COMMENT 'Platform GraphSpec JSON（运行语义）',
    `canvas_json`                  MEDIUMTEXT   DEFAULT NULL COMMENT 'Workflow Studio 画布布局',
    `input_schema_json`            MEDIUMTEXT   DEFAULT NULL,
    `output_schema_json`           MEDIUMTEXT   DEFAULT NULL,
    `default_model_instance_id`    VARCHAR(64)  DEFAULT NULL,
    `default_resource_config_json` MEDIUMTEXT   DEFAULT NULL,
    `status`                       VARCHAR(24)  NOT NULL DEFAULT 'DRAFT',
    `definition_authority`         VARCHAR(32)  NOT NULL DEFAULT 'USER' COMMENT '定义权威: USER / SDK / SYSTEM',
    `creation_channel`             VARCHAR(32)  NOT NULL DEFAULT 'STUDIO' COMMENT '创建入口: STUDIO / AI_CODING / SDK_SYNC / AI_QUICK_ACCESS / SYSTEM_SEED',
    `extra_json`                   MEDIUMTEXT   DEFAULT NULL,
    `created_at`                   DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                   DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_ai_workflow_key_slug` (`key_slug`),
    KEY `idx_ai_workflow_project` (`project_id`, `status`),
    KEY `idx_runtime_workflow_project_kind` (`project_code`, `workflow_kind`, `status`),
    KEY `idx_runtime_workflow_engine` (`execution_engine`, `status`),
    KEY `idx_runtime_workflow_authority` (`definition_authority`, `creation_channel`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ReachAI Workflow 编排资产（GraphSpec / canvas_json）';

CREATE TABLE IF NOT EXISTS `runtime_workflow_version` (
    `id`                       BIGINT      NOT NULL AUTO_INCREMENT,
    `workflow_id`              VARCHAR(32) NOT NULL,
    `version`                  VARCHAR(32) NOT NULL,
    `snapshot_json`            MEDIUMTEXT  NOT NULL,
    `graph_spec_snapshot_json` MEDIUMTEXT  DEFAULT NULL,
    `canvas_snapshot_json`     MEDIUMTEXT  DEFAULT NULL,
    `rollout_percent`          INT         NOT NULL DEFAULT 100,
    `status`                   VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
    `published_by`             VARCHAR(64) DEFAULT NULL,
    `published_at`             DATETIME    DEFAULT NULL,
    `note`                     VARCHAR(512) DEFAULT NULL,
    `created_at`               DATETIME    DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_ai_workflow_version` (`workflow_id`, `version`),
    KEY `idx_ai_workflow_version_status` (`workflow_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Workflow 发布版本';

CREATE TABLE IF NOT EXISTS `runtime_workflow_resource_binding` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `workflow_id`   VARCHAR(32)  NOT NULL,
    `project_id`    BIGINT       NOT NULL,
    `project_code`  VARCHAR(96)  NOT NULL,
    `resource_type` VARCHAR(32)  NOT NULL COMMENT 'PAGE',
    `resource_key`  VARCHAR(256) NOT NULL COMMENT '资源域内稳定键；PAGE 使用 page_key',
    `binding_role`  VARCHAR(32)  NOT NULL DEFAULT 'TARGET',
    `status`        VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_workflow_resource` (`workflow_id`, `resource_type`, `resource_key`),
    KEY `idx_runtime_workflow_resource_lookup` (`project_code`, `resource_type`, `resource_key`, `status`),
    KEY `idx_runtime_workflow_resource_project` (`project_id`, `status`, `updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Workflow与页面等外部资源的稳定绑定';

-- Workflow Studio Eval：Workflow Working Copy 自动评测
CREATE TABLE IF NOT EXISTS `runtime_eval_target_snapshot` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `tenant_id` VARCHAR(96) NOT NULL DEFAULT 'default' COMMENT 'Eval 所属租户快照；当前 Runtime 直连默认 default',
  `project_code` VARCHAR(96) DEFAULT NULL,
  `target_type` VARCHAR(32) NOT NULL COMMENT 'AGENT / WORKFLOW',
  `target_id` VARCHAR(64) NOT NULL,
  `target_version_ref` VARCHAR(128) NOT NULL COMMENT '服务端解析的稳定版本引用',
  `agent_config_version_id` BIGINT DEFAULT NULL,
  `source_status` VARCHAR(24) NOT NULL COMMENT '捕获时 DRAFT / ACTIVE / ARCHIVED',
  `snapshot_schema_version` INT NOT NULL DEFAULT 1,
  `fingerprint_sha256` CHAR(64) NOT NULL,
  `snapshot_json` MEDIUMTEXT NOT NULL COMMENT '服务端规范化的不可变执行目标快照',
  `created_by` VARCHAR(128) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_target_fingerprint`
    (`tenant_id`, `target_type`, `target_id`, `fingerprint_sha256`),
  KEY `idx_runtime_eval_target_version` (`target_type`, `target_id`, `agent_config_version_id`),
  KEY `idx_runtime_eval_target_created` (`tenant_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Eval 服务端不可变目标快照与规范化指纹';

-- EvalOps P2：版本化数据集、不可变评分器套件、对比实验与可恢复异步任务
CREATE TABLE IF NOT EXISTS `runtime_eval_dataset` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `tenant_id` VARCHAR(96) NOT NULL DEFAULT 'default',
  `project_code` VARCHAR(96) DEFAULT NULL,
  `target_type` VARCHAR(32) NOT NULL DEFAULT 'AGENT',
  `target_id` VARCHAR(64) NOT NULL,
  `name` VARCHAR(128) NOT NULL,
  `description` VARCHAR(512) DEFAULT NULL,
  `source` VARCHAR(32) NOT NULL DEFAULT 'MANUAL' COMMENT 'MANUAL / IMPORT / RUNOPS',
  `status` VARCHAR(24) NOT NULL DEFAULT 'ACTIVE',
  `current_version_id` BIGINT DEFAULT NULL,
  `version_count` INT NOT NULL DEFAULT 0,
  `created_by` VARCHAR(128) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_dataset_name` (`tenant_id`, `target_type`, `target_id`, `name`),
  KEY `idx_runtime_eval_dataset_target` (`tenant_id`, `target_type`, `target_id`, `updated_at`),
  KEY `idx_runtime_eval_dataset_current` (`current_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 版本化数据集目录';

CREATE TABLE IF NOT EXISTS `runtime_eval_dataset_version` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `dataset_id` BIGINT NOT NULL,
  `version_no` INT NOT NULL,
  `status` VARCHAR(24) NOT NULL DEFAULT 'PUBLISHED',
  `fingerprint_sha256` CHAR(64) NOT NULL,
  `item_count` INT NOT NULL DEFAULT 0,
  `change_note` VARCHAR(512) DEFAULT NULL,
  `created_by` VARCHAR(128) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `published_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_dataset_version` (`dataset_id`, `version_no`),
  KEY `idx_runtime_eval_dataset_version_fingerprint` (`dataset_id`, `fingerprint_sha256`),
  KEY `idx_runtime_eval_dataset_version_status` (`dataset_id`, `status`, `version_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 不可变数据集版本';

CREATE TABLE IF NOT EXISTS `runtime_eval_dataset_item` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `dataset_version_id` BIGINT NOT NULL,
  `item_key` VARCHAR(64) NOT NULL,
  `message` TEXT DEFAULT NULL,
  `input_json` MEDIUMTEXT NOT NULL,
  `expected_json` MEDIUMTEXT NOT NULL,
  `metadata_json` MEDIUMTEXT NOT NULL,
  `tags_json` TEXT DEFAULT NULL,
  `source_trace_id` VARCHAR(96) DEFAULT NULL COMMENT '可选 RunOps 来源 Trace',
  `enabled` TINYINT(1) NOT NULL DEFAULT 1,
  `ordinal_no` INT NOT NULL,
  `content_sha256` CHAR(64) NOT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_dataset_item` (`dataset_version_id`, `item_key`),
  KEY `idx_runtime_eval_dataset_item_order` (`dataset_version_id`, `enabled`, `ordinal_no`),
  KEY `idx_runtime_eval_dataset_item_trace` (`source_trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 数据集版本用例快照';

CREATE TABLE IF NOT EXISTS `runtime_eval_evaluator_suite_version` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `tenant_id` VARCHAR(96) NOT NULL DEFAULT 'default',
  `name` VARCHAR(128) NOT NULL,
  `version_no` INT NOT NULL,
  `status` VARCHAR(24) NOT NULL DEFAULT 'PUBLISHED',
  `config_json` MEDIUMTEXT NOT NULL,
  `fingerprint_sha256` CHAR(64) NOT NULL,
  `created_by` VARCHAR(128) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_evaluator_suite_version` (`tenant_id`, `name`, `version_no`),
  UNIQUE KEY `uk_runtime_eval_evaluator_suite_fingerprint` (`tenant_id`, `fingerprint_sha256`),
  KEY `idx_runtime_eval_evaluator_suite_status` (`tenant_id`, `status`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 不可变评分器套件版本';

CREATE TABLE IF NOT EXISTS `runtime_eval_experiment` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `tenant_id` VARCHAR(96) NOT NULL DEFAULT 'default',
  `project_code` VARCHAR(96) DEFAULT NULL,
  `target_type` VARCHAR(32) NOT NULL DEFAULT 'AGENT',
  `target_id` VARCHAR(64) NOT NULL,
  `name` VARCHAR(128) NOT NULL,
  `dataset_version_id` BIGINT NOT NULL,
  `evaluator_suite_version_id` BIGINT NOT NULL,
  `repeat_count` INT NOT NULL DEFAULT 1,
  `status` VARCHAR(32) NOT NULL DEFAULT 'QUEUED' COMMENT 'QUEUED / RUNNING / COMPLETED / PARTIAL / CANCEL_REQUESTED / CANCELLED',
  `idempotency_key` VARCHAR(96) DEFAULT NULL,
  `variant_count` INT NOT NULL,
  `task_count` INT NOT NULL,
  `completed_task_count` INT NOT NULL DEFAULT 0,
  `failed_task_count` INT NOT NULL DEFAULT 0,
  `gate_status` VARCHAR(24) NOT NULL DEFAULT 'PENDING',
  `gate_config_json` MEDIUMTEXT NOT NULL,
  `summary_json` MEDIUMTEXT NOT NULL,
  `created_by` VARCHAR(128) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `started_at` DATETIME DEFAULT NULL,
  `finished_at` DATETIME DEFAULT NULL,
  UNIQUE KEY `uk_runtime_eval_experiment_idempotency` (`tenant_id`, `idempotency_key`),
  KEY `idx_runtime_eval_experiment_target` (`tenant_id`, `target_type`, `target_id`, `created_at`),
  KEY `idx_runtime_eval_experiment_status` (`status`, `updated_at`),
  KEY `idx_runtime_eval_experiment_dataset` (`dataset_version_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 基线候选对比实验';

CREATE TABLE IF NOT EXISTS `runtime_eval_experiment_variant` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `experiment_id` BIGINT NOT NULL,
  `variant_key` VARCHAR(48) NOT NULL,
  `display_name` VARCHAR(128) NOT NULL,
  `variant_role` VARCHAR(24) NOT NULL COMMENT 'BASELINE / CANDIDATE',
  `target_snapshot_id` BIGINT NOT NULL,
  `target_config_version_id` BIGINT NOT NULL,
  `target_config_status` VARCHAR(24) NOT NULL,
  `target_fingerprint` CHAR(64) NOT NULL,
  `status` VARCHAR(24) NOT NULL DEFAULT 'QUEUED',
  `summary_json` MEDIUMTEXT NOT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_experiment_variant` (`experiment_id`, `variant_key`),
  KEY `idx_runtime_eval_variant_target` (`target_snapshot_id`, `experiment_id`),
  KEY `idx_runtime_eval_variant_role` (`experiment_id`, `variant_role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 实验目标变体及服务端指纹';

CREATE TABLE IF NOT EXISTS `runtime_eval_experiment_item` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `experiment_id` BIGINT NOT NULL,
  `variant_id` BIGINT NOT NULL,
  `dataset_item_id` BIGINT NOT NULL,
  `repeat_no` INT NOT NULL DEFAULT 1,
  `status` VARCHAR(24) NOT NULL DEFAULT 'PENDING',
  `runtime_success` TINYINT(1) NOT NULL DEFAULT 0,
  `assertion_passed` TINYINT(1) NOT NULL DEFAULT 0,
  `score` DOUBLE DEFAULT NULL,
  `elapsed_ms` INT DEFAULT NULL,
  `answer` MEDIUMTEXT DEFAULT NULL,
  `trace_id` VARCHAR(96) DEFAULT NULL,
  `execution_metadata_json` MEDIUMTEXT DEFAULT NULL,
  `evaluator_results_json` MEDIUMTEXT DEFAULT NULL,
  `error_code` VARCHAR(128) DEFAULT NULL,
  `error_message` TEXT DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `started_at` DATETIME DEFAULT NULL,
  `finished_at` DATETIME DEFAULT NULL,
  UNIQUE KEY `uk_runtime_eval_experiment_item` (`experiment_id`, `variant_id`, `dataset_item_id`, `repeat_no`),
  KEY `idx_runtime_eval_experiment_item_status` (`experiment_id`, `status`, `id`),
  KEY `idx_runtime_eval_experiment_item_variant` (`variant_id`, `dataset_item_id`, `repeat_no`),
  KEY `idx_runtime_eval_experiment_item_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 逐变体逐用例执行结果';

CREATE TABLE IF NOT EXISTS `runtime_eval_score` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `experiment_id` BIGINT NOT NULL,
  `experiment_item_id` BIGINT NOT NULL,
  `variant_id` BIGINT NOT NULL,
  `dataset_item_id` BIGINT NOT NULL,
  `evaluator_key` VARCHAR(64) NOT NULL,
  `evaluator_type` VARCHAR(48) NOT NULL,
  `score` DOUBLE NOT NULL,
  `passed` TINYINT(1) NOT NULL,
  `weight` DOUBLE NOT NULL,
  `reason` VARCHAR(1000) DEFAULT NULL,
  `metadata_json` MEDIUMTEXT DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_eval_score_item_evaluator` (`experiment_item_id`, `evaluator_key`),
  KEY `idx_runtime_eval_score_experiment` (`experiment_id`, `variant_id`, `evaluator_key`),
  KEY `idx_runtime_eval_score_dataset_item` (`dataset_item_id`, `evaluator_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps 一等评分记录';

CREATE TABLE IF NOT EXISTS `runtime_eval_task` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `task_type` VARCHAR(32) NOT NULL DEFAULT 'EXECUTE_ITEM',
  `experiment_id` BIGINT NOT NULL,
  `experiment_item_id` BIGINT NOT NULL,
  `status` VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / LEASED / RETRY / COMPLETED / DEAD / CANCELLED',
  `priority` INT NOT NULL DEFAULT 0,
  `attempt_count` INT NOT NULL DEFAULT 0,
  `max_attempts` INT NOT NULL DEFAULT 3,
  `available_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `lease_owner` VARCHAR(128) DEFAULT NULL,
  `lease_token` CHAR(36) DEFAULT NULL,
  `leased_until` DATETIME DEFAULT NULL,
  `last_error_code` VARCHAR(128) DEFAULT NULL,
  `last_error_message` TEXT DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `completed_at` DATETIME DEFAULT NULL,
  UNIQUE KEY `uk_runtime_eval_task_item` (`task_type`, `experiment_item_id`),
  KEY `idx_runtime_eval_task_lease` (`status`, `available_at`, `leased_until`, `priority`, `id`),
  KEY `idx_runtime_eval_task_experiment` (`experiment_id`, `status`, `id`),
  KEY `idx_runtime_eval_task_token` (`lease_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EvalOps MySQL 5.7 兼容租约任务队列';


CREATE TABLE IF NOT EXISTS `runtime_managed_execution` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `execution_id` VARCHAR(64) NOT NULL,
  `tenant_id` VARCHAR(96) NOT NULL,
  `project_code` VARCHAR(128) NOT NULL,
  `requested_by_user_id` VARCHAR(128) NOT NULL,
  `source_type` VARCHAR(32) NOT NULL COMMENT 'AI_CODING_TASK / AGENT_DELEGATION / OPERATOR',
  `source_ref` VARCHAR(128) DEFAULT NULL,
  `executor_provider` VARCHAR(32) NOT NULL DEFAULT 'CODEX',
  `sandbox_profile` VARCHAR(48) NOT NULL,
  `model_ref` VARCHAR(128) DEFAULT NULL,
  `acceptance_profile` VARCHAR(128) NOT NULL DEFAULT 'PROJECT_DEFAULT',
  `objective_text` MEDIUMTEXT NOT NULL COMMENT '仅向 execution-scoped Worker 返回；禁止写入日志或 outbox',
  `objective_sha256` CHAR(64) NOT NULL,
  `status` VARCHAR(32) NOT NULL DEFAULT 'QUEUED' COMMENT 'REQUESTED / QUEUED / PROVISIONING / RUNNING / WAITING_APPROVAL / WAITING_USER / FINALIZING / CANCELLING / SUCCEEDED / FAILED / TIMED_OUT / CANCELLED',
  `cleanup_status` VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / RUNNING / COMPLETED / FAILED',
  `sandbox_ref` VARCHAR(128) DEFAULT NULL COMMENT 'Runtime-owned disposable sandbox handle; never supplied by Worker',
  `cleanup_attempt_count` INT NOT NULL DEFAULT 0,
  `cleanup_available_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `cleanup_error` VARCHAR(1000) DEFAULT NULL,
  `priority` INT NOT NULL DEFAULT 0,
  `max_wall_time_seconds` INT NOT NULL,
  `approval_timeout_seconds` INT NOT NULL,
  `approval_count` INT NOT NULL DEFAULT 0,
  `command_sequence` BIGINT NOT NULL DEFAULT 0,
  `pending_approval_request_id` VARCHAR(160) DEFAULT NULL,
  `pending_interaction_id` VARCHAR(64) DEFAULT NULL,
  `approval_decision` VARCHAR(16) DEFAULT NULL COMMENT 'accept / decline; one-shot and execution scoped',
  `approval_decided_at` DATETIME DEFAULT NULL,
  `last_event_sequence` INT NOT NULL DEFAULT 0,
  `provision_attempt_count` INT NOT NULL DEFAULT 0,
  `provision_max_attempts` INT NOT NULL DEFAULT 3,
  `provision_available_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `worker_token_digest` CHAR(64) DEFAULT NULL,
  `worker_token_expires_at` DATETIME DEFAULT NULL,
  `lease_owner` VARCHAR(128) DEFAULT NULL,
  `lease_expires_at` DATETIME DEFAULT NULL,
  `last_heartbeat_at` DATETIME DEFAULT NULL,
  `cancel_requested_at` DATETIME DEFAULT NULL,
  `error_code` VARCHAR(128) DEFAULT NULL,
  `error_message` VARCHAR(2000) DEFAULT NULL,
  `version` BIGINT NOT NULL DEFAULT 0,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `started_at` DATETIME DEFAULT NULL,
  `finalizing_at` DATETIME DEFAULT NULL,
  `completed_at` DATETIME DEFAULT NULL,
  UNIQUE KEY `uk_runtime_managed_execution_id` (`execution_id`),
  KEY `idx_runtime_managed_execution_queue` (`status`, `provision_available_at`, `priority`, `id`),
  KEY `idx_runtime_managed_execution_lease` (`status`, `lease_expires_at`, `id`),
  KEY `idx_runtime_managed_execution_cleanup` (`cleanup_status`, `cleanup_available_at`, `status`, `id`),
  UNIQUE KEY `uk_runtime_managed_execution_source` (`tenant_id`, `project_code`, `source_type`, `source_ref`),
  KEY `idx_runtime_managed_execution_project` (`tenant_id`, `project_code`, `status`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime Managed Executor execution aggregate and worker lease';

CREATE TABLE IF NOT EXISTS `runtime_managed_execution_event` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `execution_id` VARCHAR(64) NOT NULL,
  `sequence` INT NOT NULL,
  `event_id` VARCHAR(160) NOT NULL,
  `event_type` VARCHAR(48) NOT NULL,
  `phase` VARCHAR(32) NOT NULL,
  `visibility` VARCHAR(24) NOT NULL,
  `persistence` VARCHAR(24) NOT NULL DEFAULT 'DURABLE',
  `message` VARCHAR(2000) NOT NULL,
  `data_json` MEDIUMTEXT NOT NULL,
  `payload_sha256` CHAR(64) NOT NULL,
  `occurred_at` DATETIME NOT NULL,
  `received_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_managed_event_sequence` (`execution_id`, `sequence`),
  UNIQUE KEY `uk_runtime_managed_event_id` (`execution_id`, `event_id`),
  KEY `idx_runtime_managed_event_type` (`execution_id`, `event_type`, `sequence`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Sanitized ordered Managed Executor event ledger without reasoning or raw output deltas';

CREATE TABLE IF NOT EXISTS `runtime_managed_artifact` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `artifact_id` VARCHAR(128) NOT NULL,
  `execution_id` VARCHAR(64) NOT NULL,
  `artifact_type` VARCHAR(48) NOT NULL,
  `object_key` VARCHAR(1000) NOT NULL,
  `sha256` CHAR(64) NOT NULL,
  `size_bytes` BIGINT NOT NULL,
  `media_type` VARCHAR(128) NOT NULL,
  `validation_status` VARCHAR(24) NOT NULL DEFAULT 'PENDING',
  `scan_status` VARCHAR(24) NOT NULL DEFAULT 'PENDING',
  `rejection_code` VARCHAR(128) DEFAULT NULL,
  `retention_expires_at` DATETIME NOT NULL COMMENT 'Runtime access deadline; object-store lifecycle must delete no later than this deadline',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY `uk_runtime_managed_artifact_id` (`artifact_id`),
  UNIQUE KEY `uk_runtime_managed_artifact_type` (`execution_id`, `artifact_type`),
  KEY `idx_runtime_managed_artifact_validation` (`validation_status`, `scan_status`, `updated_at`),
  KEY `idx_runtime_managed_artifact_retention` (`retention_expires_at`, `execution_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Managed Executor object-store artifact metadata and fail-closed verification state';

CREATE TABLE IF NOT EXISTS `runtime_managed_execution_outbox` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `event_id` VARCHAR(64) NOT NULL,
  `execution_id` VARCHAR(64) NOT NULL,
  `event_type` VARCHAR(64) NOT NULL,
  `payload_json` MEDIUMTEXT NOT NULL COMMENT 'Metadata only; excludes objective, token and artifact body',
  `status` VARCHAR(24) NOT NULL DEFAULT 'PENDING',
  `attempt_count` INT NOT NULL DEFAULT 0,
  `available_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `lease_owner` VARCHAR(128) DEFAULT NULL,
  `lease_token` CHAR(36) DEFAULT NULL,
  `lease_expires_at` DATETIME DEFAULT NULL,
  `last_error` VARCHAR(2000) DEFAULT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `published_at` DATETIME DEFAULT NULL,
  UNIQUE KEY `uk_runtime_managed_outbox_event` (`event_id`),
  KEY `idx_runtime_managed_outbox_dispatch` (`status`, `available_at`, `lease_expires_at`, `id`),
  KEY `idx_runtime_managed_outbox_execution` (`execution_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Durable Managed Executor status and artifact notification outbox';

CREATE TABLE IF NOT EXISTS `control_managed_execution_inbox` (
  `event_id` VARCHAR(64) NOT NULL,
  `execution_id` VARCHAR(64) NOT NULL,
  `event_type` VARCHAR(64) NOT NULL,
  `tenant_id` VARCHAR(96) NOT NULL,
  `project_code` VARCHAR(128) NOT NULL,
  `source_type` VARCHAR(32) NOT NULL,
  `source_ref` VARCHAR(128) DEFAULT NULL,
  `runtime_status` VARCHAR(32) NOT NULL,
  `payload_sha256` CHAR(64) NOT NULL COMMENT 'Digest of the exact HMAC-verified Runtime request body',
  `payload_json` MEDIUMTEXT NOT NULL COMMENT 'Sanitized metadata only; excludes objective, token and Artifact body',
  `projection_status` VARCHAR(24) NOT NULL DEFAULT 'RECEIVED',
  `projection_error` VARCHAR(1000) DEFAULT NULL,
  `projection_attempt_count` INT NOT NULL DEFAULT 0,
  `projection_available_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `received_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `applied_at` DATETIME DEFAULT NULL,
  PRIMARY KEY (`event_id`),
  KEY `idx_control_managed_inbox_execution` (`execution_id`, `received_at`),
  KEY `idx_control_managed_inbox_source` (`source_type`, `source_ref`, `received_at`),
  KEY `idx_control_managed_inbox_projection` (`projection_status`, `projection_available_at`, `received_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Idempotent Runtime Managed Execution event inbox and Control projection receipt';


CREATE TABLE IF NOT EXISTS `runtime_agent_eval_dataset` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `agent_id` VARCHAR(64) DEFAULT NULL COMMENT '关联 runtime_agent.id；Workflow Working Copy 评测可为空',
  `agent_name` VARCHAR(128) DEFAULT NULL,
  `name` VARCHAR(128) NOT NULL,
  `description` VARCHAR(512) DEFAULT NULL,
  `source` VARCHAR(32) NOT NULL DEFAULT 'IMPORT',
  `case_count` INT NOT NULL DEFAULT 0,
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  KEY `idx_eval_dataset_agent` (`agent_id`, `create_time`),
  KEY `idx_eval_dataset_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Supervisor 评测数据集';

CREATE TABLE IF NOT EXISTS `runtime_agent_eval_case` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `dataset_id` BIGINT NOT NULL,
  `case_no` VARCHAR(64) NOT NULL,
  `message` TEXT DEFAULT NULL,
  `input_params_json` MEDIUMTEXT DEFAULT NULL,
  `expected_json` MEDIUMTEXT DEFAULT NULL,
  `judge_config_json` MEDIUMTEXT DEFAULT NULL,
  `tags` VARCHAR(512) DEFAULT NULL,
  `enabled` TINYINT(1) NOT NULL DEFAULT 1,
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `idx_eval_case_dataset` (`dataset_id`, `enabled`, `id`),
  UNIQUE KEY `uk_eval_case_no` (`dataset_id`, `case_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Supervisor 评测用例';

CREATE TABLE IF NOT EXISTS `runtime_agent_eval_run` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `dataset_id` BIGINT NOT NULL,
  `agent_id` VARCHAR(64) DEFAULT NULL,
  `agent_name` VARCHAR(128) DEFAULT NULL,
  `run_name` VARCHAR(128) DEFAULT NULL,
  `repeat_count` INT NOT NULL DEFAULT 1,
  `status` VARCHAR(32) NOT NULL DEFAULT 'PENDING',
  `target_snapshot_id` BIGINT DEFAULT NULL,
  `target_config_version_id` BIGINT DEFAULT NULL,
  `target_config_status` VARCHAR(24) DEFAULT NULL,
  `target_fingerprint` CHAR(64) DEFAULT NULL,
  `canvas_snapshot_json` MEDIUMTEXT DEFAULT NULL,
  `graph_spec_json` MEDIUMTEXT DEFAULT NULL,
  `summary_json` MEDIUMTEXT DEFAULT NULL,
  `suggestion_json` MEDIUMTEXT DEFAULT NULL,
  `started_at` DATETIME DEFAULT NULL,
  `finished_at` DATETIME DEFAULT NULL,
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `idx_eval_run_dataset` (`dataset_id`, `create_time`),
  KEY `idx_eval_run_agent` (`agent_id`, `create_time`),
  KEY `idx_eval_run_status` (`status`, `create_time`),
  KEY `idx_eval_run_target_snapshot` (`target_snapshot_id`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Supervisor 真实运行时评测任务';

CREATE TABLE IF NOT EXISTS `runtime_agent_eval_case_result` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `run_id` BIGINT NOT NULL,
  `dataset_id` BIGINT NOT NULL,
  `case_id` BIGINT NOT NULL,
  `case_no` VARCHAR(64) NOT NULL,
  `round_no` INT NOT NULL DEFAULT 1,
  `status` VARCHAR(32) NOT NULL DEFAULT 'PENDING',
  `runtime_success` TINYINT(1) NOT NULL DEFAULT 0,
  `assertion_passed` TINYINT(1) NOT NULL DEFAULT 0,
  `semantic_score` DOUBLE DEFAULT NULL,
  `score` DOUBLE NOT NULL DEFAULT 0,
  `elapsed_ms` INT NOT NULL DEFAULT 0,
  `answer` MEDIUMTEXT DEFAULT NULL,
  `trace_id` VARCHAR(96) DEFAULT NULL,
  `step_results_json` MEDIUMTEXT DEFAULT NULL,
  `judge_result_json` MEDIUMTEXT DEFAULT NULL,
  `error_code` VARCHAR(128) DEFAULT NULL,
  `error_message` TEXT DEFAULT NULL,
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `idx_eval_result_run` (`run_id`, `case_id`, `round_no`),
  KEY `idx_eval_result_case` (`case_id`, `create_time`),
  KEY `idx_eval_result_status` (`run_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Supervisor 运行时评测用例结果';


-- ============================================================================
-- 七.六、Tool 语义检索：持久化「重建向量索引」选用的 Embedding 模型实例
-- （与历史 capability_tool_retrieval_setting 补丁一致）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_tool_retrieval_setting` (
    `id`                            CHAR(1)      NOT NULL DEFAULT '1' COMMENT '固定单例行',
    `embedding_model_instance_id`   VARCHAR(64)  DEFAULT NULL COMMENT '上次重建 Tool 向量索引选用的模型实例；对话侧语义召回与用户问题向量化共用',
    `updated_at`                    DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tool 语义检索全局运行时设置（单例）';

INSERT IGNORE INTO `capability_tool_retrieval_setting` (`id`, `embedding_model_instance_id`) VALUES ('1', NULL);


-- ============================================================================
-- 九、Phase 3.1 Tool ACL（角色 × 能力 黑白名单）
-- ----------------------------------------------------------------------------
-- 与历史 tool_acl_phase3_1 补丁保持一致（幂等）。
--   - DENY 优先；无命中默认拒绝；target_name='*' 通配；target_kind='ALL' 表示全部 TOOL。
--   - 上下文 roles 为空时走旧行为（不拦截，仅 warn），方便灰度接入。
-- ============================================================================

CREATE TABLE IF NOT EXISTS `control_tool_acl` (
    `id`            BIGINT        NOT NULL AUTO_INCREMENT,
    `role_code`     VARCHAR(64)   NOT NULL                     COMMENT '角色编码',
    `target_kind`   VARCHAR(16)   NOT NULL DEFAULT 'TOOL'      COMMENT 'TOOL / ALL',
    `target_name`   VARCHAR(128)  NOT NULL                     COMMENT 'capability_tool_definition.name 或 *',
    `permission`    VARCHAR(16)   NOT NULL DEFAULT 'ALLOW'     COMMENT 'ALLOW / DENY',
    `note`          VARCHAR(512)  DEFAULT NULL,
    `enabled`       TINYINT(1)    NOT NULL DEFAULT 1,
    `created_at`    DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_role_kind_target` (`role_code`, `target_kind`, `target_name`),
    KEY `idx_role_enabled`   (`role_code`, `enabled`),
    KEY `idx_target`         (`target_kind`, `target_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tool 角色访问控制（Phase 3.1）';

CALL add_col_if_absent('control_tool_acl', 'target_kind', 'VARCHAR(16) NOT NULL DEFAULT ''TOOL'' COMMENT ''TOOL / ALL'' AFTER `role_code`');
CALL add_col_if_absent('control_tool_acl', 'permission',  'VARCHAR(16) NOT NULL DEFAULT ''ALLOW'' COMMENT ''ALLOW / DENY'' AFTER `target_name`');
CALL add_idx_if_absent('control_tool_acl', 'idx_role_enabled', 'role_code, enabled');
CALL add_idx_if_absent('control_tool_acl', 'idx_target',       'target_kind, target_name');

INSERT INTO `control_tool_acl` (`role_code`, `target_kind`, `target_name`, `permission`, `note`)
SELECT * FROM (
    SELECT 'admin'  AS role_code, 'ALL'  AS target_kind, '*' AS target_name, 'ALLOW' AS permission, '内建：管理员默认放行全部能力' AS note UNION ALL
    SELECT 'public',               'TOOL',                 '*',                 'DENY',             '内建：匿名身份默认拒绝所有 TOOL'
) AS seed
WHERE NOT EXISTS (SELECT 1 FROM `control_tool_acl` LIMIT 1);


-- ============================================================================
-- 七.c、Phase P1 —— DomainClassifier（领域定义 + 归属挂接）
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_domain_def` (
    `id`             BIGINT        NOT NULL AUTO_INCREMENT,
    `code`           VARCHAR(64)   NOT NULL,
    `name`           VARCHAR(128)  NOT NULL,
    `description`    VARCHAR(512)  DEFAULT NULL,
    `keywords_json`  VARCHAR(2000) DEFAULT NULL,
    `parent_code`    VARCHAR(64)   DEFAULT NULL,
    `enabled`        TINYINT(1)    NOT NULL DEFAULT 1,
    `created_at`     DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`     DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_code` (`code`),
    KEY `idx_parent` (`parent_code`),
    KEY `idx_parent_code` (`parent_code`),
    KEY `idx_enabled`     (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='领域定义 (Phase P1)';

CALL add_idx_if_absent('capability_domain_def', 'idx_parent', '`parent_code`');

CREATE TABLE IF NOT EXISTS `capability_domain_assignment` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `target_kind`   VARCHAR(16)  NOT NULL                COMMENT 'PROJECT / TOOL / AGENT',
    `target_name`   VARCHAR(192) NOT NULL,
    `domain_code`   VARCHAR(64)  NOT NULL,
    `weight`        DECIMAL(5,3) NOT NULL DEFAULT 1.000,
    `source`        VARCHAR(32)  NOT NULL DEFAULT 'MANUAL' COMMENT 'MANUAL / AUTO_FROM_PROJECT',
    `created_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_kind_name_domain` (`target_kind`, `target_name`, `domain_code`),
    KEY `idx_domain` (`domain_code`),
    KEY `idx_target` (`target_kind`, `target_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tool/Agent 领域挂接 (Phase P1)';

CALL add_col_if_absent('capability_scan_project', 'default_domain_code', 'VARCHAR(64) DEFAULT NULL COMMENT ''扫描项目默认领域 (Phase P1)''');


-- ============================================================================
-- 七.d、MCP 互联中心（MCP Hub，clean-slate；M1 出向重塑）
--   - 旧 Phase P2 暴露白名单 / Client / 调用日志结构不兼容、不迁移、不双写。
--   - control_mcp_client / control_mcp_call_log 仅在检测到 Phase P2 旧结构时删除，
--     避免基线重复执行误删已经按新结构运行的数据。
--   - 出向 Tool 投影来自能力目录与已发布 Workflow，发布时冻结为不可变修订快照；
--     tools/list 与 tools/call 只按已发布修订执行。
--   - control_mcp_call_log 双向复用：direction OUTBOUND 被外部调用 / INBOUND 调用外部。
-- ============================================================================

DROP TABLE IF EXISTS `control_mcp_visibility`;

SET @drop_legacy_mcp_client = IF(
    EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = DATABASE()
          AND table_name = 'control_mcp_client'
          AND column_name = 'tool_whitelist_json'
    ),
    'DROP TABLE `control_mcp_client`',
    'SELECT 1'
);
PREPARE drop_legacy_mcp_client_stmt FROM @drop_legacy_mcp_client;
EXECUTE drop_legacy_mcp_client_stmt;
DEALLOCATE PREPARE drop_legacy_mcp_client_stmt;

SET @drop_legacy_mcp_call_log = IF(
    EXISTS (
        SELECT 1 FROM information_schema.tables
        WHERE table_schema = DATABASE()
          AND table_name = 'control_mcp_call_log'
    )
    AND NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = DATABASE()
          AND table_name = 'control_mcp_call_log'
          AND column_name = 'direction'
    ),
    'DROP TABLE `control_mcp_call_log`',
    'SELECT 1'
);
PREPARE drop_legacy_mcp_call_log_stmt FROM @drop_legacy_mcp_call_log;
EXECUTE drop_legacy_mcp_call_log_stmt;
DEALLOCATE PREPARE drop_legacy_mcp_call_log_stmt;

CREATE TABLE IF NOT EXISTS `control_mcp_publication` (
    `id`                   BIGINT        NOT NULL AUTO_INCREMENT,
    `name`                 VARCHAR(128)  NOT NULL COMMENT '对外服务名称（唯一）',
    `description`          VARCHAR(1000) DEFAULT NULL COMMENT '发布说明',
    `state`                VARCHAR(16)   NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT/VALIDATING/READY/PUBLISHED/SUSPENDED/ARCHIVED',
    `current_revision_id`  BIGINT        DEFAULT NULL COMMENT '当前生效修订（PUBLISHED 必填）',
    `created_at`           DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`           DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_name`   (`name`),
    KEY        `idx_state` (`state`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MCP 对外发布单元 (MCP Hub)';

CREATE TABLE IF NOT EXISTS `control_mcp_publication_item` (
    `id`                   BIGINT        NOT NULL AUTO_INCREMENT,
    `publication_id`       BIGINT        NOT NULL,
    `source_kind`          VARCHAR(16)   NOT NULL COMMENT 'CAPABILITY / WORKFLOW',
    `source_ref`           VARCHAR(192)  NOT NULL COMMENT '能力 name 或 workflowId',
    `alias`                VARCHAR(128)  DEFAULT NULL COMMENT '对外工具名覆盖（空则用解析名）',
    `description_override` VARCHAR(1000) DEFAULT NULL COMMENT '对外描述覆盖',
    `risk_level_override`  VARCHAR(16)   DEFAULT NULL COMMENT '发布上下文风险覆盖：READ/WRITE/PAGE_ACTION/IRREVERSIBLE',
    `enabled`              TINYINT(1)    NOT NULL DEFAULT 1,
    `created_at`           DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`           DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_pub_kind_ref` (`publication_id`, `source_kind`, `source_ref`),
    KEY        `idx_source`       (`source_kind`, `source_ref`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MCP 发布条目（草稿组成单元）';

CREATE TABLE IF NOT EXISTS `control_mcp_publication_revision` (
    `id`                  BIGINT        NOT NULL AUTO_INCREMENT,
    `publication_id`      BIGINT        NOT NULL,
    `revision_no`         BIGINT        NOT NULL COMMENT '发布修订号，从 1 递增',
    `tools_snapshot_json` MEDIUMTEXT    NOT NULL COMMENT '冻结的工具投影快照（tools/list 事实源）',
    `risk_summary_json`   VARCHAR(2048) DEFAULT NULL COMMENT '风险等级汇总与 IRREVERSIBLE 放行证据',
    `published_at`        DATETIME      NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_pub_revision` (`publication_id`, `revision_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MCP 发布修订（不可变快照）';

CREATE TABLE IF NOT EXISTS `control_mcp_client` (
    `id`                   BIGINT       NOT NULL AUTO_INCREMENT,
    `publication_id`       BIGINT       NOT NULL COMMENT '凭证归属的发布单元',
    `name`                 VARCHAR(128) NOT NULL,
    `api_key_prefix`       VARCHAR(16)  NOT NULL,
    `project_id`           BIGINT       DEFAULT NULL COMMENT '所属项目',
    `project_code`         VARCHAR(96)  NOT NULL COMMENT '所属项目编码',
    `environment`          VARCHAR(32)  NOT NULL COMMENT '环境',
    `tenant_id`            VARCHAR(96)  NOT NULL COMMENT '租户',
    `api_key_hash`         VARCHAR(128) NOT NULL,
    `roles_json`           VARCHAR(1024) DEFAULT NULL COMMENT '对接 control_tool_acl 的角色',
    `tool_scope_json`      TEXT         DEFAULT NULL COMMENT '工具范围（仅可引用所属发布内工具，空=整包）',
    `state`                VARCHAR(16)  NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/ROTATED/REVOKED/EXPIRED；后三者为终态不可逆',
    `enabled`              TINYINT(1)   NOT NULL DEFAULT 1,
    `expires_at`           DATETIME     DEFAULT NULL,
    `last_used_at`         DATETIME     DEFAULT NULL,
    `created_at`           DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`           DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_api_key_hash` (`api_key_hash`),
    KEY        `idx_publication` (`publication_id`),
    KEY        `idx_enabled`     (`enabled`),
    KEY        `idx_mcp_client_project` (`project_id`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MCP Client 凭证 (MCP Hub，挂接单一发布单元)';

CREATE TABLE IF NOT EXISTS `control_mcp_call_log` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `direction`        VARCHAR(16)  NOT NULL DEFAULT 'OUTBOUND' COMMENT 'OUTBOUND 被外部调用 / INBOUND 平台调用外部',
    `publication_id`   BIGINT       DEFAULT NULL COMMENT 'OUTBOUND 方向的发布单元',
    `client_id`        BIGINT       DEFAULT NULL,
    `client_name`      VARCHAR(128) DEFAULT NULL,
    `remote_server_id` BIGINT       DEFAULT NULL COMMENT 'INBOUND 方向的外部 MCP 服务 (M2)',
    `method`           VARCHAR(64)  NOT NULL,
    `tool_name`        VARCHAR(128) DEFAULT NULL,
    `project_id`       BIGINT       DEFAULT NULL COMMENT '所属项目',
    `project_code`     VARCHAR(96)  DEFAULT NULL COMMENT '所属项目编码',
    `environment`      VARCHAR(32)  DEFAULT NULL COMMENT '环境',
    `tenant_id`        VARCHAR(96)  DEFAULT NULL COMMENT '租户',
    `success`          TINYINT(1)   NOT NULL DEFAULT 0,
    `latency_ms`       BIGINT       DEFAULT NULL,
    `error_category`   VARCHAR(32)  DEFAULT NULL COMMENT 'PROTOCOL/AUTH/POLICY/RUNTIME/REMOTE',
    `request_body`     MEDIUMTEXT   DEFAULT NULL,
    `response_body`    MEDIUMTEXT   DEFAULT NULL,
    `error_message`    VARCHAR(2000) DEFAULT NULL,
    `trace_id`         VARCHAR(64)  DEFAULT NULL,
    `run_id`           VARCHAR(64)  DEFAULT NULL COMMENT 'MCP tools/call 关联的 Runtime run（双向复用）',
    `remote_ip`        VARCHAR(64)  DEFAULT NULL,
    `created_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_direction_created`  (`direction`, `created_at`),
    KEY `idx_publication_created` (`publication_id`, `created_at`),
    KEY `idx_client_created`     (`client_id`, `created_at`),
    KEY `idx_method`             (`method`, `created_at`),
    KEY `idx_trace`              (`trace_id`),
    KEY `idx_mcp_log_project_trace` (`project_code`, `trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MCP 双向调用审计 (MCP Hub)';


-- ============================================================================
-- 七.e、A2A 互联中心（A2A Hub，clean-slate A2A Protocol 1.0）
--   - 旧 endpoint/call-log/task 结构不兼容、不迁移、不双写。
--   - control_a2a_task 仅在检测到旧 endpoint_id 列时删除，避免基线重复执行
--     误删已经按新结构运行的数据。
--   - Message/Artifact 正文只保存密文或受控对象引用；transport event 禁止原文。
-- ============================================================================

DROP TABLE IF EXISTS `control_a2a_endpoint`;
DROP TABLE IF EXISTS `control_a2a_call_log`;

SET @drop_legacy_a2a_task = IF(
    EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = DATABASE()
          AND table_name = 'control_a2a_task'
          AND column_name = 'endpoint_id'
    ),
    'DROP TABLE `control_a2a_task`',
    'SELECT 1'
);
PREPARE drop_legacy_a2a_task_stmt FROM @drop_legacy_a2a_task;
EXECUTE drop_legacy_a2a_task_stmt;
DEALLOCATE PREPARE drop_legacy_a2a_task_stmt;

CREATE TABLE IF NOT EXISTS `control_a2a_trust_profile` (
    `id`                           BIGINT        NOT NULL AUTO_INCREMENT,
    `profile_key`                  VARCHAR(96)   NOT NULL COMMENT '稳定策略标识',
    `name`                         VARCHAR(128)  NOT NULL,
    `description`                  VARCHAR(1000) DEFAULT NULL,
    `direction`                    VARCHAR(16)   NOT NULL COMMENT 'INBOUND / OUTBOUND / BIDIRECTIONAL',
    `environment`                  VARCHAR(32)   NOT NULL DEFAULT 'DEVELOPMENT',
    `trust_level`                  VARCHAR(24)   NOT NULL DEFAULT 'KNOWN' COMMENT 'UNTRUSTED / KNOWN / TRUSTED / PRIVILEGED',
    `authentication_methods_json`  TEXT          NOT NULL COMMENT '允许的标准认证方式；不含凭据',
    `allowed_scopes_json`          TEXT          DEFAULT NULL,
    `authorization_policy_json`    MEDIUMTEXT    NOT NULL COMMENT 'Publication/AgentSkill/tenant/操作白名单',
    `data_policy_json`             MEDIUMTEXT    NOT NULL COMMENT '数据等级、Part、正文与保留策略',
    `delegated_identity_policy`    VARCHAR(32)   NOT NULL DEFAULT 'DENY' COMMENT 'DENY / ATTESTED_ONLY',
    `personal_memory_policy`       VARCHAR(32)   NOT NULL DEFAULT 'DISABLED' COMMENT 'A2A Principal 默认禁用个人记忆',
    `rate_limit_per_minute`        INT           NOT NULL DEFAULT 60,
    `max_concurrent_tasks`         INT           NOT NULL DEFAULT 10,
    `max_request_bytes`            BIGINT        NOT NULL DEFAULT 1048576,
    `max_artifact_bytes`           BIGINT        NOT NULL DEFAULT 10485760,
    `task_timeout_ms`              BIGINT        NOT NULL DEFAULT 300000,
    `allow_anonymous`              TINYINT(1)    NOT NULL DEFAULT 0 COMMENT '仅显式本地开发策略可启用',
    `status`                       VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / DISABLED / ARCHIVED',
    `version`                      INT           NOT NULL DEFAULT 0 COMMENT '乐观锁',
    `created_by`                   VARCHAR(64)   DEFAULT NULL,
    `updated_by`                   VARCHAR(64)   DEFAULT NULL,
    `created_at`                   DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                   DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_trust_profile_key` (`profile_key`),
    KEY `idx_a2a_trust_profile_status` (`direction`, `environment`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 认证、授权、数据与配额信任策略';

CREATE TABLE IF NOT EXISTS `control_a2a_credential` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `credential_key`        VARCHAR(96)   NOT NULL,
    `name`                  VARCHAR(128)  NOT NULL,
    `direction`             VARCHAR(16)   NOT NULL COMMENT 'INBOUND / OUTBOUND',
    `credential_type`       VARCHAR(40)   NOT NULL COMMENT 'API_KEY / HMAC / OAUTH2_CLIENT / MTLS / BEARER',
    `material_mode`         VARCHAR(32)   NOT NULL COMMENT 'HASHED / ENCRYPTED / EXTERNAL_REF / CERTIFICATE_REF',
    `material_hash`         VARCHAR(255)  DEFAULT NULL COMMENT '入站验证慢哈希；不保存原始秘密',
    `material_salt`         VARCHAR(128)  DEFAULT NULL,
    `material_ciphertext`   MEDIUMTEXT    DEFAULT NULL COMMENT 'AES-GCM 密文；禁止 API 回显',
    `encryption_key_id`     VARCHAR(96)   DEFAULT NULL,
    `encryption_nonce`      VARCHAR(128)  DEFAULT NULL,
    `external_ref`          VARCHAR(512)  DEFAULT NULL COMMENT '外部 secret/certificate 引用',
    `fingerprint`           CHAR(64)      NOT NULL COMMENT '不可逆显示与轮换识别指纹',
    `version_no`            INT           NOT NULL DEFAULT 1,
    `status`                VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / GRACE / REVOKED / EXPIRED',
    `not_before`            DATETIME      DEFAULT NULL,
    `expires_at`            DATETIME      DEFAULT NULL,
    `last_used_at`          DATETIME      DEFAULT NULL,
    `rotated_from_id`       BIGINT        DEFAULT NULL,
    `created_by`            VARCHAR(64)   DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_credential_key_version` (`credential_key`, `version_no`),
    KEY `idx_a2a_credential_status` (`direction`, `credential_type`, `status`, `expires_at`),
    KEY `idx_a2a_credential_rotation` (`rotated_from_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 哈希、密文或外部引用凭据；永不保存或回显明文';

CREATE TABLE IF NOT EXISTS `control_a2a_principal` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `principal_key`         VARCHAR(128)  NOT NULL,
    `principal_type`        VARCHAR(32)   NOT NULL DEFAULT 'REMOTE_AGENT' COMMENT 'REMOTE_AGENT / REMOTE_CLIENT / LOCAL_AGENT',
    `display_name`          VARCHAR(128)  NOT NULL,
    `tenant_scope`          VARCHAR(96)   NOT NULL DEFAULT '' COMMENT '空串表示平台作用域',
    `authenticated_subject` VARCHAR(512)  DEFAULT NULL COMMENT '凭据映射后的稳定 subject；不是请求 metadata',
    `trust_profile_id`      BIGINT        NOT NULL,
    `credential_id`         BIGINT        DEFAULT NULL,
    `scopes_json`           TEXT          DEFAULT NULL,
    `attributes_json`       TEXT          DEFAULT NULL COMMENT '经管理员确认的非秘密属性',
    `status`                VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / DISABLED / QUARANTINED / ARCHIVED',
    `last_authenticated_at` DATETIME      DEFAULT NULL,
    `version`               INT           NOT NULL DEFAULT 0,
    `created_by`            VARCHAR(64)   DEFAULT NULL,
    `updated_by`            VARCHAR(64)   DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_principal_key` (`principal_key`),
    KEY `idx_a2a_principal_owner` (`tenant_scope`, `principal_type`, `status`),
    KEY `idx_a2a_principal_trust` (`trust_profile_id`, `status`),
    KEY `idx_a2a_principal_credential` (`credential_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 经认证的调用主体；安全身份不得来自协议 metadata';

CREATE TABLE IF NOT EXISTS `control_a2a_publication` (
    `id`                       BIGINT        NOT NULL AUTO_INCREMENT,
    `publication_key`          VARCHAR(96)   NOT NULL COMMENT '稳定公开标识',
    `agent_id`                 VARCHAR(32)   NOT NULL COMMENT 'runtime_agent.id 逻辑引用',
    `project_id`               BIGINT        DEFAULT NULL,
    `project_code`             VARCHAR(96)   DEFAULT NULL,
    `environment`              VARCHAR(32)   NOT NULL DEFAULT 'DEVELOPMENT',
    `tenant_scope`             VARCHAR(96)   NOT NULL DEFAULT '',
    `public_host`              VARCHAR(255)  DEFAULT NULL COMMENT '生产 host-based routing；不含 scheme/path',
    `trust_profile_id`         BIGINT        NOT NULL,
    `current_revision_id`      BIGINT        DEFAULT NULL,
    `status`                   VARCHAR(24)   NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT / VALIDATING / READY / PUBLISHED / SUSPENDED / ARCHIVED',
    `version`                  INT           NOT NULL DEFAULT 0 COMMENT '乐观锁',
    `published_at`             DATETIME      DEFAULT NULL,
    `suspended_at`             DATETIME      DEFAULT NULL,
    `created_by`               VARCHAR(64)   DEFAULT NULL,
    `updated_by`               VARCHAR(64)   DEFAULT NULL,
    `created_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_publication_key` (`publication_key`),
    UNIQUE KEY `uk_a2a_publication_agent_scope` (`agent_id`, `environment`, `tenant_scope`),
    UNIQUE KEY `uk_a2a_publication_public_host` (`public_host`),
    KEY `idx_a2a_publication_status` (`environment`, `status`, `updated_at`),
    KEY `idx_a2a_publication_trust` (`trust_profile_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 本地 Agent 发布聚合与当前不可变修订';

CREATE TABLE IF NOT EXISTS `control_a2a_publication_revision` (
    `id`                          BIGINT        NOT NULL AUTO_INCREMENT,
    `publication_id`              BIGINT        NOT NULL,
    `revision_no`                 INT           NOT NULL,
    `agent_config_version_id`     BIGINT        NOT NULL COMMENT '固定 runtime_agent_config_version.id',
    `agent_version`               VARCHAR(64)   NOT NULL,
    `name`                        VARCHAR(128)  NOT NULL,
    `description`                 VARCHAR(2000) NOT NULL,
    `provider_organization`       VARCHAR(255)  DEFAULT NULL,
    `provider_url`                VARCHAR(1000) DEFAULT NULL,
    `documentation_url`           VARCHAR(1000) DEFAULT NULL,
    `icon_url`                    VARCHAR(1000) DEFAULT NULL,
    `public_origin`               VARCHAR(1000) NOT NULL COMMENT '绝对 HTTPS origin 或显式开发 origin',
    `protocol_base_path`          VARCHAR(255)  NOT NULL DEFAULT '/a2a/v1',
    `protocol_binding`            VARCHAR(32)   NOT NULL DEFAULT 'HTTP+JSON',
    `protocol_version`            VARCHAR(16)   NOT NULL DEFAULT '1.0',
    `streaming_supported`         TINYINT(1)    NOT NULL DEFAULT 0,
    `push_notifications_supported` TINYINT(1)   NOT NULL DEFAULT 0,
    `extended_card_supported`     TINYINT(1)    NOT NULL DEFAULT 0,
    `default_input_modes_json`    TEXT          NOT NULL,
    `default_output_modes_json`   TEXT          NOT NULL,
    `protocol_skills_json`        MEDIUMTEXT    NOT NULL COMMENT 'A2A AgentSkill 标准描述；不是 ReachAI Skill 资产',
    `security_schemes_json`       MEDIUMTEXT    DEFAULT NULL COMMENT '公开 scheme 定义；不含凭据',
    `security_requirements_json`  TEXT          DEFAULT NULL,
    `agent_card_json`             MEDIUMTEXT    NOT NULL COMMENT '确定性生成的不可变 A2A 1.0 Card snapshot',
    `agent_card_sha256`           CHAR(64)      NOT NULL,
    `signature_status`            VARCHAR(32)   NOT NULL DEFAULT 'NOT_CONFIGURED',
    `conformance_status`          VARCHAR(32)   NOT NULL DEFAULT 'NOT_RUN',
    `validation_summary_json`     MEDIUMTEXT    DEFAULT NULL COMMENT '无秘密的发布门禁摘要',
    `status`                      VARCHAR(24)   NOT NULL DEFAULT 'DRAFT' COMMENT 'DRAFT / READY / PUBLISHED / ARCHIVED',
    `created_by`                  VARCHAR(64)   DEFAULT NULL,
    `created_at`                  DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `published_at`                DATETIME      DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_publication_revision` (`publication_id`, `revision_no`),
    UNIQUE KEY `uk_a2a_publication_card_hash` (`publication_id`, `agent_card_sha256`),
    KEY `idx_a2a_publication_revision_status` (`publication_id`, `status`, `revision_no`),
    KEY `idx_a2a_publication_agent_config` (`agent_config_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 不可变本地发布修订与 Agent Card 快照';

CREATE TABLE IF NOT EXISTS `control_a2a_remote_agent` (
    `id`                       BIGINT        NOT NULL AUTO_INCREMENT,
    `remote_agent_key`         VARCHAR(128)  NOT NULL,
    `display_name`             VARCHAR(128)  NOT NULL,
    `tenant_scope`             VARCHAR(96)   NOT NULL DEFAULT '',
    `card_url`                 VARCHAR(1000) NOT NULL,
    `card_url_sha256`          CHAR(64)      NOT NULL,
    `trust_profile_id`         BIGINT        DEFAULT NULL,
    `credential_id`            BIGINT        DEFAULT NULL,
    `current_revision_id`      BIGINT        DEFAULT NULL,
    `preferred_interface_key`  VARCHAR(128)  DEFAULT NULL,
    `preferred_security_scheme_key` VARCHAR(128) DEFAULT NULL COMMENT '审核固定的 Agent Card security scheme key；不含秘密',
    `status`                   VARCHAR(32)   NOT NULL DEFAULT 'DISCOVERED' COMMENT 'DISCOVERED / VERIFYING / REVIEW_REQUIRED / TRUSTED / DISABLED / QUARANTINED / ARCHIVED',
    `health_status`            VARCHAR(24)   NOT NULL DEFAULT 'UNKNOWN' COMMENT 'UNKNOWN / HEALTHY / DEGRADED / UNREACHABLE',
    `consecutive_health_failures` INT        NOT NULL DEFAULT 0,
    `last_discovered_at`       DATETIME      DEFAULT NULL,
    `last_health_checked_at`   DATETIME      DEFAULT NULL,
    `last_health_summary`      VARCHAR(1000) DEFAULT NULL,
    `version`                  INT           NOT NULL DEFAULT 0,
    `created_by`               VARCHAR(64)   DEFAULT NULL,
    `updated_by`               VARCHAR(64)   DEFAULT NULL,
    `created_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_remote_agent_key` (`remote_agent_key`),
    UNIQUE KEY `uk_a2a_remote_agent_card_url` (`tenant_scope`, `card_url_sha256`),
    KEY `idx_a2a_remote_agent_status` (`tenant_scope`, `status`, `health_status`),
    KEY `idx_a2a_remote_agent_trust` (`trust_profile_id`, `status`),
    KEY `idx_a2a_remote_agent_credential` (`credential_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 远程 Agent 稳定目录身份与信任/健康状态';

CREATE TABLE IF NOT EXISTS `control_a2a_remote_agent_revision` (
    `id`                         BIGINT        NOT NULL AUTO_INCREMENT,
    `remote_agent_id`            BIGINT        NOT NULL,
    `revision_no`                INT           NOT NULL,
    `name`                       VARCHAR(128)  NOT NULL,
    `description`                VARCHAR(2000) NOT NULL,
    `provider_organization`      VARCHAR(255)  DEFAULT NULL,
    `provider_url`               VARCHAR(1000) DEFAULT NULL,
    `documentation_url`          VARCHAR(1000) DEFAULT NULL,
    `icon_url`                   VARCHAR(1000) DEFAULT NULL,
    `agent_version`              VARCHAR(64)   NOT NULL,
    `supported_interfaces_json`  MEDIUMTEXT    NOT NULL,
    `capabilities_json`          TEXT          NOT NULL,
    `default_input_modes_json`   TEXT          NOT NULL,
    `default_output_modes_json`  TEXT          NOT NULL,
    `protocol_skills_json`       MEDIUMTEXT    NOT NULL,
    `security_schemes_json`      MEDIUMTEXT    DEFAULT NULL,
    `security_requirements_json` TEXT          DEFAULT NULL,
    `agent_card_json`            MEDIUMTEXT    NOT NULL,
    `agent_card_sha256`          CHAR(64)      NOT NULL,
    `signature_status`           VARCHAR(32)   NOT NULL DEFAULT 'NOT_PRESENT' COMMENT 'NOT_PRESENT / VERIFIED / INVALID / UNTRUSTED_KEY',
    `signing_key_id`             VARCHAR(255)  DEFAULT NULL,
    `tls_identity_sha256`        CHAR(64)      DEFAULT NULL,
    `network_evidence_json`      TEXT          DEFAULT NULL COMMENT '无 host/IP/证书正文的安全验证摘要',
    `http_etag`                  VARCHAR(255)  DEFAULT NULL,
    `http_last_modified`         VARCHAR(255)  DEFAULT NULL,
    `review_status`              VARCHAR(24)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / APPROVED / REJECTED / SUPERSEDED',
    `discovered_at`              DATETIME      NOT NULL,
    `reviewed_by`                VARCHAR(64)   DEFAULT NULL,
    `reviewed_at`                DATETIME      DEFAULT NULL,
    `created_at`                 DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_remote_agent_revision` (`remote_agent_id`, `revision_no`),
    KEY `idx_a2a_remote_agent_card_hash` (`remote_agent_id`, `agent_card_sha256`, `revision_no`),
    KEY `idx_a2a_remote_revision_review` (`remote_agent_id`, `review_status`, `revision_no`),
    KEY `idx_a2a_remote_revision_discovered` (`discovered_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 不可变远端 Agent Card 修订与验证证据';

CREATE TABLE IF NOT EXISTS `control_a2a_context` (
    `id`                   BIGINT        NOT NULL AUTO_INCREMENT,
    `context_id`           VARCHAR(128)  NOT NULL COMMENT 'A2A opaque context id',
    `direction`            VARCHAR(16)   NOT NULL COMMENT 'INBOUND / OUTBOUND',
    `principal_id`         BIGINT        NOT NULL,
    `tenant_scope`         VARCHAR(96)   NOT NULL DEFAULT '',
    `publication_id`       BIGINT        DEFAULT NULL,
    `remote_agent_id`      BIGINT        DEFAULT NULL,
    `remote_revision_id`   BIGINT        DEFAULT NULL,
    `remote_context_id`    VARCHAR(128)  DEFAULT NULL COMMENT '远端 A2A opaque context id；与本地 context_id 分离',
    `runtime_session_id`   VARCHAR(128)  DEFAULT NULL,
    `status`               VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / CLOSED / EXPIRED',
    `last_activity_at`     DATETIME      NOT NULL,
    `expires_at`           DATETIME      DEFAULT NULL,
    `created_at`           DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`           DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_context_owner` (`direction`, `principal_id`, `tenant_scope`, `context_id`),
    KEY `idx_a2a_context_publication` (`publication_id`, `status`, `last_activity_at`),
    KEY `idx_a2a_context_remote` (`remote_agent_id`, `remote_revision_id`, `status`, `last_activity_at`),
    KEY `idx_a2a_context_expiry` (`status`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub Principal 作用域的连续协作上下文';

CREATE TABLE IF NOT EXISTS `control_a2a_task` (
    `id`                     BIGINT        NOT NULL AUTO_INCREMENT,
    `task_id`                VARCHAR(128)  NOT NULL COMMENT 'Hub/A2A task id；新任务由服务端生成',
    `direction`              VARCHAR(16)   NOT NULL COMMENT 'INBOUND / OUTBOUND',
    `principal_id`           BIGINT        NOT NULL,
    `tenant_scope`           VARCHAR(96)   NOT NULL DEFAULT '',
    `context_ref_id`         BIGINT        NOT NULL,
    `context_id`             VARCHAR(128)  NOT NULL,
    `publication_id`         BIGINT        DEFAULT NULL,
    `publication_revision_id` BIGINT       DEFAULT NULL,
    `remote_agent_id`        BIGINT        DEFAULT NULL,
    `remote_revision_id`     BIGINT        DEFAULT NULL,
    `remote_task_id`         VARCHAR(128)  DEFAULT NULL,
    `origin_message_id`      VARCHAR(128)  NOT NULL,
    `origin_payload_sha256`  CHAR(64)      NOT NULL COMMENT '重复 messageId 的确定性冲突校验',
    `state`                  VARCHAR(48)   NOT NULL DEFAULT 'TASK_STATE_SUBMITTED',
    `state_version`          INT           NOT NULL DEFAULT 0 COMMENT '状态乐观锁',
    `last_event_sequence`    BIGINT        NOT NULL DEFAULT 0,
    `execution_id`           VARCHAR(128)  NOT NULL COMMENT 'Control/Runtime 幂等执行标识',
    `runtime_run_id`         VARCHAR(128)  DEFAULT NULL,
    `trace_id`               VARCHAR(128)  DEFAULT NULL,
    `runtime_interaction_id` VARCHAR(128)  DEFAULT NULL COMMENT 'Runtime 中断/继续令牌；仅服务间使用',
    `status_message_summary` VARCHAR(1000) DEFAULT NULL COMMENT '脱敏摘要，不是协议正文',
    `error_code`             VARCHAR(96)   DEFAULT NULL,
    `error_summary`          VARCHAR(1000) DEFAULT NULL COMMENT '安全错误摘要',
    `cancel_phase`           VARCHAR(24)   NOT NULL DEFAULT 'NONE' COMMENT 'NONE / REQUESTED / ACCEPTED / UNKNOWN',
    `cancel_requested_at`    DATETIME      DEFAULT NULL,
    `attempt_count`          INT           NOT NULL DEFAULT 0,
    `submitted_at`           DATETIME      NOT NULL,
    `started_at`             DATETIME      DEFAULT NULL,
    `completed_at`           DATETIME      DEFAULT NULL,
    `retention_expires_at`   DATETIME      DEFAULT NULL,
    `deadline_at`            DATETIME      NOT NULL COMMENT 'Task execution deadline derived from accepted Trust Profile',
    `created_at`             DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`             DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_task_owner` (`direction`, `principal_id`, `tenant_scope`, `task_id`),
    UNIQUE KEY `uk_a2a_task_message_idempotency` (`direction`, `principal_id`, `tenant_scope`, `origin_message_id`),
    UNIQUE KEY `uk_a2a_task_execution` (`execution_id`),
    KEY `idx_a2a_task_context` (`context_ref_id`, `submitted_at`),
    KEY `idx_a2a_task_publication_state` (`publication_id`, `state`, `updated_at`),
    KEY `idx_a2a_task_remote_state` (`remote_agent_id`, `state`, `updated_at`),
    KEY `idx_a2a_task_trace` (`trace_id`),
    KEY `idx_a2a_task_run` (`runtime_run_id`),
    KEY `idx_a2a_task_retention` (`retention_expires_at`),
    KEY `idx_a2a_task_deadline` (`state`, `deadline_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub Task 当前投影、严格 owner scope 与 Runtime/Trace 关联';

CREATE TABLE IF NOT EXISTS `control_a2a_outbound_execution` (
    `id`                         BIGINT        NOT NULL AUTO_INCREMENT,
    `task_ref_id`                BIGINT        NOT NULL COMMENT 'control_a2a_task.id；每个出站 Task 唯一扩展',
    `runtime_binding_id`         BIGINT        NOT NULL COMMENT 'Runtime 固定 Agent 配置绑定逻辑引用',
    `agent_config_version_id`    BIGINT        NOT NULL COMMENT '发起委派时的不可变 Agent 配置版本',
    `principal_trust_profile_id` BIGINT        NOT NULL COMMENT 'Task 接受时固定的本地 Principal Trust Profile',
    `remote_trust_profile_id`    BIGINT        NOT NULL COMMENT 'Task 接受时固定的远端 Agent Trust Profile',
    `remote_interface_key`       VARCHAR(128)  NOT NULL COMMENT '固定 Revision 内审核通过的 HTTP+JSON 接口',
    `remote_security_scheme_key` VARCHAR(128)  DEFAULT NULL COMMENT '固定认证方案键；不含秘密',
    `credential_id`              BIGINT        DEFAULT NULL COMMENT '固定出站凭据引用；秘密仍在 credential 密文中',
    `protocol_skill_id`          VARCHAR(200)  NOT NULL COMMENT '本次 Task 已接受的 A2A AgentSkill 描述 id',
    `content_classification`     VARCHAR(32)   NOT NULL,
    `accepted_output_modes_json` TEXT          NOT NULL,
    `history_length`             INT           NOT NULL DEFAULT 20,
    `max_request_bytes`          BIGINT        NOT NULL,
    `max_response_bytes`         INT           NOT NULL,
    `max_artifact_bytes`         BIGINT        NOT NULL,
    `timeout_ms`                 BIGINT        NOT NULL,
    `poll_status`                VARCHAR(24)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / ACTIVE / PAUSED / TERMINAL',
    `poll_attempt_count`         INT           NOT NULL DEFAULT 0,
    `next_poll_at`               DATETIME      DEFAULT NULL,
    `last_polled_at`             DATETIME      DEFAULT NULL,
    `last_poll_error_code`       VARCHAR(96)   DEFAULT NULL,
    `last_poll_error_summary`    VARCHAR(1000) DEFAULT NULL,
    `lease_owner`                VARCHAR(128)  DEFAULT NULL,
    `lease_until`                DATETIME      DEFAULT NULL,
    `created_at`                 DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                 DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_outbound_execution_task` (`task_ref_id`),
    KEY `idx_a2a_outbound_poll_claim` (`poll_status`, `next_poll_at`, `lease_until`),
    KEY `idx_a2a_outbound_binding` (`runtime_binding_id`, `agent_config_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 出站 Task 不可变执行快照与分布式轮询租约';


CREATE TABLE IF NOT EXISTS `control_a2a_message` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `message_id`            VARCHAR(128)  NOT NULL,
    `direction`             VARCHAR(16)   NOT NULL,
    `principal_id`          BIGINT        NOT NULL,
    `tenant_scope`          VARCHAR(96)   NOT NULL DEFAULT '',
    `context_ref_id`        BIGINT        NOT NULL,
    `task_ref_id`           BIGINT        DEFAULT NULL,
    `role`                  VARCHAR(24)   NOT NULL COMMENT 'ROLE_USER / ROLE_AGENT',
    `payload_ciphertext`    MEDIUMTEXT    DEFAULT NULL COMMENT 'canonical Message AES-GCM 密文',
    `payload_object_ref`    VARCHAR(1000) DEFAULT NULL COMMENT '受控对象存储引用；与密文二选一',
    `encryption_key_id`     VARCHAR(96)   DEFAULT NULL,
    `encryption_nonce`      VARCHAR(128)  DEFAULT NULL,
    `payload_sha256`        CHAR(64)      NOT NULL,
    `idempotency_sha256`    CHAR(64)      NOT NULL COMMENT '与密文完整性哈希分离的稳定命令指纹',
    `payload_bytes`         BIGINT        NOT NULL DEFAULT 0,
    `content_classification` VARCHAR(32)  NOT NULL DEFAULT 'INTERNAL',
    `safe_summary`          VARCHAR(1000) DEFAULT NULL,
    `safe_metadata_json`    TEXT          DEFAULT NULL COMMENT 'allowlist 后的无秘密 metadata 摘要',
    `retention_expires_at`  DATETIME      DEFAULT NULL,
    `content_deleted_at`    DATETIME      DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_message_owner` (`direction`, `principal_id`, `tenant_scope`, `message_id`),
    KEY `idx_a2a_message_context` (`context_ref_id`, `created_at`),
    KEY `idx_a2a_message_task` (`task_ref_id`, `created_at`),
    KEY `idx_a2a_message_retention` (`retention_expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 加密 Message 资源；正文不进入 transport log';

CREATE TABLE IF NOT EXISTS `control_a2a_artifact` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `task_ref_id`           BIGINT        NOT NULL,
    `artifact_id`           VARCHAR(128)  NOT NULL,
    `name`                  VARCHAR(255)  DEFAULT NULL,
    `description`           VARCHAR(1000) DEFAULT NULL,
    `payload_ciphertext`    MEDIUMTEXT    DEFAULT NULL COMMENT 'canonical Artifact AES-GCM 密文',
    `payload_object_ref`    VARCHAR(1000) DEFAULT NULL,
    `encryption_key_id`     VARCHAR(96)   DEFAULT NULL,
    `encryption_nonce`      VARCHAR(128)  DEFAULT NULL,
    `payload_sha256`        CHAR(64)      NOT NULL,
    `payload_bytes`         BIGINT        NOT NULL DEFAULT 0,
    `media_types_json`      TEXT          DEFAULT NULL,
    `content_classification` VARCHAR(32)  NOT NULL DEFAULT 'INTERNAL',
    `safe_summary`          VARCHAR(1000) DEFAULT NULL,
    `append_revision`       INT           NOT NULL DEFAULT 0,
    `last_chunk`            TINYINT(1)    NOT NULL DEFAULT 1,
    `retention_expires_at`  DATETIME      DEFAULT NULL,
    `content_deleted_at`    DATETIME      DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_artifact_task` (`task_ref_id`, `artifact_id`),
    KEY `idx_a2a_artifact_retention` (`retention_expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 加密或外置 Task Artifact';

CREATE TABLE IF NOT EXISTS `control_a2a_task_event` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `task_ref_id`           BIGINT        NOT NULL,
    `sequence_no`           BIGINT        NOT NULL,
    `event_id`              VARCHAR(128)  NOT NULL,
    `event_type`            VARCHAR(48)   NOT NULL COMMENT 'TASK_CREATED / STATE_CHANGED / MESSAGE_ADDED / ARTIFACT_UPDATED / POLICY_DECISION / CANCEL_*',
    `from_state`            VARCHAR(48)   DEFAULT NULL,
    `to_state`              VARCHAR(48)   DEFAULT NULL,
    `actor_type`            VARCHAR(32)   NOT NULL,
    `actor_id`              VARCHAR(128)  DEFAULT NULL,
    `resource_type`         VARCHAR(32)   DEFAULT NULL,
    `resource_id`           VARCHAR(128)  DEFAULT NULL,
    `safe_summary`          VARCHAR(1000) DEFAULT NULL,
    `safe_payload_json`     TEXT          DEFAULT NULL COMMENT '脱敏事件摘要；禁止 Message/Artifact 正文',
    `runtime_sequence`      BIGINT        DEFAULT NULL,
    `trace_id`              VARCHAR(128)  DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_task_event_sequence` (`task_ref_id`, `sequence_no`),
    UNIQUE KEY `uk_a2a_task_event_id` (`event_id`),
    UNIQUE KEY `uk_a2a_task_runtime_event` (`task_ref_id`, `runtime_sequence`),
    KEY `idx_a2a_task_event_trace` (`trace_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 不可变、有序 Task 事件账本';

CREATE TABLE IF NOT EXISTS `control_a2a_push_notification_config` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `task_ref_id`           BIGINT        NOT NULL,
    `principal_id`          BIGINT        NOT NULL,
    `config_id`             VARCHAR(128)  NOT NULL,
    `callback_url`          VARCHAR(1000) NOT NULL COMMENT '通过 SSRF/HTTPS 策略验证后的 URL',
    `callback_url_sha256`   CHAR(64)      NOT NULL,
    `credential_id`         BIGINT        DEFAULT NULL,
    `status`                VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / DISABLED / FAILED / DELETED',
    `consecutive_failures`  INT           NOT NULL DEFAULT 0,
    `last_delivery_at`      DATETIME      DEFAULT NULL,
    `last_delivery_summary` VARCHAR(1000) DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_push_config` (`task_ref_id`, `config_id`),
    KEY `idx_a2a_push_principal` (`principal_id`, `status`),
    KEY `idx_a2a_push_credential` (`credential_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub Task push notification 配置与凭据引用';

CREATE TABLE IF NOT EXISTS `control_a2a_transport_event` (
    `id`                       BIGINT        NOT NULL AUTO_INCREMENT,
    `request_id`               VARCHAR(128)  NOT NULL,
    `direction`                VARCHAR(16)   NOT NULL,
    `principal_id`             BIGINT        DEFAULT NULL,
    `publication_id`           BIGINT        DEFAULT NULL,
    `remote_agent_id`          BIGINT        DEFAULT NULL,
    `task_ref_id`              BIGINT        DEFAULT NULL,
    `protocol_binding`         VARCHAR(32)   NOT NULL,
    `protocol_version`         VARCHAR(16)   NOT NULL,
    `operation`                VARCHAR(64)   NOT NULL,
    `success`                  TINYINT(1)    NOT NULL DEFAULT 0,
    `http_status`              INT           DEFAULT NULL,
    `protocol_error_code`      VARCHAR(96)   DEFAULT NULL,
    `latency_ms`               BIGINT        DEFAULT NULL,
    `request_bytes`            BIGINT        DEFAULT NULL,
    `response_bytes`           BIGINT        DEFAULT NULL,
    `request_payload_sha256`   CHAR(64)      DEFAULT NULL,
    `response_payload_sha256`  CHAR(64)      DEFAULT NULL,
    `request_summary_json`     TEXT          DEFAULT NULL COMMENT '固定 allowlist 摘要；无 header/body/secret',
    `response_summary_json`    TEXT          DEFAULT NULL,
    `remote_address_sha256`    CHAR(64)      DEFAULT NULL COMMENT '加盐哈希；不保存原始 IP',
    `error_summary`            VARCHAR(1000) DEFAULT NULL,
    `trace_id`                 VARCHAR(128)  DEFAULT NULL,
    `created_at`               DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_transport_request` (`request_id`),
    KEY `idx_a2a_transport_direction_time` (`direction`, `created_at`),
    KEY `idx_a2a_transport_publication` (`publication_id`, `success`, `created_at`),
    KEY `idx_a2a_transport_remote` (`remote_agent_id`, `success`, `created_at`),
    KEY `idx_a2a_transport_task` (`task_ref_id`, `created_at`),
    KEY `idx_a2a_transport_trace` (`trace_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 无原始请求/响应正文的传输审计摘要';

CREATE TABLE IF NOT EXISTS `control_a2a_rate_limit_window` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT,
    `principal_id`          BIGINT       NOT NULL,
    `publication_id`        BIGINT       NOT NULL,
    `window_start`          DATETIME     NOT NULL COMMENT 'UTC minute window',
    `request_count`         INT          NOT NULL DEFAULT 0,
    `updated_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_rate_window` (`principal_id`, `publication_id`, `window_start`),
    KEY `idx_a2a_rate_window_cleanup` (`window_start`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 多实例共享的 Principal 每分钟限流窗口';

CREATE TABLE IF NOT EXISTS `control_a2a_conformance_run` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `run_id`                VARCHAR(128)  NOT NULL,
    `target_type`           VARCHAR(32)   NOT NULL COMMENT 'PUBLICATION_REVISION / REMOTE_REVISION',
    `target_id`             BIGINT        NOT NULL,
    `protocol_version`      VARCHAR(16)   NOT NULL DEFAULT '1.0',
    `transport`             VARCHAR(32)   NOT NULL DEFAULT 'HTTP+JSON',
    `suite_name`            VARCHAR(64)   NOT NULL DEFAULT 'A2A_TCK',
    `suite_version`         VARCHAR(64)   DEFAULT NULL,
    `requirement_level`     VARCHAR(16)   NOT NULL DEFAULT 'MUST',
    `status`                VARCHAR(24)   NOT NULL DEFAULT 'QUEUED' COMMENT 'QUEUED / RUNNING / PASSED / FAILED / ERROR',
    `passed_count`          INT           NOT NULL DEFAULT 0,
    `failed_count`          INT           NOT NULL DEFAULT 0,
    `skipped_count`         INT           NOT NULL DEFAULT 0,
    `report_ref`            VARCHAR(1000) DEFAULT NULL,
    `report_sha256`         CHAR(64)      DEFAULT NULL,
    `safe_summary_json`     MEDIUMTEXT    DEFAULT NULL,
    `triggered_by`          VARCHAR(64)   DEFAULT NULL,
    `started_at`            DATETIME      DEFAULT NULL,
    `completed_at`          DATETIME      DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_conformance_run_id` (`run_id`),
    KEY `idx_a2a_conformance_target` (`target_type`, `target_id`, `created_at`),
    KEY `idx_a2a_conformance_status` (`status`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub 官方 TCK 与发布预检证据';

CREATE TABLE IF NOT EXISTS `control_a2a_outbox` (
    `id`                    BIGINT        NOT NULL AUTO_INCREMENT,
    `event_id`              VARCHAR(128)  NOT NULL,
    `aggregate_type`        VARCHAR(48)   NOT NULL,
    `aggregate_id`          VARCHAR(128)  NOT NULL,
    `event_type`            VARCHAR(64)   NOT NULL,
    `resource_ref_json`     TEXT          NOT NULL COMMENT '只保存资源引用和安全摘要，不复制正文/凭据',
    `status`                VARCHAR(24)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / CLAIMED / DELIVERED / RETRY / DEAD',
    `attempt_count`         INT           NOT NULL DEFAULT 0,
    `next_attempt_at`       DATETIME      NOT NULL,
    `lease_owner`           VARCHAR(128)  DEFAULT NULL,
    `lease_until`           DATETIME      DEFAULT NULL,
    `last_error_code`       VARCHAR(96)   DEFAULT NULL,
    `last_error_summary`    VARCHAR(1000) DEFAULT NULL,
    `created_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `delivered_at`          DATETIME      DEFAULT NULL,
    `updated_at`            DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_a2a_outbox_event` (`event_id`),
    KEY `idx_a2a_outbox_claim` (`status`, `next_attempt_at`, `lease_until`),
    KEY `idx_a2a_outbox_aggregate` (`aggregate_type`, `aggregate_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='A2A Hub durable outbox；payload 不含 Message/Artifact 明文';

CREATE TABLE IF NOT EXISTS `runtime_agent_remote_agent_binding` (
    `id`                         BIGINT        NOT NULL AUTO_INCREMENT,
    `agent_id`                   VARCHAR(32)   NOT NULL COMMENT 'runtime_agent.id',
    `agent_config_version_id`    BIGINT        NOT NULL COMMENT '随 Agent 配置版本不可变',
    `principal_id`               BIGINT        NOT NULL COMMENT 'Control LOCAL_AGENT Principal 逻辑引用；Runtime 不直读 Control 表',
    `remote_agent_id`            BIGINT        NOT NULL COMMENT 'Control 目录逻辑引用；Runtime 不直读 Control 表',
    `remote_agent_revision_id`   BIGINT        NOT NULL COMMENT '固定不可变远程修订，不允许 latest',
    `remote_agent_key_snapshot`  VARCHAR(128)  NOT NULL,
    `tool_name`                  VARCHAR(128)  NOT NULL COMMENT '暴露给 Supervisor 的稳定 Tool 名',
    `description_snapshot`       VARCHAR(2000) NOT NULL,
    `allowed_skill_ids_json`     TEXT          DEFAULT NULL COMMENT 'A2A AgentSkill id allowlist',
    `input_modes_json`           TEXT          NOT NULL,
    `output_modes_json`          TEXT          NOT NULL,
    `risk_level`                 VARCHAR(24)   NOT NULL DEFAULT 'READ' COMMENT 'READ / WRITE / IRREVERSIBLE',
    `permission_key`             VARCHAR(160)  DEFAULT NULL,
    `timeout_ms`                 BIGINT        NOT NULL DEFAULT 60000,
    `priority`                   INT           NOT NULL DEFAULT 0,
    `enabled`                    TINYINT(1)    NOT NULL DEFAULT 1,
    `created_at`                 DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                 DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_agent_remote_revision` (`agent_config_version_id`, `remote_agent_revision_id`),
    UNIQUE KEY `uk_runtime_agent_remote_tool` (`agent_config_version_id`, `tool_name`),
    KEY `idx_runtime_agent_remote_binding` (`agent_id`, `agent_config_version_id`, `enabled`),
    KEY `idx_runtime_remote_agent_ref` (`remote_agent_id`, `remote_agent_revision_id`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime Agent 配置版本固定远程 Agent 修订的委派白名单';


-- ============================================================================
-- 七.f、Phase 4.0 接口图谱（ApiCallGraph 一期）
--   - capability_api_graph_node：接口/字段/DTO/模块节点
--   - capability_api_graph_edge：接口间引用关系（请求引用蓝 / 响应引用绿 / 数据模型紫虚线）
--   - capability_api_graph_layout：画布坐标
--   - 与历史 api_graph_phase4_0 补丁一致；详见
--     docs/接口图谱-设计与落地.md
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_api_graph_node` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`    BIGINT       NOT NULL,
    `kind`          VARCHAR(16)  NOT NULL                       COMMENT 'API / FIELD_IN / FIELD_OUT / DTO / MODULE',
    `ref_id`        BIGINT       DEFAULT NULL,
    `parent_id`     BIGINT       DEFAULT NULL,
    `label`         VARCHAR(255) NOT NULL,
    `type_name`     VARCHAR(255) DEFAULT NULL,
    `props_json`    TEXT         DEFAULT NULL,
    `created_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_node_identity` (`project_id`, `kind`, `ref_id`, `parent_id`, `label`),
    KEY `idx_project_kind` (`project_id`, `kind`),
    KEY `idx_parent`       (`parent_id`),
    KEY `idx_type`         (`project_id`, `type_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='接口图谱节点 (Phase 4.0)';

CREATE TABLE IF NOT EXISTS `capability_api_graph_edge` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`      BIGINT       NOT NULL,
    `source_node_id`  BIGINT       NOT NULL,
    `target_node_id`  BIGINT       NOT NULL,
    `kind`            VARCHAR(16)  NOT NULL                     COMMENT 'REQUEST_REF / RESPONSE_REF / MODEL_REF / BELONGS_TO',
    `source`          VARCHAR(8)   NOT NULL DEFAULT 'manual'    COMMENT 'auto / manual',
    `confidence`      DOUBLE       DEFAULT NULL,
    `status`          VARCHAR(16)  NOT NULL DEFAULT 'CONFIRMED' COMMENT 'CANDIDATE / CONFIRMED / REJECTED',
    `infer_strategy`  VARCHAR(32)  DEFAULT NULL                 COMMENT 'schema_match / dto_match / trace_value_match / llm_assisted',
    `confirmed_by`    VARCHAR(64)  DEFAULT NULL,
    `confirmed_at`    DATETIME     DEFAULT NULL,
    `reject_reason`   VARCHAR(512) DEFAULT NULL,
    `evidence_json`   TEXT         DEFAULT NULL,
    `note`            VARCHAR(512) DEFAULT NULL,
    `enabled`         TINYINT(1)   NOT NULL DEFAULT 1,
    `created_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_edge_identity` (`project_id`, `kind`, `source_node_id`, `target_node_id`, `source`),
    KEY `idx_project_kind` (`project_id`, `kind`),
    KEY `idx_project_status` (`project_id`, `status`),
    KEY `idx_source_node`  (`source_node_id`),
    KEY `idx_target_node`  (`target_node_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='接口图谱边 (Phase 4.0)';

CALL add_col_if_absent('capability_api_graph_edge', 'status', 'VARCHAR(16) NOT NULL DEFAULT ''CONFIRMED'' COMMENT ''CANDIDATE / CONFIRMED / REJECTED'' AFTER `confidence`');
CALL add_col_if_absent('capability_api_graph_edge', 'infer_strategy', 'VARCHAR(32) DEFAULT NULL COMMENT ''schema_match / dto_match / trace_value_match / llm_assisted'' AFTER `status`');
CALL add_col_if_absent('capability_api_graph_edge', 'confirmed_by', 'VARCHAR(64) DEFAULT NULL AFTER `infer_strategy`');
CALL add_col_if_absent('capability_api_graph_edge', 'confirmed_at', 'DATETIME DEFAULT NULL AFTER `confirmed_by`');
CALL add_col_if_absent('capability_api_graph_edge', 'reject_reason', 'VARCHAR(512) DEFAULT NULL AFTER `confirmed_at`');
CALL add_idx_if_absent('capability_api_graph_edge', 'idx_project_status', '`project_id`, `status`');

CREATE TABLE IF NOT EXISTS `capability_api_graph_layout` (
    `id`         BIGINT     NOT NULL AUTO_INCREMENT,
    `project_id` BIGINT     NOT NULL,
    `node_id`    BIGINT     NOT NULL,
    `x`          DOUBLE     NOT NULL DEFAULT 0,
    `y`          DOUBLE     NOT NULL DEFAULT 0,
    `ext_json`   TEXT       DEFAULT NULL,
    `updated_at` DATETIME   DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project_node` (`project_id`, `node_id`),
    KEY `idx_project` (`project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='接口图谱画布布局 (Phase 4.0)';


-- ============================================================================
-- 七.g、Phase 4.2 生产护栏：统一治理决策日志
-- ============================================================================

CREATE TABLE IF NOT EXISTS `runtime_guard_decision_log` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `trace_id`       VARCHAR(64)  DEFAULT NULL                 COMMENT '关联 traceId，可为空',
    `decision_type`  VARCHAR(32)  NOT NULL                     COMMENT 'RATE_LIMIT / BREAKER / ACL / SIDE_EFFECT / PREFLIGHT',
    `target_kind`    VARCHAR(32)  NOT NULL                     COMMENT 'AGENT / TOOL / MCP_CLIENT / A2A_ENDPOINT / PROJECT',
    `target_name`    VARCHAR(255) NOT NULL                     COMMENT '目标名称或 key',
    `decision`       VARCHAR(16)  NOT NULL                     COMMENT 'ALLOW / DENY / WARN / SKIP / DRY_RUN',
    `reason`         VARCHAR(512) DEFAULT NULL                 COMMENT '决策原因',
    `metadata_json`  TEXT         DEFAULT NULL                 COMMENT '扩展上下文 JSON',
    `created_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_trace` (`trace_id`),
    KEY `idx_type_target` (`decision_type`, `target_kind`, `target_name`),
    KEY `idx_decision_time` (`decision`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产护栏决策日志 (Phase 4.2)';


-- ============================================================================
-- 七.i、Phase P4 AI 注册中心：项目隔离、实例心跳、能力同步日志
-- ============================================================================

CALL add_col_if_absent('capability_scan_project', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''稳定项目编码'' AFTER `name`');
CALL add_col_if_absent('capability_scan_project', 'project_kind', 'VARCHAR(24) NOT NULL DEFAULT ''SCAN'' COMMENT ''SCAN / REGISTERED / HYBRID'' AFTER `project_code`');
CALL add_col_if_absent('capability_scan_project', 'environment', 'VARCHAR(32) NOT NULL DEFAULT ''default'' COMMENT ''项目环境'' AFTER `project_kind`');
CALL add_col_if_absent('capability_scan_project', 'owner', 'VARCHAR(128) DEFAULT NULL COMMENT ''负责人或团队'' AFTER `environment`');
CALL add_col_if_absent('capability_scan_project', 'visibility', 'VARCHAR(24) NOT NULL DEFAULT ''PRIVATE'' COMMENT ''PRIVATE / PROJECT / SHARED / PUBLIC'' AFTER `owner`');
CALL add_idx_if_absent('capability_scan_project', 'idx_scan_project_code', '`project_code`');
CALL add_idx_if_absent('capability_scan_project', 'idx_scan_project_env', '`environment`, `status`');

CALL add_col_if_absent('capability_tool_definition', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''冗余项目编码'' AFTER `project_id`');
CALL add_col_if_absent('capability_tool_definition', 'qualified_name', 'VARCHAR(256) DEFAULT NULL COMMENT ''projectCode:name 稳定能力全名'' AFTER `project_code`');
CALL add_idx_if_absent('capability_tool_definition', 'idx_tool_project_enabled', '`project_id`, `enabled`');
CALL add_idx_if_absent('capability_tool_definition', 'idx_tool_project_code', '`project_code`');
CALL add_idx_if_absent('capability_tool_definition', 'idx_tool_qualified_name', '`qualified_name`');

CALL add_col_if_absent('control_tool_acl', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''为空表示全局规则'' AFTER `role_code`');
CALL add_col_if_absent('control_tool_acl', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''项目编码'' AFTER `project_id`');
CALL add_idx_if_absent('control_tool_acl', 'idx_acl_project_role', '`project_id`, `role_code`, `enabled`');

CALL add_col_if_absent('knowledge_base', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `id`');
CALL add_col_if_absent('knowledge_base', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('knowledge_base', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('knowledge_base', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_idx_if_absent('knowledge_base', 'idx_kb_project', '`project_id`, `status`');

CALL add_col_if_absent('knowledge_business_index', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `id`');
CALL add_col_if_absent('knowledge_business_index', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('knowledge_business_index', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('knowledge_business_index', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_col_if_absent('knowledge_business_index', 'agent_memory_enabled', 'TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否允许生成非权威 Agent 业务记忆引用'' AFTER `remark`');
CALL add_col_if_absent('knowledge_business_index', 'resolver_capability_key', 'VARCHAR(128) DEFAULT NULL COMMENT ''按当前业务身份回源并重新鉴权的 Capability key'' AFTER `agent_memory_enabled`');
CALL add_idx_if_absent('knowledge_business_index', 'idx_biz_index_project', '`project_id`, `status`');
CALL add_idx_if_absent('knowledge_business_index', 'idx_biz_agent_memory', '`tenant_id`, `project_code`, `agent_memory_enabled`, `status`');
CALL add_col_if_absent('knowledge_business_index_record', 'source_version', 'VARCHAR(128) DEFAULT NULL COMMENT ''业务源版本；Agent 回源时用于新鲜度校验'' AFTER `metadata_json`');
CALL add_col_if_absent('knowledge_business_index_record', 'source_updated_at', 'DATETIME DEFAULT NULL COMMENT ''业务源更新时间；不替代 source_version'' AFTER `source_version`');
CALL add_idx_if_absent('knowledge_business_index_record', 'idx_biz_source_version', '`index_code`, `biz_id`, `source_version`');

CALL add_col_if_absent('runtime_tool_call_log', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `intent_type`');
CALL add_col_if_absent('runtime_tool_call_log', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('runtime_tool_call_log', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('runtime_tool_call_log', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_col_if_absent('runtime_tool_call_log', 'app_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''嵌入式业务应用 ID，等同 project_code'' AFTER `tenant_id`');
CALL add_col_if_absent('runtime_tool_call_log', 'external_user_id', 'VARCHAR(128) DEFAULT NULL COMMENT ''业务系统内用户 ID'' AFTER `app_id`');
CALL add_col_if_absent('runtime_tool_call_log', 'global_user_id', 'VARCHAR(128) DEFAULT NULL COMMENT ''跨系统稳定用户 ID'' AFTER `external_user_id`');
CALL add_col_if_absent('runtime_tool_call_log', 'page_instance_id', 'VARCHAR(128) DEFAULT NULL COMMENT ''业务前端页面实例 ID'' AFTER `global_user_id`');
CALL add_col_if_absent('runtime_tool_call_log', 'origin', 'VARCHAR(512) DEFAULT NULL COMMENT ''业务前端 origin'' AFTER `page_instance_id`');
CALL add_idx_if_absent('runtime_tool_call_log', 'idx_tool_log_project_trace', '`project_code`, `trace_id`');
CALL add_idx_if_absent('runtime_tool_call_log', 'idx_tool_log_embed_session', '`app_id`, `external_user_id`, `session_id`');

CALL add_col_if_absent('runtime_guard_decision_log', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `trace_id`');
CALL add_col_if_absent('runtime_guard_decision_log', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('runtime_guard_decision_log', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('runtime_guard_decision_log', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_idx_if_absent('runtime_guard_decision_log', 'idx_guard_project_trace', '`project_code`, `trace_id`');

CALL add_col_if_absent('control_mcp_client', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `api_key_prefix`');
CALL add_col_if_absent('control_mcp_client', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('control_mcp_client', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('control_mcp_client', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_idx_if_absent('control_mcp_client', 'idx_mcp_client_project', '`project_id`, `enabled`');

CALL add_col_if_absent('control_mcp_call_log', 'project_id', 'BIGINT DEFAULT NULL COMMENT ''所属项目'' AFTER `tool_name`');
CALL add_col_if_absent('control_mcp_call_log', 'project_code', 'VARCHAR(96) DEFAULT NULL COMMENT ''所属项目编码'' AFTER `project_id`');
CALL add_col_if_absent('control_mcp_call_log', 'environment', 'VARCHAR(32) DEFAULT NULL COMMENT ''环境'' AFTER `project_code`');
CALL add_col_if_absent('control_mcp_call_log', 'tenant_id', 'VARCHAR(96) DEFAULT NULL COMMENT ''租户'' AFTER `environment`');
CALL add_idx_if_absent('control_mcp_call_log', 'idx_mcp_log_project_trace', '`project_code`, `trace_id`');

CREATE TABLE IF NOT EXISTS `capability_project_instance` (
    `id`                BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`        BIGINT       NOT NULL,
    `project_code`      VARCHAR(96)  NOT NULL,
    `instance_id`       VARCHAR(128) NOT NULL,
    `base_url`          VARCHAR(256) DEFAULT NULL,
    `host`              VARCHAR(128) DEFAULT NULL,
    `port`              INT          DEFAULT NULL,
    `app_version`       VARCHAR(64)  DEFAULT NULL,
    `sdk_version`       VARCHAR(64)  DEFAULT NULL,
    `status`            VARCHAR(24)  NOT NULL DEFAULT 'ONLINE',
    `metadata_json`     JSON         DEFAULT NULL,
    `last_heartbeat_at` DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `created_at`        DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`        DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project_instance` (`project_code`, `instance_id`),
    KEY `idx_project_status` (`project_id`, `status`),
    KEY `idx_heartbeat` (`last_heartbeat_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI 注册中心业务系统实例';

CREATE TABLE IF NOT EXISTS `capability_sync_log` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`      BIGINT       NOT NULL,
    `project_code`    VARCHAR(96)  NOT NULL,
    `sync_id`         VARCHAR(64)  NOT NULL,
    `source`          VARCHAR(32)  NOT NULL DEFAULT 'SDK',
    `status`          VARCHAR(24)  NOT NULL DEFAULT 'RECEIVED',
    `summary_json`    JSON         DEFAULT NULL,
    `error_message`   TEXT         DEFAULT NULL,
    `created_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_sync_id` (`sync_id`),
    KEY `idx_sync_project` (`project_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力同步日志';

CREATE TABLE IF NOT EXISTS `capability_snapshot` (
    `id`           BIGINT      NOT NULL AUTO_INCREMENT,
    `project_id`   BIGINT      NOT NULL,
    `project_code` VARCHAR(96) NOT NULL,
    `sync_id`      VARCHAR(64) NOT NULL,
    `source`       VARCHAR(32) NOT NULL DEFAULT 'SDK',
    `status`       VARCHAR(24) NOT NULL DEFAULT 'PENDING',
    `payload_json` JSON        DEFAULT NULL,
    `received`     INT         NOT NULL DEFAULT 0,
    `added`        INT         NOT NULL DEFAULT 0,
    `changed`      INT         NOT NULL DEFAULT 0,
    `unchanged`    INT         NOT NULL DEFAULT 0,
    `deleted`      INT         NOT NULL DEFAULT 0,
    `created_at`   DATETIME    DEFAULT CURRENT_TIMESTAMP,
    `updated_at`   DATETIME    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_snapshot_sync` (`sync_id`),
    KEY `idx_snapshot_project` (`project_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力同步快照';

CREATE TABLE IF NOT EXISTS `capability_diff_item` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `snapshot_id`      BIGINT       NOT NULL,
    `sync_id`          VARCHAR(64)  NOT NULL,
    `project_id`       BIGINT       NOT NULL,
    `project_code`     VARCHAR(96)  NOT NULL,
    `qualified_name`   VARCHAR(256) DEFAULT NULL,
    `name`             VARCHAR(192) NOT NULL,
    `storage_name`     VARCHAR(256) NOT NULL,
    `change_type`      VARCHAR(24)  NOT NULL,
    `existing_tool_id` BIGINT       DEFAULT NULL,
    `field_diff_json`  JSON         DEFAULT NULL,
    `impact_json`      JSON         DEFAULT NULL,
    `before_state_json` JSON        DEFAULT NULL COMMENT '评审应用前的扫描目录与全局 Tool 状态，用于真实回滚',
    `review_status`    VARCHAR(24)  NOT NULL DEFAULT 'PENDING',
    `review_note`      VARCHAR(512) DEFAULT NULL,
    `created_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_diff_snapshot` (`snapshot_id`, `review_status`),
    KEY `idx_diff_qualified` (`qualified_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力同步字段级差异项';

CREATE TABLE IF NOT EXISTS `capability_apply_record` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `snapshot_id`    BIGINT       NOT NULL,
    `diff_item_id`   BIGINT       DEFAULT NULL,
    `sync_id`        VARCHAR(64)  NOT NULL,
    `project_id`     BIGINT       NOT NULL,
    `project_code`   VARCHAR(96)  NOT NULL,
    `qualified_name` VARCHAR(256) DEFAULT NULL,
    `action`         VARCHAR(32)  NOT NULL,
    `status`         VARCHAR(24)  NOT NULL,
    `operator`       VARCHAR(128) DEFAULT NULL,
    `message`        VARCHAR(1024) DEFAULT NULL,
    `created_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_apply_snapshot` (`snapshot_id`, `created_at`),
    KEY `idx_apply_diff_item` (`diff_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力评审应用记录';

CREATE TABLE IF NOT EXISTS `capability_registry_project_credential` (
    `id`           BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`   BIGINT       NOT NULL,
    `project_code` VARCHAR(96)  NOT NULL,
    `app_key`      VARCHAR(128) NOT NULL,
    `app_secret`   VARCHAR(256) NOT NULL,
    `status`       VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `expires_at`   DATETIME     DEFAULT NULL,
    `allowed_origins_json`   TEXT         DEFAULT NULL COMMENT '允许嵌入 Chat 的业务前端 origin JSON 数组',
    `allowed_agent_ids_json` TEXT         DEFAULT NULL COMMENT '允许申请 embedToken 的 Agent ID / keySlug JSON 数组，空数组表示按项目归属校验',
    `token_ttl_seconds`      INT          DEFAULT 600  COMMENT 'embedToken 默认有效期（秒）',
    `created_at`   DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`   DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_registry_credential` (`project_code`, `app_key`),
    KEY `idx_registry_credential_project` (`project_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='注册中心项目接入凭证';

CREATE TABLE IF NOT EXISTS `capability_registry_enrollment_token` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `project_code`       VARCHAR(96)  NOT NULL,
    `token_digest`       CHAR(64)     NOT NULL COMMENT 'SHA-256 digest only; raw token is returned once',
    `created_by_user_id` BIGINT       DEFAULT NULL,
    `expires_at`         DATETIME     NOT NULL,
    `consumed_at`        DATETIME     DEFAULT NULL,
    `created_at`         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_capability_registry_enrollment_digest` (`token_digest`),
    KEY `idx_capability_registry_enrollment_project` (`project_code`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='一次性 SDK 注册 enrollment token，仅保存摘要';

CREATE TABLE IF NOT EXISTS `capability_internal_auth_nonce` (
    `nonce`       VARCHAR(128) NOT NULL,
    `caller`      VARCHAR(96)  NOT NULL,
    `created_at`  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`nonce`),
    KEY `idx_capability_internal_auth_nonce_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Capability 内部 HMAC 请求防重放 nonce';

CREATE TABLE IF NOT EXISTS `capability_registry_request_nonce` (
    `nonce_digest` CHAR(64)     NOT NULL COMMENT 'SHA-256(projectCode, appKey, nonce)',
    `project_code` VARCHAR(96)  NOT NULL,
    `app_key_hash` CHAR(64)     NOT NULL COMMENT 'SHA-256 appKey; raw appKey is not retained in replay history',
    `created_at`   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`nonce_digest`),
    KEY `idx_capability_registry_request_nonce_created` (`created_at`),
    KEY `idx_capability_registry_request_nonce_project` (`project_code`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目凭证业务写入请求防重放 nonce';

CALL add_col_if_absent('capability_registry_project_credential', 'allowed_origins_json',   'TEXT DEFAULT NULL COMMENT ''允许嵌入 Chat 的业务前端 origin JSON 数组'' AFTER `expires_at`');
CALL add_col_if_absent('capability_registry_project_credential', 'allowed_agent_ids_json', 'TEXT DEFAULT NULL COMMENT ''允许申请 embedToken 的 Agent ID / keySlug JSON 数组，空数组表示按项目归属校验'' AFTER `allowed_origins_json`');
CALL add_col_if_absent('capability_registry_project_credential', 'token_ttl_seconds',      'INT DEFAULT 600 COMMENT ''embedToken 默认有效期（秒）'' AFTER `allowed_agent_ids_json`');

CREATE TABLE IF NOT EXISTS `control_business_user` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `tenant_id`      VARCHAR(96)  NOT NULL DEFAULT 'default',
    `global_user_id` VARCHAR(128) NOT NULL,
    `display_name`   VARCHAR(128) DEFAULT NULL,
    `email`          VARCHAR(128) DEFAULT NULL,
    `mobile`         VARCHAR(64)  DEFAULT NULL,
    `status`         VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `source`         VARCHAR(32)  DEFAULT NULL,
    `last_seen_at`   DATETIME     DEFAULT NULL,
    `created_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_business_user_global` (`tenant_id`, `global_user_id`),
    KEY `idx_business_user_status` (`tenant_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ReachAI 可识别的业务终端用户主体';

CREATE TABLE IF NOT EXISTS `control_external_user_binding` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `tenant_id`          VARCHAR(96)  NOT NULL DEFAULT 'default',
    `business_user_id`   BIGINT       NOT NULL,
    `app_id`             VARCHAR(96)  NOT NULL,
    `external_user_id`   VARCHAR(128) NOT NULL,
    `external_user_name` VARCHAR(128) DEFAULT NULL,
    `dept_id`            VARCHAR(128) DEFAULT NULL,
    `dept_name`          VARCHAR(128) DEFAULT NULL,
    `status`             VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `last_seen_at`       DATETIME     DEFAULT NULL,
    `created_at`         DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`         DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_binding` (`tenant_id`, `app_id`, `external_user_id`),
    KEY `idx_external_binding_user` (`business_user_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务系统外部用户身份绑定';

CREATE TABLE IF NOT EXISTS `control_external_user_role_binding` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `tenant_id`        VARCHAR(96)  NOT NULL DEFAULT 'default',
    `business_user_id` BIGINT       NOT NULL,
    `app_id`           VARCHAR(96)  NOT NULL,
    `external_user_id` VARCHAR(128) NOT NULL,
    `role_code`        VARCHAR(96)  NOT NULL,
    `role_name`        VARCHAR(128) DEFAULT NULL,
    `source`           VARCHAR(32)  DEFAULT NULL,
    `status`           VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_role` (`tenant_id`, `app_id`, `external_user_id`, `role_code`),
    KEY `idx_external_role_user` (`business_user_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务系统同步的用户角色绑定';

CREATE TABLE IF NOT EXISTS `control_embed_session` (
    `id`                  BIGINT       NOT NULL AUTO_INCREMENT,
    `session_id`          VARCHAR(96)  NOT NULL,
    `tenant_id`           VARCHAR(96)  NOT NULL DEFAULT 'default',
    `app_id`              VARCHAR(96)  NOT NULL,
    `project_code`        VARCHAR(96)  NOT NULL,
    `agent_id`            VARCHAR(128) NOT NULL,
    `external_user_id`    VARCHAR(128) NOT NULL,
    `global_user_id`      VARCHAR(128) DEFAULT NULL,
    `page_key`            VARCHAR(160) DEFAULT NULL,
    `page_instance_id`    VARCHAR(128) NOT NULL,
    `route`               VARCHAR(512) DEFAULT NULL,
    `origin`              VARCHAR(512) NOT NULL,
    `sdk_version`         VARCHAR(64)  DEFAULT NULL,
    `bridge_actions_json` TEXT         DEFAULT NULL,
    `status`              VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `expires_at`          DATETIME     NOT NULL,
    `created_at`          DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`          DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_embed_session` (`session_id`),
    KEY `idx_embed_session_identity` (`tenant_id`, `app_id`, `external_user_id`, `created_at`),
    KEY `idx_embed_session_page_key` (`project_code`, `agent_id`, `page_key`, `status`, `created_at`),
    KEY `idx_embed_session_page` (`page_instance_id`, `status`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='嵌入式对话会话与页面实例绑定';

CALL add_col_if_absent('control_embed_session', 'page_key', 'VARCHAR(160) DEFAULT NULL COMMENT ''业务前端页面稳定标识'' AFTER `global_user_id`');
CALL add_col_if_absent('control_embed_session', 'sdk_version', 'VARCHAR(64) DEFAULT NULL COMMENT ''Chat Embed SDK version'' AFTER `origin`');
CALL add_idx_if_absent('control_embed_session', 'idx_embed_session_page_key', '`project_code`, `agent_id`, `page_key`, `status`, `created_at`');

CREATE TABLE IF NOT EXISTS `control_embed_token_revocation` (
    `id`         BIGINT       NOT NULL AUTO_INCREMENT,
    `jti`        VARCHAR(128) NOT NULL,
    `reason`     VARCHAR(256) DEFAULT NULL,
    `expires_at` DATETIME     DEFAULT NULL,
    `revoked_at` DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_embed_token_revocation_jti` (`jti`),
    KEY `idx_embed_token_revocation_exp` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='嵌入式对话 token 撤销表';

CREATE TABLE IF NOT EXISTS `control_page_action_event` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT,
    `request_id`              VARCHAR(96)  NOT NULL,
    `session_id`              VARCHAR(96)  NOT NULL,
    `tenant_id`               VARCHAR(96)  NOT NULL DEFAULT 'default',
    `app_id`                  VARCHAR(96)  NOT NULL,
    `agent_id`                VARCHAR(128) NOT NULL,
    `command_type`            VARCHAR(32)  NOT NULL DEFAULT 'PAGE_ACTION',
    `parent_request_id`       VARCHAR(96)  DEFAULT NULL,
    `node_id`                 VARCHAR(128) DEFAULT NULL,
    `action_key`              VARCHAR(160) DEFAULT NULL,
    `title`                   VARCHAR(256) DEFAULT NULL,
    `args_json`               TEXT         DEFAULT NULL,
    `target_page_instance_id` VARCHAR(128) DEFAULT NULL,
    `target_page_key`         VARCHAR(160) DEFAULT NULL,
    `target_route`            VARCHAR(512) DEFAULT NULL,
    `confirm_required`        TINYINT(1)   DEFAULT 0,
    `status`                  VARCHAR(32)  NOT NULL DEFAULT 'REQUESTED',
    `result_json`             TEXT         DEFAULT NULL,
    `error_message`           VARCHAR(1024) DEFAULT NULL,
    `requested_at`            DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `completed_at`            DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_page_action_request` (`session_id`, `request_id`),
    KEY `idx_page_action_session` (`session_id`, `status`, `requested_at`),
    KEY `idx_page_action_app_agent` (`tenant_id`, `app_id`, `agent_id`, `requested_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='嵌入式页面动作请求和执行结果';

CALL add_col_if_absent('control_page_action_event', 'command_type', 'VARCHAR(32) NOT NULL DEFAULT ''PAGE_ACTION'' AFTER `agent_id`');
CALL add_col_if_absent('control_page_action_event', 'parent_request_id', 'VARCHAR(96) DEFAULT NULL AFTER `command_type`');
CALL add_col_if_absent('control_page_action_event', 'target_page_key', 'VARCHAR(160) DEFAULT NULL AFTER `target_page_instance_id`');
CALL add_col_if_absent('control_page_action_event', 'target_route', 'VARCHAR(512) DEFAULT NULL AFTER `target_page_key`');

CREATE TABLE IF NOT EXISTS `control_project_page` (
    `id`                 BIGINT        NOT NULL AUTO_INCREMENT,
    `project_id`         BIGINT        NOT NULL,
    `project_code`       VARCHAR(96)   NOT NULL,
    `page_key`           VARCHAR(160)  NOT NULL,
    `module_key`         VARCHAR(128)  DEFAULT NULL,
    `module_name`        VARCHAR(160)  DEFAULT NULL,
    `name`               VARCHAR(160)  NOT NULL,
    `description`        VARCHAR(1000) DEFAULT NULL,
    `route_pattern`      VARCHAR(512)  DEFAULT NULL,
    `business_page_url`  VARCHAR(1024) DEFAULT NULL COMMENT '浏览器可直接打开的绝对 HTTP(S) 业务页面地址',
    `component_path`     VARCHAR(768)  DEFAULT NULL,
    `source_type`        VARCHAR(32)   NOT NULL COMMENT 'AI_SCAN / MANUAL / SDK',
    `lifecycle_status`   VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / ARCHIVED',
    `last_discovered_at` DATETIME      DEFAULT NULL,
    `last_verified_at`   DATETIME      DEFAULT NULL,
    `created_at`         DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`         DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_project_page` (`project_code`, `page_key`),
    KEY `idx_control_project_page_project` (`project_id`, `lifecycle_status`, `updated_at`),
    KEY `idx_control_project_page_module` (`project_code`, `module_key`, `lifecycle_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务页面工作台稳定页面定义';

CREATE TABLE IF NOT EXISTS `control_project_page_resource` (
    `id`              BIGINT        NOT NULL AUTO_INCREMENT,
    `page_id`         BIGINT        NOT NULL,
    `project_code`    VARCHAR(96)   NOT NULL,
    `resource_type`   VARCHAR(32)   NOT NULL COMMENT 'ROUTE / COMPONENT / API / CONFIG / PERMISSION / STORE / STYLE / TEST',
    `resource_key`    VARCHAR(512)  NOT NULL,
    `display_name`    VARCHAR(256)  DEFAULT NULL,
    `location`        VARCHAR(1000) DEFAULT NULL,
    `http_method`     VARCHAR(16)   DEFAULT NULL,
    `access_mode`     VARCHAR(16)   NOT NULL DEFAULT 'READ_ONLY' COMMENT 'READ_ONLY / READ_WRITE',
    `metadata_json`   TEXT          DEFAULT NULL,
    `created_at`      DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_page_resource` (`page_id`, `resource_type`, `resource_key`(191)),
    KEY `idx_control_page_resource_page` (`page_id`, `resource_type`),
    KEY `idx_control_page_resource_project` (`project_code`, `resource_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务页面关联路由组件API配置权限与测试资源';

CREATE TABLE IF NOT EXISTS `control_page_action` (
    `id`                     BIGINT        NOT NULL AUTO_INCREMENT,
    `page_id`                BIGINT        NOT NULL,
    `project_id`             BIGINT        NOT NULL,
    `project_code`           VARCHAR(96)   NOT NULL,
    `page_key`               VARCHAR(160)  NOT NULL,
    `action_key`             VARCHAR(160)  NOT NULL,
    `title`                  VARCHAR(160)  NOT NULL,
    `description`            VARCHAR(1000) DEFAULT NULL,
    `action_type`            VARCHAR(32)   NOT NULL DEFAULT 'PAGE_ACTION',
    `risk_level`             VARCHAR(24)   NOT NULL DEFAULT 'READ',
    `confirm_required`       TINYINT(1)    NOT NULL DEFAULT 0,
    `permission_key`         VARCHAR(160)  DEFAULT NULL,
    `input_schema_json`      TEXT          DEFAULT NULL,
    `output_schema_json`     TEXT          DEFAULT NULL,
    `sample_args_json`       TEXT          DEFAULT NULL,
    `allowed_agent_ids_json` TEXT          DEFAULT NULL,
    `implementation_ref`     VARCHAR(1000) DEFAULT NULL,
    `source_type`            VARCHAR(32)   NOT NULL,
    `status`                 VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE',
    `metadata_json`          TEXT          DEFAULT NULL,
    `last_verified_at`       DATETIME      DEFAULT NULL,
    `created_at`             DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`             DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_page_action` (`page_id`, `action_key`),
    KEY `idx_control_page_action_lookup` (`project_code`, `page_key`, `action_key`, `status`),
    KEY `idx_control_page_action_project` (`project_id`, `status`, `updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务页面可执行动作契约';

CREATE TABLE IF NOT EXISTS `control_page_analysis_finding` (
    `id`                       BIGINT        NOT NULL AUTO_INCREMENT,
    `finding_key`              VARCHAR(96)   NOT NULL,
    `project_id`               BIGINT        NOT NULL,
    `project_code`             VARCHAR(96)   NOT NULL,
    `page_id`                  BIGINT        NOT NULL,
    `page_key`                 VARCHAR(160)  NOT NULL,
    `source_task_id`           VARCHAR(40)   NOT NULL,
    `category`                 VARCHAR(64)   DEFAULT NULL,
    `title`                    VARCHAR(256)  NOT NULL,
    `confirmed_fact`           TEXT          NOT NULL,
    `technical_inference`      TEXT          DEFAULT NULL,
    `open_question`            TEXT          DEFAULT NULL,
    `use_case`                 TEXT          DEFAULT NULL,
    `business_confirm_status`  VARCHAR(32)   NOT NULL DEFAULT 'PENDING',
    `technical_feasibility`    VARCHAR(32)   NOT NULL DEFAULT 'UNKNOWN',
    `operation_risk`           VARCHAR(32)   NOT NULL DEFAULT 'UNKNOWN',
    `information_completeness` VARCHAR(32)   NOT NULL DEFAULT 'PARTIAL',
    `read_scope`               TEXT          DEFAULT NULL,
    `write_scope`              TEXT          DEFAULT NULL,
    `implementation_reference` TEXT          DEFAULT NULL,
    `acceptance_criteria`      TEXT          DEFAULT NULL,
    `related_pages_json`       TEXT          DEFAULT NULL,
    `evidence_json`            MEDIUMTEXT    DEFAULT NULL,
    `code_references_json`     MEDIUMTEXT    DEFAULT NULL,
    `status`                   VARCHAR(24)   NOT NULL DEFAULT 'UNREAD' COMMENT 'UNREAD / KEPT / IGNORED',
    `reviewed_by`              VARCHAR(96)   DEFAULT NULL,
    `reviewed_at`              DATETIME      DEFAULT NULL,
    `created_at`               DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`               DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_page_finding` (`page_id`, `finding_key`),
    KEY `idx_control_page_finding_filter` (`project_code`, `status`, `updated_at`),
    KEY `idx_control_page_finding_page` (`page_id`, `status`, `updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单页面AI Coding只读分析结果';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task` (
    `task_id`                 VARCHAR(40)   NOT NULL,
    `project_id`              BIGINT        NOT NULL,
    `project_code`            VARCHAR(96)   NOT NULL,
    `capability_key`          VARCHAR(64)   NOT NULL COMMENT 'PROJECT_ONBOARDING / BUSINESS_PAGE_WORKBENCH / ...',
    `task_kind`               VARCHAR(64)   NOT NULL COMMENT 'Provider-owned task kind, for example PROJECT_ONBOARDING / PAGE_MAP_SCAN',
    `protocol_version`        VARCHAR(16)   NOT NULL DEFAULT 'v1',
    `executor_provider`       VARCHAR(24)   NOT NULL COMMENT 'CODEX / CURSOR / TRAE / CLAUDE_CODE',
    `execution_mode`          VARCHAR(24)   NOT NULL DEFAULT 'EXTERNAL_CLIENT' COMMENT 'EXTERNAL_CLIENT / MANAGED_SANDBOX',
    `managed_execution_id`    VARCHAR(64)   DEFAULT NULL COMMENT 'Runtime-owned Managed Execution id; immutable after binding',
    `sandbox_profile`         VARCHAR(48)   DEFAULT NULL COMMENT 'ANALYZE_READONLY / WORKSPACE_PATCH for managed tasks',
    `managed_execution_status` VARCHAR(32)  DEFAULT NULL COMMENT 'Last Runtime Managed Execution status projection',
    `managed_pending_interaction_id` VARCHAR(64) DEFAULT NULL COMMENT 'Current Runtime approval interaction, if any',
    `title`                   VARCHAR(256)  NOT NULL,
    `objective`               TEXT          NOT NULL,
    `access_mode`             VARCHAR(24)   NOT NULL COMMENT 'READ_ONLY / READ_WRITE',
    `execution_status`        VARCHAR(32)   NOT NULL DEFAULT 'READY' COMMENT 'READY / RUNNING / WAITING_USER / RESULT_SUBMITTED / RESULT_APPLIED / ACCEPTANCE_READY / COMPLETED / FAILED / CANCELLED',
    `result_contract_key`     VARCHAR(128)  NOT NULL,
    `result_contract_version` VARCHAR(32)   NOT NULL,
    `context_snapshot_json`   MEDIUMTEXT    NOT NULL,
    `last_message`            VARCHAR(1000) DEFAULT NULL,
    `lock_version`            BIGINT        NOT NULL DEFAULT 0,
    `created_by`              VARCHAR(96)   DEFAULT NULL,
    `started_at`              DATETIME      DEFAULT NULL,
    `result_submitted_at`     DATETIME      DEFAULT NULL,
    `completed_at`            DATETIME      DEFAULT NULL,
    `created_at`              DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`              DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`task_id`),
    KEY `idx_control_ai_task_project` (`project_id`, `task_kind`, `updated_at`),
    KEY `idx_control_ai_task_status` (`project_code`, `execution_status`, `updated_at`),
    KEY `idx_control_ai_task_capability` (`capability_key`, `execution_status`, `updated_at`),
    UNIQUE KEY `uk_control_ai_task_managed_execution` (`managed_execution_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ReachAI通用AI Coding任务根对象';

CREATE TABLE IF NOT EXISTS `control_ai_coding_project_policy` (
    `project_id`                      BIGINT      NOT NULL COMMENT '逻辑关联 capability_scan_project.id，不建跨服务外键',
    `handoff_activation_ttl_hours`    INT         NOT NULL DEFAULT 72 COMMENT '新签发一次性交接码的有效小时数，1-168',
    `task_token_ttl_hours`            INT         NOT NULL DEFAULT 72 COMMENT '新激活任务Token的有效小时数，1-720',
    `updated_by`                      VARCHAR(96) DEFAULT NULL,
    `created_at`                      DATETIME    DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                      DATETIME    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目级AI Coding交接与任务凭据策略';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task_target` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `task_id`       VARCHAR(40)  NOT NULL,
    `target_type`   VARCHAR(40)  NOT NULL COMMENT 'PROJECT / PAGE / WORKFLOW / API / ...',
    `target_key`    VARCHAR(256) NOT NULL,
    `target_role`   VARCHAR(24)  NOT NULL COMMENT 'PRIMARY / RELATED',
    `access_mode`   VARCHAR(24)  NOT NULL COMMENT 'READ_ONLY / READ_WRITE',
    `snapshot_json` MEDIUMTEXT   DEFAULT NULL,
    `created_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_ai_task_target` (`task_id`, `target_type`, `target_key`, `target_role`),
    KEY `idx_control_ai_task_target_lookup` (`target_type`, `target_key`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Coding任务领域目标与访问范围';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task_handoff` (
    `handoff_id`                VARCHAR(40)   NOT NULL,
    `task_id`                   VARCHAR(40)   NOT NULL,
    `activation_code_hash`      CHAR(64)      NOT NULL,
    `activation_status`         VARCHAR(24)   NOT NULL DEFAULT 'ISSUED' COMMENT 'ISSUED / ACTIVATED / EXPIRED / REVOKED',
    `activation_attempts`       INT           NOT NULL DEFAULT 0,
    `last_activation_attempt_at` DATETIME     DEFAULT NULL,
    `activation_expires_at`     DATETIME      NOT NULL,
    `task_token_hash`           CHAR(64)      DEFAULT NULL,
    `token_expires_at`          DATETIME      DEFAULT NULL,
    `client_provider`           VARCHAR(24)   DEFAULT NULL,
    `client_session_ref`        VARCHAR(256)  DEFAULT NULL,
    `activated_at`              DATETIME      DEFAULT NULL,
    `last_seen_at`              DATETIME      DEFAULT NULL,
    `lease_expires_at`          DATETIME      DEFAULT NULL,
    `closed_at`                 DATETIME      DEFAULT NULL,
    `close_reason`              VARCHAR(128)  DEFAULT NULL,
    `issued_by`                 VARCHAR(96)   DEFAULT NULL,
    `created_at`                DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`handoff_id`),
    KEY `idx_control_ai_handoff_task` (`task_id`, `created_at`),
    KEY `idx_control_ai_handoff_activation` (`activation_status`, `activation_expires_at`),
    KEY `idx_control_ai_handoff_lease` (`lease_expires_at`, `closed_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Coding一次性交接与短期任务连接事实';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task_event` (
    `id`                     BIGINT        NOT NULL AUTO_INCREMENT,
    `task_id`                VARCHAR(40)   NOT NULL,
    `client_event_id`        VARCHAR(96)   DEFAULT NULL,
    `event_type`             VARCHAR(40)   NOT NULL,
    `execution_status_after` VARCHAR(32)   DEFAULT NULL,
    `message`                VARCHAR(1000) DEFAULT NULL,
    `payload_json`           MEDIUMTEXT    DEFAULT NULL,
    `actor_type`             VARCHAR(24)   NOT NULL,
    `actor_name`             VARCHAR(96)   DEFAULT NULL,
    `created_at`             DATETIME      DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_ai_task_event_client` (`task_id`, `client_event_id`),
    KEY `idx_control_ai_task_event_task` (`task_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Coding任务不可变事件';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task_question` (
    `question_id`   VARCHAR(96)  NOT NULL,
    `task_id`       VARCHAR(40)  NOT NULL,
    `title`         VARCHAR(256) NOT NULL,
    `body`          TEXT         NOT NULL,
    `options_json`  TEXT         DEFAULT NULL,
    `status`        VARCHAR(24)  NOT NULL DEFAULT 'OPEN',
    `answer`        TEXT         DEFAULT NULL,
    `asked_by`      VARCHAR(96)  DEFAULT NULL,
    `answered_by`   VARCHAR(96)  DEFAULT NULL,
    `asked_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `answered_at`   DATETIME     DEFAULT NULL,
    `updated_at`    DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`question_id`),
    KEY `idx_control_ai_task_question` (`task_id`, `status`, `asked_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Coding任务待回答问题';

CREATE TABLE IF NOT EXISTS `control_ai_coding_task_artifact` (
    `artifact_id`            BIGINT        NOT NULL AUTO_INCREMENT,
    `task_id`                VARCHAR(40)   NOT NULL,
    `artifact_key`           VARCHAR(128)  NOT NULL,
    `contract_key`           VARCHAR(128)  NOT NULL,
    `contract_version`       VARCHAR(32)   NOT NULL,
    `content_hash`           CHAR(64)      NOT NULL,
    `content_json`           LONGTEXT      NOT NULL,
    `processing_status`      VARCHAR(24)   NOT NULL DEFAULT 'RECEIVED' COMMENT 'RECEIVED / VALIDATED / APPLIED / REJECTED',
    `validation_message`     VARCHAR(1000) DEFAULT NULL,
    `application_result_json` LONGTEXT     DEFAULT NULL,
    `reported_by`            VARCHAR(96)   DEFAULT NULL,
    `created_at`             DATETIME      DEFAULT CURRENT_TIMESTAMP,
    `validated_at`           DATETIME      DEFAULT NULL,
    `applied_at`             DATETIME      DEFAULT NULL,
    PRIMARY KEY (`artifact_id`),
    UNIQUE KEY `uk_control_ai_task_artifact` (`task_id`, `artifact_key`),
    KEY `idx_control_ai_task_artifact_task` (`task_id`, `artifact_id`),
    KEY `idx_control_ai_task_artifact_contract` (`contract_key`, `contract_version`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Coding通用Artifact与领域应用结果';

CREATE TABLE IF NOT EXISTS `control_embed_chat_event` (
    `id`           BIGINT      NOT NULL AUTO_INCREMENT,
    `session_id`   VARCHAR(96) NOT NULL,
    `event_type`   VARCHAR(64) NOT NULL,
    `role`         VARCHAR(32) DEFAULT NULL,
    `content`      TEXT        DEFAULT NULL,
    `payload_json` LONGTEXT    DEFAULT NULL,
    `trace_id`     VARCHAR(96) DEFAULT NULL,
    `created_at`   DATETIME    DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_embed_chat_event_session` (`session_id`, `created_at`),
    KEY `idx_embed_chat_event_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='嵌入式对话事件审计';

CREATE TABLE IF NOT EXISTS `control_embed_renderer` (
    `id`                BIGINT       NOT NULL AUTO_INCREMENT,
    `renderer_key`      VARCHAR(160) NOT NULL,
    `app_id`            VARCHAR(96)  NOT NULL,
    `name`              VARCHAR(128) NOT NULL,
    `version`           VARCHAR(32)  NOT NULL DEFAULT '1.0',
    `input_schema_json` TEXT         DEFAULT NULL,
    `allowed_agent_ids_json` TEXT    DEFAULT NULL,
    `status`            VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`        DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`        DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_embed_renderer` (`app_id`, `renderer_key`, `version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='嵌入式对话自定义结构化渲染器注册';

-- ============================================================================
-- Context Governance Kernel v1（企业 Agent 上下文治理底座）
-- 说明：Project Dev Memory 与 Runtime User Memory 通过 memory_lane 逻辑隔离。
-- 本阶段未启用 FULLTEXT（MySQL 5.7/8 中文分词与 ngram 配置差异较大，先用 LIKE 检索）。
-- ============================================================================

CREATE TABLE IF NOT EXISTS `control_context_namespace` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `namespace_key`   VARCHAR(128) NOT NULL,
    `namespace_type`  VARCHAR(32)  NOT NULL COMMENT 'PERSONAL/PROJECT/MODULE/FEATURE/PAGE/API/WORKFLOW/AGENT/USER/TENANT/SESSION/GLOBAL',
    `tenant_id`       VARCHAR(96)  DEFAULT NULL,
    `project_id`      BIGINT       DEFAULT NULL,
    `project_code`    VARCHAR(96)  DEFAULT NULL,
    `owner_type`      VARCHAR(32)  DEFAULT NULL,
    `owner_id`        VARCHAR(128) DEFAULT NULL,
    `display_name`    VARCHAR(128) DEFAULT NULL,
    `description`     VARCHAR(512) DEFAULT NULL,
    `status`          VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_by`      VARCHAR(128) DEFAULT NULL,
    `created_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`      DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_context_namespace_key` (`namespace_key`),
    KEY `idx_context_namespace_scope` (`tenant_id`, `project_code`, `namespace_type`, `status`),
    KEY `idx_context_namespace_owner` (`owner_type`, `owner_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上下文隔离命名空间';

CREATE TABLE IF NOT EXISTS `control_context_item` (
    `id`               BIGINT        NOT NULL AUTO_INCREMENT,
    `item_key`         VARCHAR(128)  NOT NULL,
    `namespace_id`     BIGINT        NOT NULL,
    `item_type`        VARCHAR(32)   NOT NULL COMMENT 'FACT/PREFERENCE/RULE/DECISION/PITFALL/PAGE_CONTEXT/API_CONTRACT/WORKFLOW_CONTEXT/SESSION_SUMMARY/TRACE_LEARNING/NOTE',
    `memory_lane`      VARCHAR(32)   NOT NULL DEFAULT 'PROJECT_DEV' COMMENT 'PROJECT_DEV or RUNTIME_USER',
    `title`            VARCHAR(256)  DEFAULT NULL,
    `content`          MEDIUMTEXT    NOT NULL,
    `summary`          TEXT          DEFAULT NULL,
    `metadata_json`    MEDIUMTEXT    DEFAULT NULL,
    `source_type`      VARCHAR(48)   NOT NULL COMMENT 'USER_CONFIRMED/USER_MESSAGE/AGENT_OUTPUT/CODE/SQL/DOC/API/TRACE/PAGE/WORKFLOW/SYSTEM/MANUAL',
    `source_ref`       VARCHAR(512)  DEFAULT NULL,
    `confidence`       DECIMAL(5,4)  NOT NULL DEFAULT 0.7000,
    `trust_level`      VARCHAR(24)   NOT NULL DEFAULT 'MEDIUM' COMMENT 'LOW/MEDIUM/HIGH/VERIFIED',
    `visibility`       VARCHAR(32)   NOT NULL DEFAULT 'PRIVATE' COMMENT 'PRIVATE/PROJECT/TENANT/GLOBAL',
    `status`           VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/STALE/REVOKED/DELETED',
    `effective_from`   DATETIME      DEFAULT NULL,
    `expires_at`       DATETIME      DEFAULT NULL,
    `last_verified_at` DATETIME      DEFAULT NULL,
    `stale_after`      DATETIME      DEFAULT NULL,
    `created_by`       VARCHAR(128)  DEFAULT NULL,
    `updated_by`       VARCHAR(128)  DEFAULT NULL,
    `created_at`       DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`       DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`       DATETIME      DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_context_item_key` (`item_key`),
    KEY `idx_context_item_namespace` (`namespace_id`, `status`, `item_type`),
    KEY `idx_context_item_lane` (`memory_lane`, `status`, `updated_at`),
    KEY `idx_context_item_expiry` (`status`, `expires_at`),
    KEY `idx_context_item_trust` (`trust_level`, `confidence`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上下文资产主表';

CREATE TABLE IF NOT EXISTS `control_context_memory_outbox` (
    `id`              BIGINT        NOT NULL AUTO_INCREMENT,
    `event_id`        VARCHAR(64)   NOT NULL COMMENT '跨服务幂等事件 ID',
    `aggregate_type`  VARCHAR(32)   NOT NULL COMMENT 'PERSONAL_MEMORY',
    `aggregate_id`    VARCHAR(128)  NOT NULL COMMENT 'control_context_item.id',
    `event_type`      VARCHAR(48)   NOT NULL COMMENT 'PERSONAL_MEMORY_UPSERT / PERSONAL_MEMORY_DELETE',
    `correlation_id`  VARCHAR(64)   DEFAULT NULL COMMENT '跨域擦除请求关联号；不包含 owner 或正文',
    `payload_json`    MEDIUMTEXT    NOT NULL COMMENT '最小搜索投影事件；删除事件不含原文',
    `status`          VARCHAR(24)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING / PUBLISHING / PUBLISHED / DEAD / SUPERSEDED',
    `attempt_count`   INT           NOT NULL DEFAULT 0,
    `next_attempt_at` DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `locked_at`       DATETIME      DEFAULT NULL,
    `last_error`      VARCHAR(1000) DEFAULT NULL COMMENT '应用仅写最长 64 字符机器失败码；禁止异常消息、响应正文或记忆内容',
    `created_at`      DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `published_at`    DATETIME      DEFAULT NULL,
    `updated_at`      DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_context_memory_outbox_event` (`event_id`),
    KEY `idx_context_memory_outbox_dispatch` (`status`, `next_attempt_at`, `id`),
    KEY `idx_context_memory_outbox_aggregate` (`aggregate_type`, `aggregate_id`, `id`),
    KEY `idx_context_memory_outbox_correlation` (`correlation_id`, `status`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Control 个人记忆到 Knowledge 搜索投影的事务 outbox';

CREATE TABLE IF NOT EXISTS `control_memory_erasure_request` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT,
    `request_id`              VARCHAR(64)  NOT NULL COMMENT '对外不可变请求 ID',
    `client_request_id`       VARCHAR(64)  NOT NULL COMMENT 'tenant 内幂等键',
    `tenant_id`               VARCHAR(96)  NOT NULL,
    `runtime_user_id`         VARCHAR(128) DEFAULT NULL COMMENT '自动域完成后立即擦除',
    `runtime_user_hash`       CHAR(64)     NOT NULL COMMENT '独立密钥 HMAC 后的 owner 证明',
    `reason_code`             VARCHAR(64)  NOT NULL COMMENT '机器可读原因码',
    `reference_id`            VARCHAR(128) NOT NULL COMMENT '审批/工单引用，不保存说明正文',
    `status`                  VARCHAR(32)  NOT NULL COMMENT 'REQUESTED/RUNNING/RETRY/BLOCKED_LEGAL_HOLD/ACTION_REQUIRED/FAILED/COMPLETED/COMPLETED_WITH_RETENTION',
    `attempt_count`           INT          NOT NULL DEFAULT 0,
    `next_attempt_at`         DATETIME     DEFAULT NULL,
    `lease_owner`             VARCHAR(64)  DEFAULT NULL,
    `lease_expires_at`        DATETIME     DEFAULT NULL,
    `last_failure_code`       VARCHAR(64)  DEFAULT NULL COMMENT '机器失败码；禁止异常正文',
    `requested_by_hash`       CHAR(64)     NOT NULL,
    `automated_completed_at`  DATETIME     DEFAULT NULL,
    `completed_at`            DATETIME     DEFAULT NULL,
    `created_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_memory_erasure_request_id` (`request_id`),
    UNIQUE KEY `uk_memory_erasure_client` (`tenant_id`, `client_request_id`),
    KEY `idx_memory_erasure_dispatch` (`status`, `next_attempt_at`, `lease_expires_at`, `id`),
    KEY `idx_memory_erasure_target` (`tenant_id`, `runtime_user_hash`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Control 跨域 Agent 记忆擦除任务';

CREATE TABLE IF NOT EXISTS `control_memory_erasure_domain` (
    `id`                  BIGINT       NOT NULL AUTO_INCREMENT,
    `erasure_request_id`  BIGINT       NOT NULL,
    `domain_code`         VARCHAR(64)  NOT NULL,
    `owner_service`       VARCHAR(96)  NOT NULL,
    `execution_mode`      VARCHAR(32)  NOT NULL COMMENT 'AUTOMATED / MANUAL_EVIDENCE',
    `status`              VARCHAR(32)  NOT NULL COMMENT 'PENDING/WAITING_DEPENDENCY/WAITING_EVIDENCE/BLOCKED_LEGAL_HOLD/FAILED/COMPLETED',
    `result_code`         VARCHAR(64)  DEFAULT NULL,
    `affected_count`      BIGINT       DEFAULT NULL COMMENT '仅聚合数量，不保存对象 ID',
    `evidence_reference`  VARCHAR(128) DEFAULT NULL COMMENT '外部证据引用，不保存正文或 URL 凭据',
    `attempt_count`       INT          NOT NULL DEFAULT 0,
    `last_failure_code`   VARCHAR(64)  DEFAULT NULL,
    `completed_at`        DATETIME     DEFAULT NULL,
    `created_at`          DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`          DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_memory_erasure_domain` (`erasure_request_id`, `domain_code`),
    KEY `idx_memory_erasure_domain_status` (`status`, `updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='跨域 Agent 记忆擦除逐域证据状态';

CREATE TABLE IF NOT EXISTS `control_context_binding` (
    `id`           BIGINT       NOT NULL AUTO_INCREMENT,
    `item_id`      BIGINT       NOT NULL,
    `bind_type`    VARCHAR(32)  NOT NULL COMMENT 'TENANT/PROJECT/USER/AGENT/WORKFLOW/PAGE/API/SESSION/MODULE/FEATURE',
    `bind_id`      VARCHAR(128) NOT NULL,
    `bind_key`     VARCHAR(256) DEFAULT NULL,
    `tenant_id`    VARCHAR(96)  DEFAULT NULL,
    `project_id`   BIGINT       DEFAULT NULL,
    `project_code` VARCHAR(96)  DEFAULT NULL,
    `status`       VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `deleted_at`   DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_context_binding_target` (`bind_type`, `bind_id`, `status`),
    KEY `idx_context_binding_item` (`item_id`, `status`),
    KEY `idx_context_binding_project` (`project_code`, `bind_type`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上下文资产绑定关系';

CREATE TABLE IF NOT EXISTS `control_context_evidence` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `item_id`         BIGINT       NOT NULL,
    `evidence_type`   VARCHAR(48)  NOT NULL COMMENT 'USER_CONFIRMATION/SOURCE_FILE/SQL_SCHEMA/API_RESPONSE/TRACE_SPAN/TOOL_CALL/DOCUMENT/MANUAL_NOTE',
    `evidence_ref`    VARCHAR(512) DEFAULT NULL,
    `evidence_excerpt` TEXT        DEFAULT NULL,
    `trace_id`        VARCHAR(128) DEFAULT NULL,
    `confidence`      DECIMAL(5,4) DEFAULT NULL,
    `metadata_json`   MEDIUMTEXT   DEFAULT NULL,
    `created_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_context_evidence_item` (`item_id`),
    KEY `idx_context_evidence_trace` (`trace_id`),
    KEY `idx_context_evidence_type` (`evidence_type`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上下文资产来源证据';

CREATE TABLE IF NOT EXISTS `control_context_audit_event` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `event_type`    VARCHAR(48)  NOT NULL COMMENT 'CREATE/UPDATE/READ/SEARCH/INJECT/DELETE/REVOKE/EXPIRE/VERIFY/MARK_STALE',
    `item_id`       BIGINT       DEFAULT NULL,
    `namespace_id`  BIGINT       DEFAULT NULL,
    `actor_type`    VARCHAR(32)  DEFAULT NULL,
    `actor_id`      VARCHAR(128) DEFAULT NULL,
    `tenant_id`     VARCHAR(96)  DEFAULT NULL,
    `project_id`    BIGINT       DEFAULT NULL,
    `project_code`  VARCHAR(96)  DEFAULT NULL,
    `agent_id`      VARCHAR(128) DEFAULT NULL,
    `workflow_id`   VARCHAR(128) DEFAULT NULL,
    `session_id`    VARCHAR(128) DEFAULT NULL,
    `trace_id`      VARCHAR(128) DEFAULT NULL,
    `decision`      VARCHAR(32)  DEFAULT NULL COMMENT 'ALLOW/DENY/SKIP',
    `reason`        VARCHAR(512) DEFAULT NULL,
    `metadata_json` MEDIUMTEXT   DEFAULT NULL,
    `created_at`    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_context_audit_item` (`item_id`, `created_at`),
    KEY `idx_context_audit_actor` (`actor_type`, `actor_id`, `created_at`),
    KEY `idx_context_audit_trace` (`trace_id`),
    KEY `idx_context_audit_project` (`project_code`, `created_at`),
    KEY `idx_context_audit_project_id` (`project_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上下文治理审计事件';

CREATE TABLE IF NOT EXISTS `control_context_memory_candidate` (
    `id`                BIGINT        NOT NULL AUTO_INCREMENT,
    `candidate_key`     VARCHAR(128)  NOT NULL,
    `tenant_id`         VARCHAR(96)   NOT NULL,
    `project_id`        BIGINT        DEFAULT NULL,
    `project_code`      VARCHAR(96)   DEFAULT NULL,
    `namespace_id`      BIGINT        DEFAULT NULL,
    `namespace_key`     VARCHAR(128)  DEFAULT NULL,
    `memory_lane`       VARCHAR(32)   NOT NULL DEFAULT 'RUNTIME_USER',
    `candidate_type`    VARCHAR(32)   NOT NULL COMMENT 'PREFERENCE/FACT/RULE/PAGE_CONTEXT/WORKFLOW_CONTEXT/API_CONTEXT/NOTE',
    `title`             VARCHAR(256)  DEFAULT NULL,
    `content`           MEDIUMTEXT    NOT NULL,
    `content_sha256`    CHAR(64)      DEFAULT NULL COMMENT '规范化候选内容摘要；不替代加密与访问控制',
    `dedupe_key`        VARCHAR(128)  DEFAULT NULL COMMENT '仅 PENDING 阶段持有的并发去重键；审核后清空',
    `semantic_key`      VARCHAR(128)  DEFAULT NULL COMMENT '可选稳定语义键，用于识别属性替换冲突',
    `summary`           TEXT          DEFAULT NULL,
    `reason`            VARCHAR(512)  DEFAULT NULL,
    `source_type`       VARCHAR(48)   NOT NULL COMMENT 'USER_MESSAGE/USER_CONFIRMED/AGENT_OUTPUT/TRACE/PAGE/WORKFLOW/MANUAL/SYSTEM',
    `source_ref`        VARCHAR(512)  DEFAULT NULL,
    `trace_id`          VARCHAR(128)  DEFAULT NULL,
    `session_id`        VARCHAR(128)  DEFAULT NULL,
    `user_id`           VARCHAR(128)  DEFAULT NULL,
    `external_user_id`  VARCHAR(128)  DEFAULT NULL,
    `global_user_id`    VARCHAR(128)  DEFAULT NULL,
    `agent_id`          VARCHAR(128)  DEFAULT NULL,
    `agent_key`         VARCHAR(128)  DEFAULT NULL,
    `workflow_id`       VARCHAR(128)  DEFAULT NULL,
    `workflow_key`      VARCHAR(128)  DEFAULT NULL,
    `page_instance_id`  VARCHAR(128)  DEFAULT NULL,
    `origin`            VARCHAR(512)  DEFAULT NULL,
    `confidence`        DECIMAL(5,4)  NOT NULL DEFAULT 0.7000,
    `trust_level`       VARCHAR(24)   NOT NULL DEFAULT 'LOW',
    `visibility`        VARCHAR(32)   NOT NULL DEFAULT 'PRIVATE',
    `status`            VARCHAR(24)   NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/APPROVED/REJECTED/EXPIRED/DELETED',
    `proposed_by`       VARCHAR(128)  DEFAULT NULL,
    `reviewed_by`       VARCHAR(128)  DEFAULT NULL,
    `reviewed_at`       DATETIME      DEFAULT NULL,
    `review_reason`     VARCHAR(512)  DEFAULT NULL,
    `approved_item_id`  BIGINT        DEFAULT NULL COMMENT '批准后写入 control_context_item.id',
    `conflict_item_id`  BIGINT        DEFAULT NULL COMMENT '同一语义键下待替换的既有个人记忆条目',
    `conflict_type`     VARCHAR(32)   DEFAULT NULL COMMENT 'REPLACEMENT；精确重复会直接跳过而不创建候选',
    `occurrence_count`  INT           NOT NULL DEFAULT 1 COMMENT '同一 pending 候选被重复观察到的次数',
    `last_seen_at`      DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近一次重复观察时间',
    `extraction_version` VARCHAR(32)  DEFAULT NULL COMMENT '规则或模型提取器版本',
    `metadata_json`     MEDIUMTEXT    DEFAULT NULL,
    `expires_at`        DATETIME      DEFAULT NULL,
    `created_at`        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`        DATETIME      DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_context_memory_candidate_key` (`candidate_key`),
    UNIQUE KEY `uk_context_memory_candidate_dedupe` (`dedupe_key`),
    KEY `idx_context_memory_candidate_scope` (`tenant_id`, `project_code`, `memory_lane`, `status`),
    KEY `idx_context_memory_candidate_user` (`tenant_id`, `user_id`, `status`, `created_at`),
    KEY `idx_context_memory_candidate_session` (`session_id`, `status`, `created_at`),
    KEY `idx_context_memory_candidate_trace` (`trace_id`),
    KEY `idx_context_memory_candidate_agent` (`agent_id`, `status`, `created_at`),
    KEY `idx_context_memory_candidate_review` (`status`, `expires_at`, `created_at`),
    KEY `idx_context_memory_candidate_namespace` (`namespace_id`, `status`),
    KEY `idx_context_memory_candidate_semantic` (`namespace_id`, `semantic_key`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime User Memory 写回候选（确认前缓冲）';

CREATE TABLE IF NOT EXISTS `knowledge_personal_memory_index` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `memory_id`          BIGINT       NOT NULL COMMENT 'Control canonical control_context_item.id',
    `tenant_id`          VARCHAR(96)  NOT NULL,
    `runtime_user_hash`  CHAR(64)     NOT NULL COMMENT 'HMAC 后用户标识；Knowledge 不保存原始 Runtime user id',
    `item_type`          VARCHAR(32)  NOT NULL,
    `title`              VARCHAR(256) DEFAULT NULL,
    `content`            MEDIUMTEXT   NOT NULL,
    `summary`            TEXT         DEFAULT NULL,
    `trust_level`        VARCHAR(24)  NOT NULL,
    `source_version`     BIGINT       NOT NULL COMMENT 'Control 条目版本；旧事件不得覆盖新投影',
    `status`             VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `expires_at`         DATETIME     DEFAULT NULL,
    `embedding_vector`   MEDIUMBLOB   DEFAULT NULL COMMENT '可重建 float32 向量；不作为 canonical 事实源',
    `embedding_format`   VARCHAR(32)  DEFAULT NULL COMMENT '当前为 FLOAT32_BE_V1',
    `embedding_dimension` INT         DEFAULT NULL,
    `embedding_model_instance_id` VARCHAR(64) DEFAULT NULL,
    `embedding_source_version` BIGINT DEFAULT NULL COMMENT '必须等于 source_version 才可参与召回',
    `embedding_text_sha256` CHAR(64)  DEFAULT NULL COMMENT '发送给 Model Gateway 的截断文本摘要',
    `embedding_status`   VARCHAR(24)  NOT NULL DEFAULT 'DISABLED' COMMENT 'DISABLED/PENDING/PROCESSING/RETRY/READY/DEAD/DELETED',
    `embedding_attempts` INT          NOT NULL DEFAULT 0,
    `embedding_error_code` VARCHAR(64) DEFAULT NULL COMMENT '仅机器错误码，不保存供应商响应或记忆正文',
    `embedding_next_attempt_at` DATETIME DEFAULT NULL,
    `embedding_claim_token` VARCHAR(64) DEFAULT NULL,
    `embedding_claim_until` DATETIME DEFAULT NULL,
    `created_at`         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`         DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_knowledge_personal_memory` (`tenant_id`, `runtime_user_hash`, `memory_id`),
    KEY `idx_knowledge_personal_memory_scope` (`tenant_id`, `runtime_user_hash`, `status`, `updated_at`),
    KEY `idx_knowledge_personal_memory_expiry` (`status`, `expires_at`),
    KEY `idx_knowledge_personal_memory_embedding` (`embedding_status`, `embedding_next_attempt_at`, `embedding_claim_until`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Knowledge 可重建的个人记忆搜索投影；非事实源';

-- Keep initV2 re-runnable against an earlier baseline where the projection
-- table already exists. Fresh databases already have these definitions above.
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_vector',
    'MEDIUMBLOB DEFAULT NULL COMMENT ''可重建 float32 向量；不作为 canonical 事实源'' AFTER `expires_at`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_format',
    'VARCHAR(32) DEFAULT NULL COMMENT ''当前为 FLOAT32_BE_V1'' AFTER `embedding_vector`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_dimension',
    'INT DEFAULT NULL AFTER `embedding_format`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_model_instance_id',
    'VARCHAR(64) DEFAULT NULL AFTER `embedding_dimension`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_source_version',
    'BIGINT DEFAULT NULL COMMENT ''必须等于 source_version 才可参与召回'' AFTER `embedding_model_instance_id`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_text_sha256',
    'CHAR(64) DEFAULT NULL COMMENT ''发送给 Model Gateway 的截断文本摘要'' AFTER `embedding_source_version`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_status',
    'VARCHAR(24) NOT NULL DEFAULT ''DISABLED'' COMMENT ''DISABLED/PENDING/PROCESSING/RETRY/READY/DEAD/DELETED'' AFTER `embedding_text_sha256`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_attempts',
    'INT NOT NULL DEFAULT 0 AFTER `embedding_status`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_error_code',
    'VARCHAR(64) DEFAULT NULL COMMENT ''仅机器错误码，不保存供应商响应或记忆正文'' AFTER `embedding_attempts`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_next_attempt_at',
    'DATETIME DEFAULT NULL AFTER `embedding_error_code`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_claim_token',
    'VARCHAR(64) DEFAULT NULL AFTER `embedding_next_attempt_at`');
CALL add_col_if_absent('knowledge_personal_memory_index', 'embedding_claim_until',
    'DATETIME DEFAULT NULL AFTER `embedding_claim_token`');
CALL add_idx_if_absent('knowledge_personal_memory_index', 'idx_knowledge_personal_memory_embedding',
    '`embedding_status`, `embedding_next_attempt_at`, `embedding_claim_until`, `id`');

UPDATE `knowledge_personal_memory_index`
SET `embedding_vector` = NULL,
    `embedding_format` = NULL,
    `embedding_dimension` = NULL,
    `embedding_model_instance_id` = NULL,
    `embedding_source_version` = NULL,
    `embedding_text_sha256` = NULL,
    `embedding_status` = 'DELETED',
    `embedding_attempts` = 0,
    `embedding_error_code` = NULL,
    `embedding_next_attempt_at` = NULL,
    `embedding_claim_token` = NULL,
    `embedding_claim_until` = NULL
WHERE `status` <> 'ACTIVE';

CREATE TABLE IF NOT EXISTS `knowledge_personal_memory_index_event` (
    `id`            BIGINT        NOT NULL AUTO_INCREMENT,
    `event_id`      VARCHAR(64)   NOT NULL,
    `event_type`    VARCHAR(48)   NOT NULL,
    `memory_id`     BIGINT        NOT NULL,
    `status`        VARCHAR(24)   NOT NULL COMMENT 'APPLIED / IGNORED_STALE',
    `payload_sha256` CHAR(64)     NOT NULL,
    `processed_at`  DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_knowledge_personal_memory_event` (`event_id`),
    KEY `idx_knowledge_personal_memory_event_memory` (`memory_id`, `processed_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='个人记忆搜索投影消费幂等账本';

CREATE TABLE IF NOT EXISTS `control_context_runtime_user_mapping` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `tenant_id`        VARCHAR(96)  NOT NULL DEFAULT 'default',
    `platform_user_id` BIGINT       NOT NULL COMMENT '平台管理端用户 ID',
    `runtime_user_id`  VARCHAR(128) NOT NULL COMMENT 'Context runtime user ownerId（global/external/user 归一值）',
    `global_user_id`   VARCHAR(128) DEFAULT NULL,
    `external_user_id` VARCHAR(128) DEFAULT NULL,
    `project_id`       BIGINT       DEFAULT NULL,
    `project_code`     VARCHAR(96)  DEFAULT NULL,
    `status`           VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `active_marker`    TINYINT(1)   DEFAULT 1 COMMENT 'ACTIVE 时为 1，删除后为 NULL；用于唯一活跃 owner 槽位',
    `created_by`       VARCHAR(128) DEFAULT NULL,
    `created_at`       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted_at`       DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_context_runtime_user_mapping_active` (`tenant_id`, `platform_user_id`, `active_marker`),
    KEY `idx_context_runtime_user_mapping_actor` (`tenant_id`, `platform_user_id`, `runtime_user_id`, `status`),
    KEY `idx_context_runtime_user_mapping_project` (`tenant_id`, `project_code`, `project_id`, `status`),
    KEY `idx_context_runtime_user_mapping_runtime` (`tenant_id`, `runtime_user_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台用户代审 Runtime User 记忆的身份映射';

CALL add_idx_if_absent('control_context_audit_event', 'idx_context_audit_project_id', '`project_id`, `created_at`');

CREATE TABLE IF NOT EXISTS `control_platform_user` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `username`         VARCHAR(96)  NOT NULL,
    `display_name`     VARCHAR(128) DEFAULT NULL,
    `email`            VARCHAR(128) DEFAULT NULL,
    `mobile`           VARCHAR(64)  DEFAULT NULL,
    `status`           VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `source_provider`  VARCHAR(32)  NOT NULL,
    `external_subject` VARCHAR(128) NOT NULL,
    `password_hash`    VARCHAR(256) DEFAULT NULL,
    `last_login_at`    DATETIME     DEFAULT NULL,
    `created_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_user_provider_subject` (`source_provider`, `external_subject`),
    UNIQUE KEY `uk_platform_user_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台管理用户';

CREATE TABLE IF NOT EXISTS `control_platform_role` (
    `id`          BIGINT       NOT NULL AUTO_INCREMENT,
    `role_code`   VARCHAR(64)  NOT NULL,
    `role_name`   VARCHAR(128) NOT NULL,
    `description` VARCHAR(512) DEFAULT NULL,
    `role_kind`   VARCHAR(24)  NOT NULL DEFAULT 'CUSTOM' COMMENT 'SYSTEM / CUSTOM',
    `status`      VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`  DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`  DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_role_code` (`role_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台管理角色';

CREATE TABLE IF NOT EXISTS `control_platform_user_role` (
    `id`          BIGINT      NOT NULL AUTO_INCREMENT,
    `user_id`     BIGINT      NOT NULL,
    `role_id`     BIGINT      NOT NULL,
    `scope_type`  VARCHAR(32) NOT NULL DEFAULT 'GLOBAL',
    `scope_value` VARCHAR(96) DEFAULT '*',
    `created_at`  DATETIME    DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_user_role_scope` (`user_id`, `role_id`, `scope_type`, `scope_value`),
    KEY `idx_platform_user_role_user` (`user_id`),
    KEY `idx_platform_user_role_role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台用户角色绑定';

CREATE TABLE IF NOT EXISTS `control_platform_permission` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `permission_code` VARCHAR(96)  NOT NULL,
    `permission_name` VARCHAR(128) NOT NULL,
    `resource_type`   VARCHAR(64)  DEFAULT NULL,
    `action`          VARCHAR(64)  DEFAULT NULL,
    `description`     VARCHAR(512) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_permission_code` (`permission_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台管理权限';

CREATE TABLE IF NOT EXISTS `control_platform_role_permission` (
    `id`            BIGINT NOT NULL AUTO_INCREMENT,
    `role_id`       BIGINT NOT NULL,
    `permission_id` BIGINT NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_role_permission` (`role_id`, `permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台角色权限绑定';

CREATE TABLE IF NOT EXISTS `control_platform_login_session` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `session_id`       VARCHAR(96)  NOT NULL,
    `user_id`          BIGINT       NOT NULL,
    `provider`         VARCHAR(32)  NOT NULL,
    `access_token_id`  VARCHAR(160) NOT NULL,
    `refresh_token_id` VARCHAR(160) DEFAULT NULL,
    `ip`               VARCHAR(64)  DEFAULT NULL,
    `user_agent`       VARCHAR(512) DEFAULT NULL,
    `expires_at`       DATETIME     NOT NULL,
    `revoked_at`       DATETIME     DEFAULT NULL,
    `created_at`       DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_session_id` (`session_id`),
    UNIQUE KEY `uk_platform_access_token` (`access_token_id`),
    KEY `idx_platform_session_user` (`user_id`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台管理端登录会话';

CREATE TABLE IF NOT EXISTS `control_platform_auth_provider` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `provider_code`   VARCHAR(32)  NOT NULL,
    `provider_name`   VARCHAR(128) NOT NULL,
    `provider_type`   VARCHAR(32)  NOT NULL,
    `config_json`     TEXT         DEFAULT NULL,
    `status`          VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_platform_auth_provider` (`provider_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台管理端身份提供方配置';

CREATE TABLE IF NOT EXISTS `control_platform_auth_audit_event` (
    `id`               BIGINT       NOT NULL AUTO_INCREMENT,
    `event_type`       VARCHAR(96)  NOT NULL,
    `actor_user_id`    BIGINT       DEFAULT NULL,
    `actor_session_id` VARCHAR(96)  DEFAULT NULL,
    `target_type`      VARCHAR(64)  NOT NULL,
    `target_id`        VARCHAR(128) DEFAULT NULL,
    `details_json`     TEXT         DEFAULT NULL COMMENT '不含密码、Token、密钥或个人记忆正文的安全元数据',
    `created_at`       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_platform_auth_audit_actor` (`actor_user_id`, `created_at`),
    KEY `idx_platform_auth_audit_target` (`target_type`, `target_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台认证与高风险身份映射管理操作审计';

INSERT IGNORE INTO `control_platform_role` (`role_code`, `role_name`, `description`, `role_kind`, `status`)
VALUES
('PLATFORM_ADMIN', '平台管理员', '平台全量管理与配置', 'SYSTEM', 'ACTIVE'),
('AGENT_DESIGNER', '智能体设计者', '创建与编辑智能体及工作流', 'SYSTEM', 'ACTIVE'),
('PROJECT_OWNER', '项目负责人', '管理已分配业务项目', 'SYSTEM', 'ACTIVE'),
('OPERATOR', '运维操作员', '运行、调试与回放会话', 'SYSTEM', 'ACTIVE'),
('AUDITOR', '审计员', '只读审计与追踪', 'SYSTEM', 'ACTIVE');

INSERT IGNORE INTO `control_platform_permission` (`permission_code`, `permission_name`, `resource_type`, `action`)
VALUES
('*', 'All platform permissions', 'PLATFORM', '*'),
('platform:read', 'Read platform assets', 'PLATFORM', 'READ'),
('platform:write', 'Write platform assets', 'PLATFORM', 'WRITE'),
('platform:admin', 'Administer platform users and roles', 'PLATFORM', 'ADMIN'),
('workspace:build:access', 'Access the build workspace', 'PLATFORM_WORKSPACE', 'ACCESS_BUILD'),
('workspace:operate:access', 'Access the operations and governance workspace', 'PLATFORM_WORKSPACE', 'ACCESS_OPERATE'),
('identity:business-user:read', 'Read the governed business user directory', 'BUSINESS_USER', 'READ'),
('identity:business-user:manage', 'Manage business user profiles and external identity bindings', 'BUSINESS_USER', 'MANAGE'),
('skill:read', 'Read standard Agent Skill packages', 'AGENT_SKILL', 'READ'),
('skill:import', 'Import standard Agent Skill packages', 'AGENT_SKILL', 'IMPORT'),
('skill:review', 'Review standard Agent Skill package versions', 'AGENT_SKILL', 'REVIEW'),
('skill:publish', 'Publish, deprecate, and revoke Agent Skill versions', 'AGENT_SKILL', 'PUBLISH'),
('skill:bind', 'Bind published Agent Skill versions to Agent configuration', 'AGENT_SKILL', 'BIND'),
('skill:script:approve', 'Approve sandboxed Agent Skill script execution policy', 'AGENT_SKILL', 'APPROVE_SCRIPT'),
('a2a-hub:read', 'Read A2A Hub catalogs and safe task summaries', 'A2A_HUB', 'READ'),
('a2a-hub:publication:manage', 'Manage local A2A publications', 'A2A_PUBLICATION', 'MANAGE'),
('a2a-hub:remote-agent:manage', 'Manage trusted remote A2A agents', 'A2A_REMOTE_AGENT', 'MANAGE'),
('a2a-hub:trust:manage', 'Manage A2A principals and trust profiles', 'A2A_TRUST', 'MANAGE'),
('a2a-hub:credential:manage', 'Create, rotate, and revoke A2A credentials', 'A2A_CREDENTIAL', 'MANAGE'),
('a2a-hub:task:operate', 'Operate A2A tasks without reading retained payloads', 'A2A_TASK', 'OPERATE'),
('a2a-hub:payload:read', 'Read retained A2A payloads under audited authorization', 'A2A_PAYLOAD', 'READ'),
('a2a-hub:conformance:run', 'Run A2A diagnostics and conformance checks', 'A2A_CONFORMANCE', 'RUN'),
('mcp-hub:read', 'Read MCP publications and safe call summaries', 'MCP_HUB', 'READ'),
('mcp-hub:publication:manage', 'Manage outbound MCP publications', 'MCP_PUBLICATION', 'MANAGE'),
('mcp-hub:publication:irreversible', 'Publish MCP revisions containing irreversible tools', 'MCP_PUBLICATION', 'PUBLISH_IRREVERSIBLE'),
('mcp-hub:credential:manage', 'Create, rotate, and revoke MCP credentials', 'MCP_CREDENTIAL', 'MANAGE'),
('mcp-hub:credential-role:manage', 'Grant Tool ACL roles to MCP credentials', 'MCP_CREDENTIAL_ROLE', 'MANAGE'),
('mcp-hub:payload:read', 'Read retained redacted MCP payloads under audited authorization', 'MCP_PAYLOAD', 'READ'),
('context:runtime-user:review', 'Review runtime user context candidates', 'CONTEXT_RUNTIME_USER', 'REVIEW'),
('context:runtime-user:mapping:manage', 'Manage runtime user review mappings', 'CONTEXT_RUNTIME_USER', 'MANAGE_MAPPING'),
('context:memory:erasure:manage', 'Manage cross-domain Agent memory erasure', 'CONTEXT_MEMORY', 'MANAGE_ERASURE'),
('runtime:session:retention:manage', 'Manage Runtime session retention and legal hold', 'RUNTIME_SESSION', 'MANAGE_RETENTION'),
('automation:read', 'Read Automation definitions and execution history', 'AUTOMATION', 'READ'),
('automation:write', 'Create, version, pause, resume, and archive Automations', 'AUTOMATION', 'WRITE'),
('automation:operate', 'Run, retry, and cancel Automation occurrences', 'AUTOMATION', 'OPERATE'),
('agent:read', 'Read Agent definitions and versions', 'AGENT', 'READ'),
('agent:write', 'Create and edit Agent definitions and drafts', 'AGENT', 'WRITE'),
('agent:debug', 'Debug Agents and handle development approvals', 'AGENT', 'DEBUG'),
('agent:evaluate', 'Create and operate Agent evaluation assets', 'AGENT', 'EVALUATE'),
('agent:publish', 'Publish Agent configuration versions', 'AGENT', 'PUBLISH'),
('workflow:read', 'Read Workflow definitions and versions', 'WORKFLOW', 'READ'),
('workflow:write', 'Create and edit Workflow definitions and working copies', 'WORKFLOW', 'WRITE'),
('workflow:debug', 'Debug Workflow working copies', 'WORKFLOW', 'DEBUG'),
('workflow:publish', 'Publish and roll back Workflow versions', 'WORKFLOW', 'PUBLISH'),
('workflow:credential:manage', 'Manage Workflow execution credentials', 'WORKFLOW_CREDENTIAL', 'MANAGE'),
('runops:read', 'Read traces, diagnostics, and guard decisions', 'RUNOPS', 'READ'),
('runops:operate', 'Replay and operate Runtime executions', 'RUNOPS', 'OPERATE');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code = 'PLATFORM_ADMIN'
  AND p.permission_code IN ('*', 'platform:read', 'platform:write', 'platform:admin',
                            'workspace:build:access', 'workspace:operate:access',
                            'identity:business-user:read', 'identity:business-user:manage',
                            'skill:read', 'skill:import', 'skill:review',
                            'skill:publish', 'skill:bind', 'skill:script:approve',
                            'a2a-hub:read', 'a2a-hub:publication:manage',
                            'a2a-hub:remote-agent:manage', 'a2a-hub:trust:manage',
                            'a2a-hub:credential:manage', 'a2a-hub:task:operate',
                            'a2a-hub:payload:read', 'a2a-hub:conformance:run',
                            'context:runtime-user:review', 'context:runtime-user:mapping:manage',
                            'context:memory:erasure:manage',
                            'runtime:session:retention:manage',
                            'automation:read', 'automation:write', 'automation:operate',
                            'agent:read', 'agent:write', 'agent:debug',
                            'agent:evaluate', 'agent:publish',
                            'workflow:read', 'workflow:write', 'workflow:debug',
                            'workflow:publish', 'workflow:credential:manage',
                            'runops:read', 'runops:operate');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code IN ('AGENT_DESIGNER', 'PROJECT_OWNER', 'OPERATOR')
  AND p.permission_code IN ('platform:read', 'platform:write');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE (r.role_code = 'AGENT_DESIGNER' AND p.permission_code = 'workspace:build:access')
   OR (r.role_code = 'PROJECT_OWNER'
       AND p.permission_code IN ('workspace:build:access', 'workspace:operate:access'))
   OR (r.role_code IN ('OPERATOR', 'AUDITOR')
       AND p.permission_code = 'workspace:operate:access');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code = 'AUDITOR'
  AND p.permission_code = 'platform:read';

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE (r.role_code IN ('AGENT_DESIGNER', 'PROJECT_OWNER')
       AND p.permission_code IN ('automation:read', 'automation:write', 'automation:operate'))
   OR (r.role_code = 'OPERATOR'
       AND p.permission_code IN ('automation:read', 'automation:operate'))
   OR (r.role_code = 'AUDITOR' AND p.permission_code = 'automation:read');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE (r.role_code = 'AGENT_DESIGNER'
       AND p.permission_code IN ('agent:read', 'agent:write', 'agent:debug', 'agent:evaluate',
                                 'workflow:read', 'workflow:write', 'workflow:debug',
                                 'runops:read'))
   OR (r.role_code = 'PROJECT_OWNER'
       AND p.permission_code IN ('agent:read', 'agent:write', 'agent:debug', 'agent:evaluate',
                                 'agent:publish', 'workflow:read', 'workflow:write',
                                 'workflow:debug', 'workflow:publish',
                                 'runops:read', 'runops:operate'))
   OR (r.role_code = 'OPERATOR'
       AND p.permission_code IN ('agent:read', 'agent:debug',
                                 'workflow:read', 'workflow:debug',
                                 'runops:read', 'runops:operate'))
   OR (r.role_code = 'AUDITOR'
       AND p.permission_code IN ('agent:read', 'workflow:read', 'runops:read'));

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code = 'AGENT_DESIGNER'
  AND p.permission_code IN ('skill:read', 'skill:import', 'skill:bind');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code = 'PROJECT_OWNER'
  AND p.permission_code IN ('skill:read', 'skill:import', 'skill:review', 'skill:publish', 'skill:bind');

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE r.role_code IN ('OPERATOR', 'AUDITOR')
  AND p.permission_code = 'skill:read';

INSERT IGNORE INTO `control_platform_role_permission` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `control_platform_role` r JOIN `control_platform_permission` p
WHERE (r.role_code = 'AGENT_DESIGNER'
       AND p.permission_code IN ('a2a-hub:read', 'a2a-hub:publication:manage',
                                 'a2a-hub:remote-agent:manage', 'a2a-hub:conformance:run'))
   OR (r.role_code = 'OPERATOR'
       AND p.permission_code IN ('a2a-hub:read', 'a2a-hub:task:operate',
                                 'a2a-hub:conformance:run'))
   OR (r.role_code = 'AUDITOR' AND p.permission_code = 'a2a-hub:read');

INSERT IGNORE INTO `control_platform_auth_provider` (`provider_code`, `provider_name`, `provider_type`, `status`, `config_json`)
VALUES
('LOCAL', '本地开发登录', 'LOCAL', 'ACTIVE', '{}'),
('HEADER', '受信任网关请求头', 'HEADER', 'ACTIVE', '{}'),
('OIDC', '企业 OIDC 登录', 'OIDC', 'INACTIVE', '{}'),
('SAML', '企业 SAML 登录', 'SAML', 'INACTIVE', '{}');

CREATE TABLE IF NOT EXISTS `runtime_agent_workflow_credential` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `credential_ref` VARCHAR(128) NOT NULL,
    `name`           VARCHAR(128) NOT NULL,
    `type`           VARCHAR(32)  NOT NULL,
    `project_id`     BIGINT       DEFAULT NULL,
    `project_code`   VARCHAR(96)  DEFAULT NULL,
    `scope`          VARCHAR(24)  NOT NULL DEFAULT 'PROJECT',
    `status`         VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `secret_json`    MEDIUMTEXT   NOT NULL,
    `created_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`     DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_workflow_credential_ref` (`credential_ref`),
    KEY `idx_workflow_credential_project` (`project_id`, `status`),
    KEY `idx_workflow_credential_code` (`project_code`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Workflow Studio 节点凭证';

CREATE TABLE IF NOT EXISTS `control_internal_auth_nonce` (
    `nonce`      VARCHAR(128) NOT NULL COMMENT 'Runtime→Control HMAC nonce',
    `caller`     VARCHAR(96)  NOT NULL COMMENT '调用方服务 ID',
    `created_at` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '写入时间',
    PRIMARY KEY (`nonce`),
    KEY `idx_control_internal_auth_nonce_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Control internal service auth nonce anti-replay store';

CREATE TABLE IF NOT EXISTS `control_agent_skill_market_source` (
    `id`                BIGINT        NOT NULL AUTO_INCREMENT,
    `source_key`        VARCHAR(64)   NOT NULL COMMENT '稳定来源键',
    `display_name`      VARCHAR(128)  NOT NULL,
    `source_type`       VARCHAR(32)   NOT NULL COMMENT 'AGGREGATOR / TRANSPORT / CURATED_REPOSITORY',
    `base_url`          VARCHAR(512)  DEFAULT NULL,
    `repository_url`    VARCHAR(512)  DEFAULT NULL,
    `trust_level`       VARCHAR(32)   NOT NULL COMMENT 'AGGREGATED / TRANSPORT / OFFICIAL / VERIFIED',
    `status`            VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE',
    `supports_search`   TINYINT(1)    NOT NULL DEFAULT 0,
    `supports_import`   TINYINT(1)    NOT NULL DEFAULT 0,
    `official`          TINYINT(1)    NOT NULL DEFAULT 0,
    `description`       VARCHAR(1000) DEFAULT NULL,
    `display_order`     INT           NOT NULL DEFAULT 100,
    `created_at`        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_agent_skill_market_source` (`source_key`),
    KEY `idx_control_agent_skill_market_source_status` (`status`, `display_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Skill 外部发现来源与可信度说明';

CREATE TABLE IF NOT EXISTS `control_agent_skill_market_import` (
    `id`                     BIGINT       NOT NULL AUTO_INCREMENT,
    `origin_fingerprint`     CHAR(64)     NOT NULL COMMENT '不可变来源与导入版本的幂等指纹',
    `provider_key`           VARCHAR(64)  NOT NULL,
    `marketplace_skill_id`   VARCHAR(256) DEFAULT NULL,
    `repository`             VARCHAR(201) NOT NULL COMMENT 'GitHub owner/repository',
    `repository_url`         VARCHAR(512) NOT NULL,
    `source_commit_sha`      CHAR(40)     NOT NULL,
    `source_root`            VARCHAR(512) NOT NULL COMMENT '仓库内 Skill 相对目录',
    `bundle_source_sha256`   CHAR(64)     NOT NULL,
    `selected_source_sha256` CHAR(64)     NOT NULL,
    `skill_id`               BIGINT       NOT NULL,
    `skill_version_id`       BIGINT       NOT NULL,
    `publisher`              VARCHAR(64)  NOT NULL,
    `standard_name`          VARCHAR(64)  NOT NULL,
    `version`                VARCHAR(64)  NOT NULL,
    `visibility`             VARCHAR(24)  NOT NULL,
    `project_code`           VARCHAR(128) DEFAULT NULL,
    `imported_by_user_id`    BIGINT       DEFAULT NULL,
    `imported_by`            VARCHAR(128) NOT NULL,
    `created_at`             DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_agent_skill_market_origin` (`origin_fingerprint`),
    KEY `idx_control_agent_skill_market_import_version` (`skill_version_id`, `created_at`),
    KEY `idx_control_agent_skill_market_import_repo` (`repository`, `source_commit_sha`),
    KEY `idx_control_agent_skill_market_import_actor` (`imported_by_user_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 Skill 导入来源、commit 与摘要证据';

INSERT IGNORE INTO `control_agent_skill_market_source`
(`source_key`, `display_name`, `source_type`, `base_url`, `repository_url`,
 `trust_level`, `status`, `supports_search`, `supports_import`, `official`,
 `description`, `display_order`)
VALUES
('SKILLS_SH', 'skills.sh', 'AGGREGATOR', 'https://skills.sh', NULL,
 'AGGREGATED', 'ACTIVE', 1, 1, 0,
 '开放 Agent Skills 聚合发现与热度信号；搜索结果必须重新锁定 GitHub commit 并进入 ReachAI 评审。', 10),
('GITHUB', 'GitHub 公共仓库', 'TRANSPORT', 'https://github.com', NULL,
 'TRANSPORT', 'ACTIVE', 0, 1, 0,
 '只支持公开 HTTPS 仓库；下载、重定向、大小与摘要均由 Control 限制。', 20),
('ANTHROPIC_SKILLS', 'Anthropic Skills', 'CURATED_REPOSITORY', 'https://github.com',
 'https://github.com/anthropics/skills', 'OFFICIAL', 'ACTIVE', 0, 1, 1,
 'Anthropic 官方 Agent Skills 仓库；每个 Skill 的许可证和 Host 依赖仍需逐版本评审。', 30),
('VERCEL_AGENT_SKILLS', 'Vercel Agent Skills', 'CURATED_REPOSITORY', 'https://github.com',
 'https://github.com/vercel-labs/agent-skills', 'OFFICIAL', 'ACTIVE', 0, 1, 1,
 'Vercel 官方 Web 与工程类 Agent Skills 集合。', 40),
('JETBRAINS_SKILLS', 'JetBrains Skills', 'CURATED_REPOSITORY', 'https://github.com',
 'https://github.com/JetBrains/skills', 'VERIFIED', 'ACTIVE', 0, 1, 0,
 'ReachAI 精选的 JetBrains 公共 Skill 仓库；每次引入仍需锁定 commit 并经过独立评审。', 50),
('OPENAI_PLUGINS', 'OpenAI Plugins · Skills', 'CURATED_REPOSITORY', 'https://github.com',
 'https://github.com/openai/plugins', 'OFFICIAL', 'ACTIVE', 0, 1, 1,
 'OpenAI 当前 Codex 插件示例；仅导入其中标准 Skill 子树，MCP、App、Hook 不自动安装或授权。', 60);

CREATE TABLE IF NOT EXISTS `control_agent_skill` (
    `id`                 BIGINT        NOT NULL AUTO_INCREMENT,
    `scope_key`          VARCHAR(192)  NOT NULL COMMENT '平台安装范围键：USER:<id> / PROJECT:<code> / SHARED:* / PUBLIC:*',
    `publisher`          VARCHAR(64)   NOT NULL COMMENT '发布者命名空间；不改写标准 name',
    `standard_name`      VARCHAR(64)   NOT NULL COMMENT 'Agent Skills 标准 name，必须与包目录一致',
    `display_name`       VARCHAR(128)  NOT NULL COMMENT '面向用户展示名称',
    `description`        VARCHAR(1024) NOT NULL COMMENT '标准 description 与触发范围',
    `visibility`         VARCHAR(24)   NOT NULL DEFAULT 'PRIVATE' COMMENT 'PRIVATE / PROJECT / SHARED / PUBLIC',
    `owner_user_id`      BIGINT        DEFAULT NULL COMMENT 'PRIVATE Skill 所有者；引用 Control 平台用户逻辑 ID',
    `project_code`       VARCHAR(128)  DEFAULT NULL COMMENT 'PROJECT Skill 的治理项目范围',
    `status`             VARCHAR(24)   NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE / ARCHIVED',
    `latest_version_id`  BIGINT        DEFAULT NULL COMMENT '最近一次仍处于 PUBLISHED 的不可变版本',
    `default_version_id` BIGINT        DEFAULT NULL COMMENT '默认下载/绑定版本，只能指向 PUBLISHED',
    `created_by`         VARCHAR(128)  DEFAULT NULL,
    `updated_by`         VARCHAR(128)  DEFAULT NULL,
    `created_at`         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_agent_skill_identity` (`scope_key`, `publisher`, `standard_name`),
    KEY `idx_control_agent_skill_status` (`status`, `visibility`, `updated_at`),
    KEY `idx_control_agent_skill_scope` (`visibility`, `owner_user_id`, `project_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标准 Agent Skill 包稳定身份；不是 Capability 业务资产';

CREATE TABLE IF NOT EXISTS `control_agent_skill_version` (
    `id`                        BIGINT       NOT NULL AUTO_INCREMENT,
    `skill_id`                  BIGINT       NOT NULL,
    `version`                   VARCHAR(64)  NOT NULL,
    `status`                    VARCHAR(32)  NOT NULL DEFAULT 'REVIEW_PENDING' COMMENT 'REVIEW_PENDING / APPROVED / REJECTED / PUBLISHED / DEPRECATED / REVOKED',
    `source_type`               VARCHAR(24)  NOT NULL COMMENT 'UPLOAD / GIT / MARKET / BUILTIN',
    `source_ref`                VARCHAR(512) DEFAULT NULL COMMENT '非秘密来源标签、Git ref 或原始文件名',
    `source_sha256`             CHAR(64)     NOT NULL COMMENT '原始 ZIP 字节 SHA-256',
    `content_tree_sha256`       CHAR(64)     NOT NULL COMMENT '规范化文件树 SHA-256',
    `artifact_key`              VARCHAR(256) NOT NULL COMMENT 'SkillArtifactStore 内容寻址 key',
    `artifact_size`             BIGINT       NOT NULL,
    `declared_license`          VARCHAR(512) DEFAULT NULL,
    `declared_compatibility`    VARCHAR(500) DEFAULT NULL,
    `has_scripts`               TINYINT(1)   NOT NULL DEFAULT 0,
    `frontmatter_json`          JSON         NOT NULL,
    `package_manifest_json`     JSON         NOT NULL,
    `validation_report_json`    JSON         NOT NULL,
    `risk_report_json`          JSON         NOT NULL,
    `compatibility_report_json` JSON         NOT NULL,
    `reviewed_by`               VARCHAR(128) DEFAULT NULL,
    `reviewed_at`               DATETIME     DEFAULT NULL,
    `published_by`              VARCHAR(128) DEFAULT NULL,
    `published_at`              DATETIME     DEFAULT NULL,
    `created_by`                VARCHAR(128) DEFAULT NULL,
    `created_at`                DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`                DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_control_agent_skill_version` (`skill_id`, `version`),
    KEY `idx_control_agent_skill_version_status` (`skill_id`, `status`, `published_at`),
    KEY `idx_control_agent_skill_version_source_sha` (`source_sha256`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='不可变标准 Agent Skill 包版本、校验和兼容性快照';

CREATE TABLE IF NOT EXISTS `control_agent_skill_review` (
    `id`               BIGINT        NOT NULL AUTO_INCREMENT,
    `skill_id`         BIGINT        NOT NULL,
    `skill_version_id` BIGINT        NOT NULL,
    `decision`         VARCHAR(16)   NOT NULL COMMENT 'APPROVE / REJECT',
    `comment`          VARCHAR(2000) DEFAULT NULL,
    `findings_json`    JSON          DEFAULT NULL,
    `reviewer`         VARCHAR(128)  NOT NULL,
    `created_at`       DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_control_agent_skill_review_version` (`skill_version_id`, `created_at`),
    KEY `idx_control_agent_skill_review_skill` (`skill_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标准 Agent Skill 版本人工审核记录';

CREATE TABLE IF NOT EXISTS `control_market_item` (
    `id`                       BIGINT       NOT NULL AUTO_INCREMENT,
    `asset_kind`               VARCHAR(24)  NOT NULL,
    `asset_id`                 VARCHAR(128) NOT NULL,
    `asset_key`                VARCHAR(256) DEFAULT NULL,
    `project_id`               BIGINT       DEFAULT NULL,
    `project_code`             VARCHAR(96)  DEFAULT NULL,
    `name`                     VARCHAR(192) NOT NULL,
    `description`              VARCHAR(1024) DEFAULT NULL,
    `version`                  VARCHAR(64)  NOT NULL DEFAULT '1.0.0',
    `status`                   VARCHAR(32)  NOT NULL DEFAULT 'PENDING_APPROVAL',
    `visibility`               VARCHAR(24)  NOT NULL DEFAULT 'SHARED',
    `dependency_manifest_json` JSON         DEFAULT NULL,
    `snapshot_json`            JSON         DEFAULT NULL,
    `submitted_by`             VARCHAR(128) DEFAULT NULL,
    `approved_by`              VARCHAR(128) DEFAULT NULL,
    `approved_at`              DATETIME     DEFAULT NULL,
    `created_at`               DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `updated_at`               DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_market_status` (`asset_kind`, `status`),
    KEY `idx_market_project` (`project_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent 市场资产';

CREATE TABLE IF NOT EXISTS `capability_module` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `code`               VARCHAR(96)  NOT NULL,
    `name`               VARCHAR(192) NOT NULL,
    `version`            VARCHAR(64)  NOT NULL DEFAULT '1.0.0',
    `source_type`        VARCHAR(32)  NOT NULL DEFAULT 'BUILTIN',
    `status`             VARCHAR(32)  NOT NULL DEFAULT 'ACTIVE',
    `enabled`            TINYINT(1)   NOT NULL DEFAULT 1,
    `manifest_json`      MEDIUMTEXT   DEFAULT NULL,
    `config_schema_json` MEDIUMTEXT   DEFAULT NULL,
    `config_json`        MEDIUMTEXT   DEFAULT NULL,
    `create_time`        DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`        DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_capability_module_code` (`code`),
    KEY `idx_capability_module_status` (`status`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力模块/插件';

CREATE TABLE IF NOT EXISTS `capability_tool_asset` (
    `id`                   BIGINT       NOT NULL AUTO_INCREMENT,
    `capability_module_id` BIGINT       NOT NULL,
    `capability_code`      VARCHAR(96)  NOT NULL,
    `tool_code`            VARCHAR(128) NOT NULL,
    `name`                 VARCHAR(192) DEFAULT NULL,
    `qualified_name`       VARCHAR(256) NOT NULL,
    `description`          MEDIUMTEXT   DEFAULT NULL,
    `input_schema_json`    MEDIUMTEXT   DEFAULT NULL,
    `output_schema_json`   MEDIUMTEXT   DEFAULT NULL,
    `executor_type`        VARCHAR(32)  NOT NULL DEFAULT 'BEAN',
    `executor_ref`         VARCHAR(256) DEFAULT NULL,
    `side_effect`          VARCHAR(24)  NOT NULL DEFAULT 'WRITE',
    `enabled`              TINYINT(1)   NOT NULL DEFAULT 1,
    `create_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_tool_asset_qualified` (`qualified_name`),
    UNIQUE KEY `uk_tool_asset_module_code` (`capability_module_id`, `tool_code`),
    KEY `idx_tool_asset_capability` (`capability_code`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力模块下的原子工具资产';

CREATE TABLE IF NOT EXISTS `capability_composition_definition` (
    `id`                   BIGINT       NOT NULL AUTO_INCREMENT,
    `capability_module_id` BIGINT       NOT NULL,
    `capability_code`      VARCHAR(96)  NOT NULL,
    `composition_code`     VARCHAR(128) NOT NULL,
    `name`                 VARCHAR(192) DEFAULT NULL,
    `qualified_name`       VARCHAR(256) NOT NULL,
    `description`          MEDIUMTEXT   DEFAULT NULL,
    `graph_spec_json`      MEDIUMTEXT   NOT NULL,
    `input_schema_json`    MEDIUMTEXT   DEFAULT NULL,
    `output_schema_json`   MEDIUMTEXT   DEFAULT NULL,
    `side_effect`          VARCHAR(24)  NOT NULL DEFAULT 'WRITE',
    `enabled`              TINYINT(1)   NOT NULL DEFAULT 1,
    `create_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_composition_qualified` (`qualified_name`),
    UNIQUE KEY `uk_composition_module_code` (`capability_module_id`, `composition_code`),
    KEY `idx_composition_capability` (`capability_code`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能力模块下的组合定义';

CREATE TABLE IF NOT EXISTS `capability_interaction_definition` (
    `id`                   BIGINT       NOT NULL AUTO_INCREMENT,
    `capability_module_id` BIGINT       NOT NULL,
    `capability_code`      VARCHAR(96)  NOT NULL,
    `interaction_code`     VARCHAR(128) NOT NULL,
    `name`                 VARCHAR(192) DEFAULT NULL,
    `qualified_name`       VARCHAR(256) NOT NULL,
    `description`          MEDIUMTEXT   DEFAULT NULL,
    `interaction_type`     VARCHAR(32)  NOT NULL DEFAULT 'COLLECT_INPUT',
    `spec_json`            MEDIUMTEXT   DEFAULT NULL,
    `input_schema_json`    MEDIUMTEXT   DEFAULT NULL,
    `output_schema_json`   MEDIUMTEXT   DEFAULT NULL,
    `enabled`              TINYINT(1)   NOT NULL DEFAULT 1,
    `create_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`          DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_interaction_qualified` (`qualified_name`),
    UNIQUE KEY `uk_interaction_module_code` (`capability_module_id`, `interaction_code`),
    KEY `idx_interaction_capability` (`capability_code`, `enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Interaction definition assets';

CREATE TABLE IF NOT EXISTS `runtime_interaction_session` (
    `id`                         VARCHAR(64)  NOT NULL COMMENT '运行时 interactionId',
    `source_type`                VARCHAR(32)  NOT NULL DEFAULT 'WORKFLOW' COMMENT 'WORKFLOW / COMPOSITION / DEBUG / SUPERVISOR_POLICY / MANAGED_EXECUTOR',
    `agent_id`                   VARCHAR(64)  DEFAULT NULL COMMENT '关联 Runtime Agent；Supervisor 审批查询键',
    `run_id`                     VARCHAR(64)  DEFAULT NULL COMMENT '关联 runtime_run / 调试 runId',
    `trace_id`                   VARCHAR(64)  DEFAULT NULL COMMENT '关联 root traceId',
    `workflow_id`                VARCHAR(64)  DEFAULT NULL COMMENT 'Workflow ID',
    `workflow_version_id`        BIGINT       DEFAULT NULL COMMENT '已发布 Workflow 版本 ID',
    `composition_qualified_name` VARCHAR(256) DEFAULT NULL COMMENT '历史 composition 兼容字段；GraphSpec-native 可为空',
    `graph_spec_snapshot_json`   MEDIUMTEXT   DEFAULT NULL COMMENT '暂停时 GraphSpec 快照，恢复不得读最新版',
    `node_id`                    VARCHAR(128) NOT NULL COMMENT '当前等待节点或 Supervisor Tool 名称',
    `interaction_type`           VARCHAR(32)  NOT NULL COMMENT 'COLLECT_INPUT / USER_CHOICE / CONFIRM_ACTION / PRESENT_OUTPUT / CUSTOM',
    `status`                     VARCHAR(32)  NOT NULL DEFAULT 'WAITING_USER' COMMENT 'WAITING_USER / RESUMING / COMPLETED / CANCELLED / EXPIRED / FAILED',
    `revision`                   INT          NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
    `idempotency_key`            VARCHAR(128) DEFAULT NULL COMMENT '当前恢复尝试的幂等键',
    `resume_checkpoint_json`     MEDIUMTEXT   DEFAULT NULL COMMENT '版本化内部断点 envelope；不得作为公共结果返回',
    `checkpoint_schema_version`  SMALLINT     NOT NULL DEFAULT 0 COMMENT '0=历史 Map；1=WorkflowCheckpointV1',
    `execution_engine_version`   VARCHAR(32)  NOT NULL DEFAULT 'LEGACY' COMMENT 'LEGACY / RUNTIME_KERNEL_V2',
    `checkpoint_digest`          CHAR(64)     DEFAULT NULL COMMENT 'resume_checkpoint_json 原始 UTF-8 字节 SHA-256',
    `checkpoint_size_bytes`      INT          DEFAULT NULL COMMENT '断点 UTF-8 字节数；Runtime V1 上限 1 MiB',
    `ui_request_json`            MEDIUMTEXT   DEFAULT NULL COMMENT 'canonical uiRequest',
    `submitted_payload_json`     MEDIUMTEXT   DEFAULT NULL COMMENT '最近一次提交（已脱敏策略由服务层保证）',
    `result_json`                MEDIUMTEXT   DEFAULT NULL COMMENT '可幂等重放的完整恢复结果',
    `continuation_json`          MEDIUMTEXT   DEFAULT NULL COMMENT 'Supervisor 续跑信息',
    `app_id`                     VARCHAR(96)  DEFAULT NULL,
    `tenant_id`                  VARCHAR(96)  DEFAULT NULL,
    `session_id`                 VARCHAR(128) DEFAULT NULL COMMENT 'chat / embed session',
    `user_id`                    VARCHAR(128) DEFAULT NULL,
    `create_time`                DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`                DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `expires_at`                 DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_interaction_session_run` (`run_id`),
    KEY `idx_interaction_session_trace` (`trace_id`),
    KEY `idx_interaction_session_status` (`status`, `expires_at`),
    KEY `idx_interaction_session_owner` (`session_id`, `app_id`, `status`),
    KEY `idx_interaction_session_agent` (`source_type`, `interaction_type`, `agent_id`, `status`, `create_time`),
    KEY `idx_interaction_session_idem` (`id`, `idempotency_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime interaction sessions';

CREATE TABLE IF NOT EXISTS `runtime_interaction_event` (
    `id`           BIGINT       NOT NULL AUTO_INCREMENT,
    `session_id`   VARCHAR(64)  NOT NULL COMMENT 'runtime_interaction_session.id',
    `event_type`   VARCHAR(32)  NOT NULL COMMENT 'CREATED / REQUESTED / SUBMITTED / RESUMING / RESUMED / COMPLETED / CANCELLED / EXPIRED / FAILED',
    `payload_json` MEDIUMTEXT   DEFAULT NULL COMMENT '脱敏后事件载荷，禁止 secret/token 原文',
    `operator_id`  VARCHAR(128) DEFAULT NULL,
    `create_time`  DATETIME     DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_interaction_event_session` (`session_id`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Interaction runtime events';

CREATE TABLE IF NOT EXISTS `runtime_executable_debug_session` (
    `id`                    VARCHAR(64)  NOT NULL,
    `run_id`                VARCHAR(128) DEFAULT NULL,
    `trace_id`              VARCHAR(128) DEFAULT NULL,
    `target_type`           VARCHAR(64)  NOT NULL DEFAULT 'AGENT_WORKING_COPY' COMMENT 'AGENT_WORKING_COPY / WORKFLOW_WORKING_COPY / WORKFLOW_VERSION 等',
    `status`                VARCHAR(32)  NOT NULL DEFAULT 'RUNNING' COMMENT 'RUNNING / SUSPENDED / RESUMING / COMPLETED / FAILED / CANCELLED / EXPIRED',
    `revision`              INT          NOT NULL DEFAULT 0 COMMENT '乐观锁版本，Debug submit CAS',
    `idempotency_key`       VARCHAR(128) DEFAULT NULL COMMENT '最近一次成功提交的幂等键',
    `submitted_payload_json` MEDIUMTEXT  DEFAULT NULL COMMENT '规范化提交载荷，用于幂等比较',
    `result_json`           MEDIUMTEXT   DEFAULT NULL COMMENT '最近一次 submit 结果摘要',
    `current_node_id`       VARCHAR(128) DEFAULT NULL,
    `working_copy_definition_json` MEDIUMTEXT DEFAULT NULL COMMENT '调试启动时的可编辑工作副本快照',
    `debug_options_json`    MEDIUMTEXT   DEFAULT NULL,
    `state_snapshot_json`   MEDIUMTEXT   DEFAULT NULL COMMENT '调试会话当前状态快照；恢复断点只在 Runtime 内部使用',
    `messages_json`         MEDIUMTEXT   DEFAULT NULL,
    `steps_json`            MEDIUMTEXT   DEFAULT NULL,
    `ui_request_json`       MEDIUMTEXT   DEFAULT NULL,
    `create_time`           DATETIME     DEFAULT CURRENT_TIMESTAMP,
    `update_time`           DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `expires_at`            DATETIME     DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_executable_debug_session_trace` (`trace_id`),
    KEY `idx_executable_debug_session_status` (`status`, `expires_at`),
    KEY `idx_executable_debug_session_idem` (`id`, `idempotency_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Executable debug sessions for Studio and runtime workbench';

INSERT INTO `capability_module`
(`code`, `name`, `version`, `source_type`, `status`, `enabled`, `manifest_json`, `config_schema_json`, `config_json`)
VALUES
('system', '系统内置能力', '1.0.0', 'BUILTIN', 'ACTIVE', 1,
 '{"code":"system","name":"系统内置能力"}', '{}', '{}')
ON DUPLICATE KEY UPDATE
    `name` = VALUES(`name`),
    `version` = VALUES(`version`),
    `source_type` = VALUES(`source_type`),
    `status` = VALUES(`status`),
    `enabled` = VALUES(`enabled`);

INSERT INTO `capability_tool_asset`
(`capability_module_id`, `capability_code`, `tool_code`, `name`, `qualified_name`, `description`,
 `input_schema_json`, `output_schema_json`, `executor_type`, `executor_ref`, `side_effect`, `enabled`)
VALUES
((SELECT `id` FROM `capability_module` WHERE `code` = 'system' LIMIT 1),
 'system', 'echo', '回声工具', 'system.echo', '用于能力内核验证的回声工具',
 '{"type":"object","properties":{"message":{"type":"string"}}}', '{"type":"object"}',
 'ECHO', 'echo', 'READ', 1)
ON DUPLICATE KEY UPDATE
    `name` = VALUES(`name`),
    `description` = VALUES(`description`),
    `executor_type` = VALUES(`executor_type`),
    `executor_ref` = VALUES(`executor_ref`),
    `enabled` = VALUES(`enabled`);

INSERT INTO `capability_composition_definition`
(`capability_module_id`, `capability_code`, `composition_code`, `name`, `qualified_name`, `description`,
 `graph_spec_json`, `input_schema_json`, `output_schema_json`, `side_effect`, `enabled`)
VALUES
((SELECT `id` FROM `capability_module` WHERE `code` = 'system' LIMIT 1),
 'system', 'echo_flow', '回声组合', 'system.echo_flow', '用于能力内核验证的最小图组合',
 '{"schemaVersion":2,"entryNodeId":"input","exitNodeIds":["answer"],"nodes":[{"id":"input","type":"USER_INPUT","config":{"fields":[{"name":"message","required":true}]}},{"id":"echo","type":"TOOL","config":{"qualifiedName":"system.echo","inputMapping":{"message":"params.message"},"outputAlias":"echoed"}},{"id":"answer","type":"ANSWER","config":{"template":"{{ echoed.message }}"}}],"edges":[{"from":"input","to":"echo","condition":"always"},{"from":"echo","to":"answer","condition":"success"}]}',
 '{"type":"object","properties":{"message":{"type":"string"}}}', '{"type":"string"}',
 'READ', 1)
ON DUPLICATE KEY UPDATE
    `name` = VALUES(`name`),
    `description` = VALUES(`description`),
    `graph_spec_json` = VALUES(`graph_spec_json`),
    `enabled` = VALUES(`enabled`);

INSERT INTO `capability_interaction_definition`
(`capability_module_id`, `capability_code`, `interaction_code`, `name`, `qualified_name`, `description`,
 `interaction_type`, `spec_json`, `input_schema_json`, `output_schema_json`, `enabled`)
VALUES
((SELECT `id` FROM `capability_module` WHERE `code` = 'system' LIMIT 1),
 'system', 'echo_input', '回声输入交互', 'system.echo_input', '调用回声工具前采集用户输入',
 'COLLECT_INPUT',
 '{"interactionType":"COLLECT_INPUT","title":"Echo Input","fields":[{"key":"message","name":"message","label":"Message","type":"string","required":true}],"behavior":{"askPolicy":"MISSING_ONLY"}}',
 '{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}', '{"type":"object"}', 1),
((SELECT `id` FROM `capability_module` WHERE `code` = 'system' LIMIT 1),
 'system', 'echo_result', '回声结果展示', 'system.echo_result', '以详情卡片展示回声结果',
 'PRESENT_OUTPUT',
 '{"interactionType":"PRESENT_OUTPUT","title":"Echo Result","component":"DETAIL","data":"echoed"}',
 '{"type":"object"}', '{"type":"object"}', 1)
ON DUPLICATE KEY UPDATE
    `name` = VALUES(`name`),
    `description` = VALUES(`description`),
    `interaction_type` = VALUES(`interaction_type`),
    `spec_json` = VALUES(`spec_json`),
    `enabled` = VALUES(`enabled`);

INSERT INTO `capability_composition_definition`
(`capability_module_id`, `capability_code`, `composition_code`, `name`, `qualified_name`, `description`,
 `graph_spec_json`, `input_schema_json`, `output_schema_json`, `side_effect`, `enabled`)
VALUES
((SELECT `id` FROM `capability_module` WHERE `code` = 'system' LIMIT 1),
 'system', 'interactive_echo', '交互式回声组合', 'system.interactive_echo', '用于验证交互内核的示例组合',
 '{"schemaVersion":2,"entryNodeId":"collect","exitNodeIds":["answer"],"nodes":[{"id":"collect","type":"INTERACTION","ref":{"kind":"INTERACTION","qualifiedName":"system.echo_input"},"config":{"interactionType":"COLLECT_INPUT","outputAlias":"params"}},{"id":"echo","type":"TOOL","config":{"qualifiedName":"system.echo","inputMapping":{"message":"params.message"},"outputAlias":"echoed"}},{"id":"show","type":"INTERACTION","ref":{"kind":"INTERACTION","qualifiedName":"system.echo_result"},"config":{"interactionType":"PRESENT_OUTPUT","data":"echoed","outputAlias":"presented"}},{"id":"answer","type":"ANSWER","config":{"template":"{{ echoed.message }}"}}],"edges":[{"from":"collect","to":"echo","condition":"always"},{"from":"echo","to":"show","condition":"success"},{"from":"show","to":"answer","condition":"always"}]}',
 '{"type":"object","properties":{"message":{"type":"string"}}}', '{"type":"string"}',
 'READ', 1)
ON DUPLICATE KEY UPDATE
    `name` = VALUES(`name`),
    `description` = VALUES(`description`),
    `graph_spec_json` = VALUES(`graph_spec_json`),
    `enabled` = VALUES(`enabled`);


-- ============================================================================
-- 八、初始化示例数据（可选；同名再跑不会插入重复行）
-- ============================================================================

-- ============================================================================
-- 九、模型实例绑定（历史 v11，model_instance_binding_v11）
-- ============================================================================

CALL add_col_if_absent('knowledge_base', 'embedding_model_instance_id',
    'VARCHAR(64) DEFAULT NULL COMMENT ''Embedding model instance id'' AFTER `description`');
CALL add_col_if_absent('knowledge_base', 'rerank_model_instance_id',
    'VARCHAR(64) DEFAULT NULL COMMENT ''Rerank model instance id'' AFTER `embedding_model_instance_id`');
CALL add_col_if_absent('knowledge_base', 'llm_model_instance_id',
    'VARCHAR(64) DEFAULT NULL COMMENT ''LLM model instance id for answer generation'' AFTER `rerank_model_instance_id`');
CALL add_col_if_absent('knowledge_business_index', 'embedding_model_instance_id',
    'VARCHAR(64) DEFAULT NULL COMMENT ''Embedding model instance id'' AFTER `field_schema`');

CALL add_idx_if_absent('knowledge_base', 'idx_kb_embedding_instance', '`embedding_model_instance_id`');
CALL add_idx_if_absent('knowledge_base', 'idx_kb_rerank_instance', '`rerank_model_instance_id`');
CALL add_idx_if_absent('knowledge_base', 'idx_kb_llm_instance', '`llm_model_instance_id`');
CALL add_idx_if_absent('knowledge_business_index', 'idx_biz_embedding_instance', '`embedding_model_instance_id`');


-- ============================================================================
-- ????????? AI Coding onboarding ?????V2 ?????
-- ============================================================================

-- AI Coding onboarding access key for external coding tools.
-- CapabilityScanProjectCatalogService generates and enables this key for newly
-- created projects. Keep the SQL default disabled so direct inserts without a
-- key do not accidentally expose a keyless AI Coding surface.
CALL add_col_if_absent('capability_scan_project', 'ai_coding_access_key', 'VARCHAR(160) DEFAULT NULL COMMENT ''AI Coding access key''');
CALL add_col_if_absent('capability_scan_project', 'ai_coding_access_enabled', 'TINYINT NOT NULL DEFAULT 0 COMMENT ''1=AI Coding manifest access enabled''');








-- ============================================================================
-- 十、API 市场（外部 API 目录与项目接入）
-- Owning service: reachai-capability-service
-- 安全边界：目录与项目接入不保存 API Key、Token 或 OAuth 凭据。
-- ============================================================================

CREATE TABLE IF NOT EXISTS `capability_external_api_source` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `source_key`      VARCHAR(96)  NOT NULL COMMENT '稳定来源键',
    `name`            VARCHAR(192) NOT NULL,
    `source_type`     VARCHAR(32)  NOT NULL COMMENT 'OFFICIAL / OPENAPI_DIRECTORY / CURATED_DIRECTORY / MANUAL',
    `source_url`      VARCHAR(1000) DEFAULT NULL COMMENT '机器来源或原始清单地址',
    `homepage_url`    VARCHAR(1000) DEFAULT NULL,
    `description`     VARCHAR(1000) DEFAULT NULL,
    `sync_strategy`   VARCHAR(32)  NOT NULL DEFAULT 'MANUAL_REVIEW',
    `trust_level`     VARCHAR(24)  NOT NULL DEFAULT 'COMMUNITY',
    `status`          VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `last_synced_at`  DATETIME     DEFAULT NULL,
    `created_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_api_source_key` (`source_key`),
    KEY `idx_external_api_source_status` (`status`, `source_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 目录来源，不保存调用凭据';

CREATE TABLE IF NOT EXISTS `capability_external_api_provider` (
    `id`              BIGINT       NOT NULL AUTO_INCREMENT,
    `provider_key`    VARCHAR(128) NOT NULL,
    `name`            VARCHAR(192) NOT NULL,
    `homepage_url`    VARCHAR(1000) DEFAULT NULL,
    `logo_url`        VARCHAR(1000) DEFAULT NULL,
    `verified`        TINYINT(1)   NOT NULL DEFAULT 0 COMMENT '供应商身份信息是否已核对',
    `status`          VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_api_provider_key` (`provider_key`),
    KEY `idx_external_api_provider_status` (`status`, `verified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 提供方';

CREATE TABLE IF NOT EXISTS `capability_external_api_entry` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT,
    `entry_key`             VARCHAR(160) NOT NULL COMMENT '跨来源稳定 API 标识',
    `provider_id`           BIGINT       NOT NULL,
    `source_id`             BIGINT       NOT NULL,
    `title`                 VARCHAR(192) NOT NULL,
    `summary`               VARCHAR(512) NOT NULL,
    `description`           TEXT         DEFAULT NULL,
    `category_code`         VARCHAR(64)  NOT NULL DEFAULT 'OTHER',
    `tags_json`             VARCHAR(2000) DEFAULT NULL,
    `auth_type`             VARCHAR(32)  NOT NULL DEFAULT 'NONE',
    `pricing_type`          VARCHAR(24)  NOT NULL DEFAULT 'UNKNOWN' COMMENT 'FREE / FREE_TIER / PAID / UNKNOWN',
    `https_supported`       TINYINT(1)   NOT NULL DEFAULT 1,
    `cors_policy`           VARCHAR(24)  DEFAULT NULL,
    `docs_url`              VARCHAR(1000) NOT NULL,
    `terms_url`             VARCHAR(1000) DEFAULT NULL,
    `source_entry_key`      VARCHAR(512) DEFAULT NULL,
    `source_category`       VARCHAR(128) DEFAULT NULL,
    `publication_status`    VARCHAR(24)  NOT NULL DEFAULT 'DRAFT',
    `verification_status`   VARCHAR(24)  NOT NULL DEFAULT 'UNVERIFIED',
    `spec_status`           VARCHAR(24)  NOT NULL DEFAULT 'NONE',
    `featured`              TINYINT(1)   NOT NULL DEFAULT 0,
    `popularity_score`      INT          NOT NULL DEFAULT 0,
    `last_verified_at`      DATETIME     DEFAULT NULL,
    `created_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_api_entry_key` (`entry_key`),
    KEY `idx_external_api_entry_discovery` (`publication_status`, `featured`, `popularity_score`),
    KEY `idx_external_api_entry_category` (`category_code`, `publication_status`),
    KEY `idx_external_api_entry_quality` (`verification_status`, `spec_status`),
    KEY `idx_external_api_entry_provider` (`provider_id`),
    KEY `idx_external_api_entry_source` (`source_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API 市场外部 API 目录条目';

CREATE TABLE IF NOT EXISTS `capability_external_api_version` (
    `id`                  BIGINT       NOT NULL AUTO_INCREMENT,
    `entry_id`            BIGINT       NOT NULL,
    `version_key`         VARCHAR(128) NOT NULL,
    `base_url`            VARCHAR(1000) NOT NULL,
    `openapi_url`         VARCHAR(1000) DEFAULT NULL,
    `spec_hash`           VARCHAR(128) DEFAULT NULL COMMENT '规范化契约摘要；不可用时为空',
    `raw_metadata_json`   MEDIUMTEXT   DEFAULT NULL COMMENT '来源元数据快照，不含秘密',
    `publication_status`  VARCHAR(24)  NOT NULL DEFAULT 'DRAFT',
    `published_at`        DATETIME     DEFAULT NULL,
    `deprecated_at`       DATETIME     DEFAULT NULL,
    `created_at`          DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`          DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_api_version` (`entry_id`, `version_key`),
    KEY `idx_external_api_version_status` (`entry_id`, `publication_status`, `published_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 不可变目录版本';

CREATE TABLE IF NOT EXISTS `capability_external_api_operation` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT,
    `version_id`            BIGINT       NOT NULL,
    `operation_key`         VARCHAR(160) NOT NULL,
    `operation_id`          VARCHAR(256) DEFAULT NULL,
    `title`                 VARCHAR(256) NOT NULL,
    `description`           TEXT         DEFAULT NULL,
    `http_method`           VARCHAR(16)  NOT NULL,
    `path`                  VARCHAR(1000) NOT NULL,
    `side_effect`           VARCHAR(32)  NOT NULL DEFAULT 'WRITE',
    `auth_required`         TINYINT(1)   NOT NULL DEFAULT 0,
    `request_schema_json`   MEDIUMTEXT   DEFAULT NULL,
    `response_schema_json`  MEDIUMTEXT   DEFAULT NULL,
    `example_params_json`   MEDIUMTEXT   DEFAULT NULL COMMENT '仅示例业务参数，不允许出现凭据',
    `status`                VARCHAR(24)  NOT NULL DEFAULT 'ACTIVE',
    `created_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_external_api_operation` (`version_id`, `operation_key`),
    KEY `idx_external_api_operation_status` (`version_id`, `status`),
    KEY `idx_external_api_operation_method` (`http_method`, `side_effect`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 版本下的可接入 Operation';

CREATE TABLE IF NOT EXISTS `capability_external_api_verification` (
    `id`                BIGINT       NOT NULL AUTO_INCREMENT,
    `entry_id`          BIGINT       NOT NULL,
    `version_id`        BIGINT       DEFAULT NULL,
    `verification_type` VARCHAR(32)  NOT NULL COMMENT 'CONNECTIVITY / DOCUMENT / SPEC',
    `status`            VARCHAR(24)  NOT NULL,
    `http_status`       INT          DEFAULT NULL,
    `latency_ms`        BIGINT       DEFAULT NULL,
    `checked_url`       VARCHAR(1000) DEFAULT NULL,
    `evidence_summary`  VARCHAR(1000) DEFAULT NULL COMMENT '禁止保存响应正文和凭据',
    `checked_at`        DATETIME     NOT NULL,
    `created_at`        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_external_api_verification_entry` (`entry_id`, `checked_at`),
    KEY `idx_external_api_verification_status` (`status`, `checked_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 质量验证证据摘要';

CREATE TABLE IF NOT EXISTS `capability_external_api_sync_run` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `source_id`          BIGINT       NOT NULL,
    `source_revision`    VARCHAR(256) DEFAULT NULL,
    `status`             VARCHAR(24)  NOT NULL,
    `discovered_count`   INT          NOT NULL DEFAULT 0,
    `changed_count`      INT          NOT NULL DEFAULT 0,
    `published_count`    INT          NOT NULL DEFAULT 0,
    `failed_count`       INT          NOT NULL DEFAULT 0,
    `error_summary`      VARCHAR(1000) DEFAULT NULL,
    `started_at`         DATETIME     NOT NULL,
    `finished_at`        DATETIME     DEFAULT NULL,
    `created_at`         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_external_api_sync_source` (`source_id`, `started_at`),
    KEY `idx_external_api_sync_status` (`status`, `started_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部 API 来源同步批次；同步结果需审核后发布';

CREATE TABLE IF NOT EXISTS `capability_project_external_api` (
    `id`            BIGINT       NOT NULL AUTO_INCREMENT,
    `project_id`    BIGINT       NOT NULL,
    `project_code`  VARCHAR(96)  NOT NULL,
    `entry_id`      BIGINT       NOT NULL,
    `version_id`    BIGINT       NOT NULL,
    `environment`   VARCHAR(32)  NOT NULL DEFAULT 'DEVELOPMENT',
    `status`        VARCHAR(24)  NOT NULL DEFAULT 'CONFIGURING',
    `note`          VARCHAR(512) DEFAULT NULL,
    `created_at`    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project_external_api` (`project_id`, `entry_id`, `environment`),
    KEY `idx_project_external_api_project` (`project_id`, `status`),
    KEY `idx_project_external_api_entry` (`entry_id`, `status`),
    KEY `idx_project_external_api_version` (`version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目对外部 API 的接入意图；不保存任何调用凭据';

CREATE TABLE IF NOT EXISTS `capability_project_external_api_operation` (
    `id`             BIGINT   NOT NULL AUTO_INCREMENT,
    `integration_id` BIGINT   NOT NULL,
    `operation_id`   BIGINT   NOT NULL,
    `created_at`     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_project_external_api_operation` (`integration_id`, `operation_id`),
    KEY `idx_project_external_api_operation_ref` (`operation_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目接入选中的外部 API Operation';

-- API 市场首批目录种子：只保存公开元数据和只读验证摘要，启动、建库均不依赖外网。
INSERT INTO `capability_external_api_source`
(`source_key`, `name`, `source_type`, `source_url`, `homepage_url`, `description`, `sync_strategy`, `trust_level`, `status`, `last_synced_at`)
VALUES
('public-apis', 'public-apis 社区目录', 'CURATED_DIRECTORY', 'https://github.com/public-apis/public-apis', 'https://github.com/public-apis/public-apis', '人工维护的公开 API 发现清单，适合提供分类、认证和文档入口，运行前仍需官方证据。', 'CURATED_IMPORT', 'COMMUNITY', 'ACTIVE', '2026-08-23 21:20:00'),
('apis-guru', 'APIs.guru OpenAPI Directory', 'OPENAPI_DIRECTORY', 'https://github.com/APIs-guru/openapi-directory', 'https://apis.guru/browse-apis/', '机器可读 OpenAPI 目录；来源定义仍受对应 API 提供方条款约束。', 'OPENAPI_IMPORT', 'CURATED', 'ACTIVE', '2026-08-23 21:20:00'),
('official-docs', '供应商官方文档', 'OFFICIAL', NULL, NULL, '由平台维护者依据供应商官方文档建立并审核的条目。', 'MANUAL_REVIEW', 'OFFICIAL', 'ACTIVE', '2026-08-23 21:20:00')
ON DUPLICATE KEY UPDATE
`name` = VALUES(`name`), `source_type` = VALUES(`source_type`), `source_url` = VALUES(`source_url`),
`homepage_url` = VALUES(`homepage_url`), `description` = VALUES(`description`),
`sync_strategy` = VALUES(`sync_strategy`), `trust_level` = VALUES(`trust_level`), `status` = VALUES(`status`);

INSERT INTO `capability_external_api_provider`
(`provider_key`, `name`, `homepage_url`, `verified`, `status`)
VALUES
('open-meteo', 'Open-Meteo', 'https://open-meteo.com/', 1, 'ACTIVE'),
('rest-countries', 'REST Countries', 'https://restcountries.com/', 1, 'ACTIVE'),
('github', 'GitHub', 'https://github.com/', 1, 'ACTIVE'),
('open-library', 'Open Library', 'https://openlibrary.org/', 1, 'ACTIVE'),
('nager-date', 'Nager.Date', 'https://date.nager.at/', 1, 'ACTIVE'),
('nasa', 'NASA', 'https://api.nasa.gov/', 1, 'ACTIVE')
ON DUPLICATE KEY UPDATE
`name` = VALUES(`name`), `homepage_url` = VALUES(`homepage_url`),
`verified` = VALUES(`verified`), `status` = VALUES(`status`);

INSERT INTO `capability_external_api_entry`
(`entry_key`, `provider_id`, `source_id`, `title`, `summary`, `description`, `category_code`, `tags_json`, `auth_type`, `pricing_type`, `https_supported`, `cors_policy`, `docs_url`, `terms_url`, `source_entry_key`, `source_category`, `publication_status`, `verification_status`, `spec_status`, `featured`, `popularity_score`, `last_verified_at`)
VALUES
('open-meteo', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='open-meteo'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='public-apis'), 'Open-Meteo', '按经纬度查询全球实时天气与预报，无需 API Key。', '适合天气助手、出行规划、现场作业提醒等只读场景。调用前需确认业务所在地区和供应商使用条款。', 'WEATHER', '["天气","预报","地理"]', 'NONE', 'FREE', 1, 'YES', 'https://open-meteo.com/en/docs', 'https://open-meteo.com/en/terms', 'Open-Meteo', 'Weather', 'PUBLISHED', 'VERIFIED', 'NONE', 1, 98, '2026-08-23 21:20:00'),
('rest-countries', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='rest-countries'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='public-apis'), 'REST Countries', '按名称查询国家、地区代码、首都和基础地理信息。', '适合地址标准化、国际化辅助和公共数据查询。', 'GEO', '["国家","地理","公共数据"]', 'NONE', 'FREE', 1, 'YES', 'https://restcountries.com/', NULL, 'REST Countries', 'Geocoding', 'PUBLISHED', 'VERIFIED', 'NONE', 1, 91, '2026-08-23 21:20:00'),
('github-rest', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='github'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='official-docs'), 'GitHub REST API', '查询仓库元数据、Issue、Release 等开发协作信息。', '首期收录仓库详情只读 Operation。匿名调用有较低限流，生产使用建议配置 Bearer Token。', 'DEVELOPER', '["GitHub","代码仓库","研发效能"]', 'BEARER', 'FREE_TIER', 1, 'YES', 'https://docs.github.com/en/rest', 'https://docs.github.com/en/site-policy/github-terms/github-terms-of-service', 'repos/get', 'Development', 'PUBLISHED', 'VERIFIED', 'VALID', 1, 97, '2026-08-23 21:20:00'),
('open-library-search', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='open-library'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='public-apis'), 'Open Library Search', '检索书籍、作者与出版信息。', '适合阅读推荐和公共书目查询。当前环境 TLS 连通性尚未完成验证。', 'DATA', '["图书","检索","公共数据"]', 'NONE', 'FREE', 1, 'YES', 'https://openlibrary.org/dev/docs/api/search', 'https://archive.org/about/terms.php', 'Open Library', 'Books', 'PUBLISHED', 'UNVERIFIED', 'NONE', 0, 76, NULL),
('nager-date', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='nager-date'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='apis-guru'), 'Nager.Date', '按国家和年份查询公共节假日。', '适合排班、工期计算和日历提醒。不同国家覆盖范围以官方文档为准。', 'DATA', '["节假日","日历","排班"]', 'NONE', 'FREE', 1, 'YES', 'https://date.nager.at/Api', 'https://date.nager.at/Legal/TermsOfService', 'date.nager.at', 'Calendar', 'PUBLISHED', 'VERIFIED', 'VALID', 1, 88, '2026-08-23 21:20:00'),
('nasa-apod', (SELECT `id` FROM `capability_external_api_provider` WHERE `provider_key`='nasa'), (SELECT `id` FROM `capability_external_api_source` WHERE `source_key`='public-apis'), 'NASA Astronomy Picture of the Day', '查询 NASA 每日天文图片及说明。', '需要 API Key；市场不会保存示例 Key，接入后应在 Runtime 凭据中配置。', 'SCIENCE', '["NASA","天文","图片"]', 'API_KEY_QUERY', 'FREE_TIER', 1, 'YES', 'https://api.nasa.gov/', 'https://www.nasa.gov/nasa-web-privacy-policy-and-important-notices/', 'NASA APOD', 'Science & Math', 'PUBLISHED', 'UNVERIFIED', 'NONE', 0, 82, NULL)
ON DUPLICATE KEY UPDATE
`provider_id` = VALUES(`provider_id`), `source_id` = VALUES(`source_id`), `title` = VALUES(`title`),
`summary` = VALUES(`summary`), `description` = VALUES(`description`), `category_code` = VALUES(`category_code`),
`tags_json` = VALUES(`tags_json`), `auth_type` = VALUES(`auth_type`), `pricing_type` = VALUES(`pricing_type`),
`https_supported` = VALUES(`https_supported`), `cors_policy` = VALUES(`cors_policy`), `docs_url` = VALUES(`docs_url`),
`terms_url` = VALUES(`terms_url`), `source_entry_key` = VALUES(`source_entry_key`),
`source_category` = VALUES(`source_category`), `publication_status` = VALUES(`publication_status`),
`verification_status` = VALUES(`verification_status`), `spec_status` = VALUES(`spec_status`),
`featured` = VALUES(`featured`), `popularity_score` = VALUES(`popularity_score`),
`last_verified_at` = VALUES(`last_verified_at`);

INSERT INTO `capability_external_api_version`
(`entry_id`, `version_key`, `base_url`, `openapi_url`, `spec_hash`, `raw_metadata_json`, `publication_status`, `published_at`)
VALUES
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='open-meteo'), 'v1', 'https://api.open-meteo.com', NULL, NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"manual-review"}', 'PUBLISHED', '2026-08-23 21:20:00'),
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='rest-countries'), 'v3.1', 'https://restcountries.com', NULL, NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"manual-review"}', 'PUBLISHED', '2026-08-23 21:20:00'),
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='github-rest'), '2022-11-28', 'https://api.github.com', 'https://github.com/github/rest-api-description', NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"official-docs"}', 'PUBLISHED', '2026-08-23 21:20:00'),
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='open-library-search'), 'v1', 'https://openlibrary.org', NULL, NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"manual-review"}', 'PUBLISHED', '2026-08-23 21:20:00'),
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='nager-date'), 'v3', 'https://date.nager.at', 'https://date.nager.at/swagger/v3/swagger.json', NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"apis-guru-plus-official"}', 'PUBLISHED', '2026-08-23 21:20:00'),
((SELECT `id` FROM `capability_external_api_entry` WHERE `entry_key`='nasa-apod'), 'v1', 'https://api.nasa.gov', NULL, NULL, '{"catalogSeed":"2026-08-23","sourceRevision":"manual-review"}', 'PUBLISHED', '2026-08-23 21:20:00')
ON DUPLICATE KEY UPDATE
`base_url` = VALUES(`base_url`), `openapi_url` = VALUES(`openapi_url`), `spec_hash` = VALUES(`spec_hash`),
`raw_metadata_json` = VALUES(`raw_metadata_json`), `publication_status` = VALUES(`publication_status`),
`published_at` = VALUES(`published_at`);

INSERT INTO `capability_external_api_operation`
(`version_id`, `operation_key`, `operation_id`, `title`, `description`, `http_method`, `path`, `side_effect`, `auth_required`, `request_schema_json`, `response_schema_json`, `example_params_json`, `status`)
VALUES
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='open-meteo' AND v.`version_key`='v1'), 'forecast', 'getForecast', '查询天气预报', '按经纬度查询当前天气和预报。', 'GET', '/v1/forecast', 'READ_ONLY', 0, '{"type":"object","properties":{"latitude":{"type":"number","location":"query","description":"纬度"},"longitude":{"type":"number","location":"query","description":"经度"},"current":{"type":"string","location":"query","description":"当前天气字段"}},"required":["latitude","longitude"]}', '{"type":"object"}', '{"latitude":39.9042,"longitude":116.4074,"current":"temperature_2m"}', 'ACTIVE'),
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='rest-countries' AND v.`version_key`='v3.1'), 'country-by-name', 'getCountryByName', '按名称查询国家', '返回匹配国家的名称、代码和首都。', 'GET', '/v3.1/name/{name}', 'READ_ONLY', 0, '{"type":"object","properties":{"name":{"type":"string","location":"path","description":"国家英文名称"},"fields":{"type":"string","location":"query","description":"返回字段列表"}},"required":["name"]}', '{"type":"array"}', '{"name":"china","fields":"name,cca2,capital"}', 'ACTIVE'),
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='github-rest' AND v.`version_key`='2022-11-28'), 'get-repository', 'repos/get', '查询 GitHub 仓库', '返回仓库描述、Star、语言和更新时间等元数据。', 'GET', '/repos/{owner}/{repo}', 'READ_ONLY', 1, '{"type":"object","properties":{"owner":{"type":"string","location":"path","description":"仓库所有者"},"repo":{"type":"string","location":"path","description":"仓库名称"}},"required":["owner","repo"]}', '{"type":"object"}', '{"owner":"public-apis","repo":"public-apis"}', 'ACTIVE'),
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='open-library-search' AND v.`version_key`='v1'), 'search-books', 'searchBooks', '检索图书', '按关键词检索图书和作者。', 'GET', '/search.json', 'READ_ONLY', 0, '{"type":"object","properties":{"q":{"type":"string","location":"query","description":"检索关键词"},"limit":{"type":"integer","location":"query","description":"返回数量"}},"required":["q"]}', '{"type":"object"}', '{"q":"agent","limit":5}', 'ACTIVE'),
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='nager-date' AND v.`version_key`='v3'), 'public-holidays', 'PublicHolidays', '查询公共节假日', '按年份和 ISO 国家码返回公共节假日。', 'GET', '/api/v3/PublicHolidays/{year}/{countryCode}', 'READ_ONLY', 0, '{"type":"object","properties":{"year":{"type":"integer","location":"path","description":"年份"},"countryCode":{"type":"string","location":"path","description":"ISO 3166-1 alpha-2 国家码"}},"required":["year","countryCode"]}', '{"type":"array"}', '{"year":2026,"countryCode":"US"}', 'ACTIVE'),
((SELECT v.`id` FROM `capability_external_api_version` v JOIN `capability_external_api_entry` e ON e.`id`=v.`entry_id` WHERE e.`entry_key`='nasa-apod' AND v.`version_key`='v1'), 'astronomy-picture', 'apod', '查询每日天文图片', '返回指定日期的天文图片和说明；API Key 由 Runtime 凭据注入。', 'GET', '/planetary/apod', 'READ_ONLY', 1, '{"type":"object","properties":{"date":{"type":"string","location":"query","description":"YYYY-MM-DD，可选"}}}', '{"type":"object"}', '{"date":"2026-08-23"}', 'ACTIVE')
ON DUPLICATE KEY UPDATE
`operation_id` = VALUES(`operation_id`), `title` = VALUES(`title`), `description` = VALUES(`description`),
`http_method` = VALUES(`http_method`), `path` = VALUES(`path`), `side_effect` = VALUES(`side_effect`),
`auth_required` = VALUES(`auth_required`), `request_schema_json` = VALUES(`request_schema_json`),
`response_schema_json` = VALUES(`response_schema_json`), `example_params_json` = VALUES(`example_params_json`),
`status` = VALUES(`status`);

-- 2026-08-23 的只读最小连通性证据；不保存响应正文。
INSERT INTO `capability_external_api_verification`
(`entry_id`, `version_id`, `verification_type`, `status`, `http_status`, `latency_ms`, `checked_url`, `evidence_summary`, `checked_at`)
SELECT e.`id`, v.`id`, 'CONNECTIVITY', 'VERIFIED', 200, evidence.`latency_ms`, evidence.`checked_url`, '只读最小请求返回 HTTP 200；未保存响应正文。', '2026-08-23 21:20:00'
FROM (
    SELECT 'open-meteo' AS `entry_key`, 'v1' AS `version_key`, 3573 AS `latency_ms`, 'https://api.open-meteo.com/v1/forecast' AS `checked_url`
    UNION ALL SELECT 'rest-countries', 'v3.1', 3861, 'https://restcountries.com/v3.1/name/china'
    UNION ALL SELECT 'github-rest', '2022-11-28', 1270, 'https://api.github.com/repos/public-apis/public-apis'
    UNION ALL SELECT 'nager-date', 'v3', 1849, 'https://date.nager.at/api/v3/PublicHolidays/2026/US'
) evidence
JOIN `capability_external_api_entry` e ON e.`entry_key` = evidence.`entry_key`
JOIN `capability_external_api_version` v ON v.`entry_id` = e.`id` AND v.`version_key` = evidence.`version_key`
WHERE NOT EXISTS (
    SELECT 1 FROM `capability_external_api_verification` existing
    WHERE existing.`entry_id` = e.`id`
      AND existing.`verification_type` = 'CONNECTIVITY'
      AND existing.`checked_at` = '2026-08-23 21:20:00'
);

-- ============================================================================
-- Runtime Automation：版本化定义、持久时钟、幂等触发、租约执行与审计
-- ============================================================================

CREATE TABLE IF NOT EXISTS `runtime_automation` (
    `id`                 BIGINT       NOT NULL AUTO_INCREMENT,
    `automation_key`     VARCHAR(64)  NOT NULL COMMENT '公开稳定标识 aut_<uuid>',
    `tenant_id`          VARCHAR(96)  NOT NULL DEFAULT 'default',
    `project_id`         BIGINT       DEFAULT NULL,
    `project_code`       VARCHAR(96)  DEFAULT NULL,
    `name`               VARCHAR(128) NOT NULL,
    `description`        VARCHAR(1000) DEFAULT NULL,
    `status`             VARCHAR(24)  NOT NULL COMMENT 'DRAFT / ACTIVE / PAUSED / COMPLETED / ARCHIVED',
    `current_version_id` BIGINT       DEFAULT NULL COMMENT '当前不可变 runtime_automation_version',
    `next_fire_at`       DATETIME(6)  DEFAULT NULL COMMENT 'UTC 投影；真实时钟事实由 runtime_scheduler_task 持有',
    `last_fire_at`       DATETIME(6)  DEFAULT NULL COMMENT '最近名义触发时刻（UTC）',
    `revision`           BIGINT       NOT NULL DEFAULT 1 COMMENT '乐观锁修订号',
    `created_by`         VARCHAR(128) NOT NULL,
    `updated_by`         VARCHAR(128) NOT NULL,
    `created_at`         DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    `updated_at`         DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_automation_key` (`automation_key`),
    KEY `idx_runtime_automation_project` (`tenant_id`, `project_code`, `status`),
    KEY `idx_runtime_automation_next_fire` (`status`, `next_fire_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Runtime 自动化聚合根与当前版本投影';

CREATE TABLE IF NOT EXISTS `runtime_automation_version` (
    `id`                       BIGINT       NOT NULL AUTO_INCREMENT,
    `automation_id`            BIGINT       NOT NULL,
    `version_no`               INT          NOT NULL,
    `target_type`              VARCHAR(24)  NOT NULL COMMENT 'AGENT / WORKFLOW',
    `target_id`                VARCHAR(64)  NOT NULL,
    `target_version_id`        BIGINT       NOT NULL COMMENT '固定发布版本；绝不漂移到 latest',
    `target_snapshot_json`     MEDIUMTEXT   NOT NULL COMMENT '目标元数据与指纹；不复制秘密',
    `trigger_type`             VARCHAR(16)  NOT NULL COMMENT 'CRON / ONCE',
    `cron_expression`          VARCHAR(128) DEFAULT NULL COMMENT 'Spring 六段 cron',
    `fire_at`                  DATETIME(6)  DEFAULT NULL COMMENT 'ONCE 的 UTC 时刻',
    `time_zone`                VARCHAR(64)  NOT NULL COMMENT 'IANA time zone',
    `misfire_policy`           VARCHAR(24)  NOT NULL COMMENT 'SKIP / FIRE_ONCE / CATCH_UP',
    `misfire_grace_seconds`    INT          NOT NULL DEFAULT 60,
    `max_catch_up`             INT          NOT NULL DEFAULT 10,
    `concurrency_policy`       VARCHAR(24)  NOT NULL COMMENT 'SKIP / QUEUE / ALLOW',
    `max_concurrent_runs`      INT          NOT NULL DEFAULT 1,
    `timeout_seconds`          INT          NOT NULL DEFAULT 900,
    `max_attempts`             INT          NOT NULL DEFAULT 3,
    `initial_backoff_seconds`  INT          NOT NULL DEFAULT 10,
    `max_backoff_seconds`      INT          NOT NULL DEFAULT 300,
    `input_json`               MEDIUMTEXT   NOT NULL COMMENT '不含凭据、最大 64KiB 的版本化输入',
    `principal_type`           VARCHAR(40)  NOT NULL COMMENT 'AUTOMATION_SERVICE_ACCOUNT',
    `principal_id`             VARCHAR(128) NOT NULL,
    `principal_snapshot_json`  MEDIUMTEXT   NOT NULL COMMENT '租户/项目/授权人快照；userTrusted=false',
    `interaction_policy`       VARCHAR(32)  NOT NULL DEFAULT 'FAIL_CLOSED',
    `fingerprint_sha256`       CHAR(64)     NOT NULL,
    `created_by`               VARCHAR(128) NOT NULL,
    `created_at`               DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_automation_version` (`automation_id`, `version_no`),
    KEY `idx_runtime_automation_target` (`target_type`, `target_id`, `target_version_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='不可变 Automation 定义版本';

CREATE TABLE IF NOT EXISTS `runtime_automation_occurrence` (
    `id`                      BIGINT       NOT NULL AUTO_INCREMENT,
    `occurrence_key`          VARCHAR(160) NOT NULL COMMENT '跨副本幂等键',
    `automation_id`           BIGINT       NOT NULL,
    `automation_version_id`   BIGINT       NOT NULL,
    `source_type`             VARCHAR(24)  NOT NULL COMMENT 'SCHEDULE / MANUAL / RETRY',
    `scheduled_at`            DATETIME(6)  NOT NULL COMMENT '名义调度 UTC 时刻',
    `available_at`            DATETIME(6)  NOT NULL,
    `status`                  VARCHAR(24)  NOT NULL COMMENT 'PENDING / RETRY / LEASED / RUNNING / SUCCEEDED / FAILED / DEAD / CANCELLED / SKIPPED',
    `priority`                INT          NOT NULL DEFAULT 0,
    `attempt_count`           INT          NOT NULL DEFAULT 0,
    `max_attempts`            INT          NOT NULL DEFAULT 3,
    `lease_owner`             VARCHAR(96)  DEFAULT NULL,
    `lease_token`             VARCHAR(64)  DEFAULT NULL,
    `leased_until`            DATETIME(6)  DEFAULT NULL,
    `trace_id`                VARCHAR(64)  DEFAULT NULL,
    `interaction_id`          VARCHAR(128) DEFAULT NULL COMMENT 'fail-closed 人工交互证据',
    `input_snapshot_json`     MEDIUMTEXT   NOT NULL,
    `principal_snapshot_json` MEDIUMTEXT   NOT NULL,
    `last_error_code`         VARCHAR(96)  DEFAULT NULL,
    `last_error_message`      VARCHAR(2000) DEFAULT NULL,
    `started_at`              DATETIME(6)  DEFAULT NULL,
    `completed_at`            DATETIME(6)  DEFAULT NULL,
    `created_at`              DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    `updated_at`              DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_automation_occurrence_key` (`occurrence_key`),
    KEY `idx_runtime_automation_occurrence_poll` (`status`, `available_at`, `priority`),
    KEY `idx_runtime_automation_occurrence_lease` (`status`, `leased_until`),
    KEY `idx_runtime_automation_occurrence_history` (`automation_id`, `scheduled_at`),
    KEY `idx_runtime_automation_occurrence_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='每个 Automation 名义触发时刻的幂等执行实例';

CREATE TABLE IF NOT EXISTS `runtime_automation_attempt` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `occurrence_id`  BIGINT       NOT NULL,
    `attempt_no`     INT          NOT NULL,
    `status`         VARCHAR(24)  NOT NULL,
    `worker_id`      VARCHAR(96)  DEFAULT NULL,
    `lease_token`    VARCHAR(64)  DEFAULT NULL,
    `trace_id`       VARCHAR(64)  DEFAULT NULL,
    `result_summary` VARCHAR(4000) DEFAULT NULL,
    `error_code`     VARCHAR(96)  DEFAULT NULL,
    `error_message`  VARCHAR(2000) DEFAULT NULL,
    `started_at`     DATETIME(6)  DEFAULT NULL,
    `ended_at`       DATETIME(6)  DEFAULT NULL,
    `created_at`     DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_runtime_automation_attempt` (`occurrence_id`, `attempt_no`),
    KEY `idx_runtime_automation_attempt_trace` (`trace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Automation occurrence 每次租约尝试的审计事实';

CREATE TABLE IF NOT EXISTS `runtime_automation_event` (
    `id`             BIGINT       NOT NULL AUTO_INCREMENT,
    `automation_id`  BIGINT       NOT NULL,
    `occurrence_id`  BIGINT       DEFAULT NULL,
    `event_type`     VARCHAR(96)  NOT NULL,
    `actor_type`     VARCHAR(32)  NOT NULL,
    `actor_id`       VARCHAR(128) NOT NULL,
    `detail_json`    MEDIUMTEXT   NOT NULL COMMENT '无凭据的状态转换与证据摘要',
    `created_at`     DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`id`),
    KEY `idx_runtime_automation_event_history` (`automation_id`, `created_at`),
    KEY `idx_runtime_automation_event_occurrence` (`occurrence_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Automation 元数据审计事件';

CREATE TABLE IF NOT EXISTS `runtime_automation_engine_command` (
    `id`                    BIGINT       NOT NULL AUTO_INCREMENT,
    `automation_id`         BIGINT       NOT NULL,
    `automation_version_id` BIGINT       DEFAULT NULL,
    `command_type`          VARCHAR(16)  NOT NULL COMMENT 'UPSERT / CANCEL',
    `status`                VARCHAR(24)  NOT NULL COMMENT 'PENDING / RETRY / PROCESSING / COMPLETED / DEAD',
    `attempt_count`         INT          NOT NULL DEFAULT 0,
    `available_at`          DATETIME(6)  NOT NULL,
    `lease_owner`           VARCHAR(96)  DEFAULT NULL,
    `lease_token`           VARCHAR(64)  DEFAULT NULL,
    `leased_until`          DATETIME(6)  DEFAULT NULL,
    `last_error_code`       VARCHAR(96)  DEFAULT NULL,
    `last_error_message`    VARCHAR(2000) DEFAULT NULL,
    `created_at`            DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    `updated_at`            DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
    `completed_at`          DATETIME(6)  DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_runtime_automation_engine_command_poll` (`status`, `available_at`, `id`),
    KEY `idx_runtime_automation_engine_command_target` (`automation_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Automation 定义事务与外部时钟表之间的持久命令';

CREATE TABLE IF NOT EXISTS `runtime_automation_execution_slot` (
    `automation_id` BIGINT       NOT NULL,
    `slot_no`       INT          NOT NULL,
    `occurrence_id` BIGINT       NOT NULL,
    `lease_owner`   VARCHAR(96)  NOT NULL,
    `lease_token`   VARCHAR(64)  NOT NULL,
    `leased_until`  DATETIME(6)  NOT NULL,
    `updated_at`    DATETIME(6)  NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
    PRIMARY KEY (`automation_id`, `slot_no`),
    UNIQUE KEY `uk_runtime_automation_slot_occurrence` (`occurrence_id`),
    KEY `idx_runtime_automation_slot_lease` (`leased_until`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='跨 Runtime 副本的 Automation 并发槽租约';

-- db-scheduler 16.11.0 官方单表模型，表名改为 Runtime owner 前缀。
-- MySQL 5.7 使用 fetch 轮询；MySQL 8 也保持相同策略。所有时间按 UTC 传输。
CREATE TABLE IF NOT EXISTS `runtime_scheduler_task` (
    `task_name`            VARCHAR(100) NOT NULL,
    `task_instance`        VARCHAR(100) NOT NULL,
    `task_data`            BLOB         DEFAULT NULL,
    `execution_time`       DATETIME(6)  NOT NULL,
    `picked`               TINYINT(1)   NOT NULL,
    `picked_by`            VARCHAR(50)  DEFAULT NULL,
    `last_success`         DATETIME(6)  DEFAULT NULL,
    `last_failure`         DATETIME(6)  DEFAULT NULL,
    `consecutive_failures` INT          DEFAULT NULL,
    `last_heartbeat`       DATETIME(6)  DEFAULT NULL,
    `version`              BIGINT       NOT NULL,
    `priority`             SMALLINT     DEFAULT NULL,
    PRIMARY KEY (`task_name`, `task_instance`),
    KEY `idx_runtime_scheduler_execution_time` (`execution_time`),
    KEY `idx_runtime_scheduler_last_heartbeat` (`last_heartbeat`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='db-scheduler 持久集群时钟；不承载业务执行结果';

-- ============================================================================
-- 清理：删除为本次脚本创建的临时存储过程
-- ============================================================================
DROP PROCEDURE IF EXISTS add_col_if_absent;
DROP PROCEDURE IF EXISTS add_idx_if_absent;
DROP PROCEDURE IF EXISTS add_unique_idx_if_absent;

-- END OF initV2.sql
