From 25c6ed3158c8db570aa8b1c556489e6aee42716c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Fri, 26 Jun 2026 14:37:56 +0800 Subject: [PATCH 1/7] chore(db): fork kingbase migration tree into independent postgresql tree PostgreSQL previously ran on the KingbaseES migration tree (db/migration/kingbase) because the two share a SQL dialect. This couples PostgreSQL-specific evolution to KingbaseES and blocks PG-only optimizations. Fork db/migration/kingbase -> db/migration/postgresql (152 files, V1-V158, byte-identical) and point application-postgres.yml's flyway.locations at the new tree. The switch is transparent for existing PostgreSQL deployments: Flyway tracks version + checksum, not the classpath location, and the identical scripts keep identical checksums, so nothing re-runs. Also align application-kingbase.yml's mate.wiki block with the postgres profile (allowed-source-roots / watcher-enabled / watcher-interval-ms), which were missing. No column definitions change in this commit; PG schema output is identical to what the kingbase tree produced. JSONB optimizations land in a follow-up. --- .../main/resources/application-kingbase.yml | 3 + .../main/resources/application-postgres.yml | 11 +- .../V100__multimodal_default_models.sql | 16 + ...V101__cleanup_blank_tool_guard_rule_id.sql | 14 + .../V102__agent_unique_name_per_workspace.sql | 20 + .../postgresql/V103__drop_fact_entity_ref.sql | 2 + ...104__wiki_chunk_embedding_text_version.sql | 15 + .../postgresql/V105__wiki_transformation.sql | 66 ++ ...106__wiki_transformation_output_target.sql | 24 + .../postgresql/V107__wiki_page_embedding.sql | 33 + ...V108__wiki_transformation_starter_pack.sql | 212 ++++++ ...109__wiki_transformation_output_format.sql | 12 + .../migration/postgresql/V10__hook_system.sql | 34 + .../V110__wiki_transformation_run_tokens.sql | 31 + ...111__wiki_transformation_output_schema.sql | 11 + .../migration/postgresql/V112__skill_file.sql | 26 + ...kspace_memory_search_and_async_indexes.sql | 15 + .../postgresql/V114__conversation_pinned.sql | 12 + .../postgresql/V115__xai_grok_provider.sql | 35 + .../postgresql/V116__conversation_model.sql | 5 + .../postgresql/V117__skill_lifecycle.sql | 25 + .../V118__purge_model_config_tombstones.sql | 15 + .../postgresql/V119__channel_tool_support.sql | 32 + .../postgresql/V11__skill_synthesis.sql | 23 + .../migration/postgresql/V120__agent_goal.sql | 75 +++ .../postgresql/V121__tool_disclosure_tier.sql | 20 + .../V122__fix_generative_tool_tier_names.sql | 13 + .../V123__conversation_progress_ledger.sql | 5 + .../V124__agent_max_iterations_150.sql | 6 + .../V125__agent_workspace_base_path.sql | 4 + .../V126__agent_binding_disabled_flags.sql | 13 + .../postgresql/V127__approval_auto_grant.sql | 25 + .../V128__approval_resolution_log.sql | 25 + .../V129__wiki_page_broken_links.sql | 7 + .../postgresql/V12__wiki_chunk_table.sql | 22 + .../postgresql/V130__agent_primary_kb.sql | 26 + .../postgresql/V131__claude_48_models.sql | 36 + ...ory_consolidation_cron_tier_discipline.sql | 13 + .../V133__wiki_agent_page_type_permission.sql | 27 + .../V134__wiki_page_type_profile.sql | 81 +++ .../V135__wiki_layered_knowledge.sql | 58 ++ .../V136__wiki_pipeline_runtime.sql | 57 ++ .../postgresql/V137__memory_owner_scope.sql | 107 +++ ...V138__rename_search_tool_to_web_search.sql | 7 + .../V139__mcp_default_read_timeout_60s.sql | 8 + .../postgresql/V13__wiki_chunk_embedding.sql | 21 + .../V140__goal_criteria_checklist.sql | 19 + .../postgresql/V141__agent_wiki_kb_scope.sql | 24 + ...__wiki_transformation_target_page_type.sql | 13 + .../V143__register_code_execute_tool.sql | 6 + ...144__ckjia_mcp_fix_production_endpoint.sql | 28 + .../V145__claude_fable_5_models.sql | 37 + .../V146__wiki_kb_watcher_enabled.sql | 14 + .../postgresql/V147__wiki_page_aliases.sql | 5 + .../postgresql/V148__wiki_entity.sql | 31 + .../postgresql/V149__wiki_entity_mention.sql | 25 + .../V14__embedding_model_config.sql | 36 + .../postgresql/V150__wiki_entity_relation.sql | 27 + .../postgresql/V151__webchat_session_id.sql | 3 + .../V152__webchat_archive_and_revocation.sql | 19 + .../postgresql/V153__zhipu_glm_5_2.sql | 18 + .../postgresql/V154__agent_wiki_disabled.sql | 3 + .../postgresql/V155__plan_conversation_id.sql | 3 + .../V156__sub_plan_assigned_agent.sql | 3 + .../V157__register_session_list_tool.sql | 10 + .../V158__register_session_send_tool.sql | 9 + .../V159__sso_external_identity.sql | 37 + ...15__purge_unavailable_dashscope_models.sql | 7 + ...16__purge_dot_version_dashscope_models.sql | 8 + .../postgresql/V1__baseline_schema.sql | 630 ++++++++++++++++++ .../V20__purge_soft_deleted_rows.sql | 34 + .../V21__provider_fallback_priority.sql | 26 + .../postgresql/V23__wiki_relation_model.sql | 16 + .../postgresql/V24__wiki_processing_job.sql | 25 + .../V25__agent_provider_preference.sql | 24 + .../postgresql/V26__dream_report.sql | 24 + .../V27__memory_recall_review_fields.sql | 6 + .../postgresql/V28__morning_card_seen.sql | 12 + .../V29__memory_fact_projection.sql | 49 ++ .../postgresql/V2__workspace_base_path.sql | 227 +++++++ .../V30__register_collab_skills.sql | 60 ++ .../V31__register_docx_render_tool.sql | 5 + .../postgresql/V32__bailian_team_provider.sql | 24 + .../postgresql/V33__expand_api_key_column.sql | 3 + .../V34__siliconflow_opencode_providers.sql | 47 ++ .../V35__skill_security_scan_result.sql | 28 + .../postgresql/V36__skill_i18n_name.sql | 51 ++ .../V37__wiki_page_source_entries.sql | 6 + .../V38__wiki_chunk_content_mediumtext.sql | 3 + .../postgresql/V39__rfc051_chunk_metadata.sql | 41 ++ .../postgresql/V3__register_cron_job_tool.sql | 4 + .../postgresql/V40__rfc051_page_locked.sql | 11 + .../postgresql/V41__rfc051_page_archived.sql | 10 + .../V42__claude_47_gpt_55_models.sql | 37 + ...V43__rfc062_claude_code_oauth_provider.sql | 32 + ...4__fix_claude_sonnet_47_does_not_exist.sql | 31 + .../postgresql/V45__deepseek_v4_models.sql | 19 + .../postgresql/V46__stt_default_enabled.sql | 21 + .../V47__agent_max_iterations_100.sql | 16 + ...ent_max_iterations_and_agents_md_tools.sql | 20 + .../V49__reenable_write_edit_file_tools.sql | 5 + .../postgresql/V4__agent_thinking_level.sql | 12 + ...50__fix_agents_md_tool_guidance_concat.sql | 50 ++ ..._remove_write_edit_file_from_toolguard.sql | 5 + .../V52__feishu_default_connection_mode.sql | 4 + .../V53__feishu_connection_mode_recover.sql | 24 + ...channel_names_to_chinese_for_zh_locale.sql | 45 ++ .../postgresql/V55__provider_enabled.sql | 45 ++ .../V56__volcengine_plan_provider.sql | 29 + .../V57__cron_run_delivery_status.sql | 38 ++ .../postgresql/V58__cron_channel_binding.sql | 23 + .../V59__approval_chat_origin_snapshot.sql | 11 + .../postgresql/V5__conversation_parent.sql | 13 + ...60__fix_invalid_workspace_member_roles.sql | 26 + .../postgresql/V61__agent_creator_user_id.sql | 14 + .../postgresql/V62__cron_job_workspace_id.sql | 14 + .../postgresql/V63__channel_identity_json.sql | 14 + .../V64__cleanup_unused_channel_seeds.sql | 10 + ..._seed_tasks_conversation_per_workspace.sql | 28 + .../V66__add_model_capabilities.sql | 11 + .../postgresql/V67__add_skill_manifest.sql | 14 + .../postgresql/V68__add_acp_endpoints.sql | 43 ++ .../postgresql/V69__cron_job_dedup_unique.sql | 16 + .../migration/postgresql/V6__plugin_table.sql | 20 + .../postgresql/V70__cron_job_dedup_safety.sql | 16 + .../postgresql/V71__hunyuan_3d_provider.sql | 23 + .../V72__hunyuan_3d_model_config.sql | 19 + .../postgresql/V73__add_skill_secret.sql | 23 + .../postgresql/V74__shedlock_table.sql | 16 + .../V75__model_config_request_timeout.sql | 16 + .../postgresql/V76__personal_access_token.sql | 24 + .../postgresql/V77__wiki_relation_table.sql | 41 ++ .../postgresql/V78__feature_flag.sql | 36 + .../V79__wiki_image_caption_cache.sql | 32 + .../V7__wiki_last_processed_hash.sql | 12 + .../V80__wiki_raw_material_mime_type.sql | 8 + .../V81__refresh_bailian_qwen_catalog.sql | 49 ++ .../postgresql/V82__wiki_hot_cache.sql | 30 + .../V84__widen_raw_material_mime_type.sql | 7 + .../postgresql/V85__ckjia_mcp_seed.sql | 28 + .../postgresql/V87__skill_usage_stat.sql | 17 + ...onvert_default_agent_icons_to_pixelart.sql | 8 + .../V89__chatgpt_oauth_model_discovery.sql | 16 + .../postgresql/V8__wiki_raw_progress.sql | 33 + .../V90__zhipu_coding_plan_provider.sql | 85 +++ .../V91__widen_message_and_skill_content.sql | 5 + .../V92__mcp_server_tools_cache.sql | 31 + .../postgresql/V93__xiaomi_mimo_provider.sql | 31 + .../V94__register_office_render_tools.sql | 16 + .../V95__wiki_raw_material_cancel.sql | 15 + .../postgresql/V96__workflow_foundations.sql | 154 +++++ .../V97__workflow_purge_tombstones.sql | 9 + .../postgresql/V98__trigger_last_error.sql | 5 + .../V99__dashscope_compat_provider.sql | 39 ++ .../postgresql/V9__usage_cache_tokens.sql | 24 + 155 files changed, 4654 insertions(+), 3 deletions(-) create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V100__multimodal_default_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V101__cleanup_blank_tool_guard_rule_id.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V102__agent_unique_name_per_workspace.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V103__drop_fact_entity_ref.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V104__wiki_chunk_embedding_text_version.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V105__wiki_transformation.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V106__wiki_transformation_output_target.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V107__wiki_page_embedding.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V108__wiki_transformation_starter_pack.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V109__wiki_transformation_output_format.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V110__wiki_transformation_run_tokens.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V111__wiki_transformation_output_schema.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V112__skill_file.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V113__workspace_memory_search_and_async_indexes.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V114__conversation_pinned.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V115__xai_grok_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V116__conversation_model.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V117__skill_lifecycle.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V118__purge_model_config_tombstones.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V119__channel_tool_support.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V11__skill_synthesis.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V121__tool_disclosure_tier.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V122__fix_generative_tool_tier_names.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V123__conversation_progress_ledger.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V124__agent_max_iterations_150.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V125__agent_workspace_base_path.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V126__agent_binding_disabled_flags.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V127__approval_auto_grant.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V128__approval_resolution_log.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V129__wiki_page_broken_links.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V12__wiki_chunk_table.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V130__agent_primary_kb.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V131__claude_48_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V132__memory_consolidation_cron_tier_discipline.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V133__wiki_agent_page_type_permission.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V137__memory_owner_scope.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V138__rename_search_tool_to_web_search.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V139__mcp_default_read_timeout_60s.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V13__wiki_chunk_embedding.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V140__goal_criteria_checklist.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V141__agent_wiki_kb_scope.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V142__wiki_transformation_target_page_type.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V143__register_code_execute_tool.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V144__ckjia_mcp_fix_production_endpoint.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V145__claude_fable_5_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V146__wiki_kb_watcher_enabled.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V147__wiki_page_aliases.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V149__wiki_entity_mention.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V14__embedding_model_config.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V150__wiki_entity_relation.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V151__webchat_session_id.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V152__webchat_archive_and_revocation.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V153__zhipu_glm_5_2.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V154__agent_wiki_disabled.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V155__plan_conversation_id.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V156__sub_plan_assigned_agent.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V157__register_session_list_tool.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V158__register_session_send_tool.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V159__sso_external_identity.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V15__purge_unavailable_dashscope_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V16__purge_dot_version_dashscope_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V20__purge_soft_deleted_rows.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V21__provider_fallback_priority.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V23__wiki_relation_model.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V25__agent_provider_preference.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V26__dream_report.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V27__memory_recall_review_fields.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V28__morning_card_seen.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V29__memory_fact_projection.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V30__register_collab_skills.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V31__register_docx_render_tool.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V32__bailian_team_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V33__expand_api_key_column.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V34__siliconflow_opencode_providers.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V35__skill_security_scan_result.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V36__skill_i18n_name.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V37__wiki_page_source_entries.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V38__wiki_chunk_content_mediumtext.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V39__rfc051_chunk_metadata.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V3__register_cron_job_tool.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V40__rfc051_page_locked.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V41__rfc051_page_archived.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V42__claude_47_gpt_55_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V43__rfc062_claude_code_oauth_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V44__fix_claude_sonnet_47_does_not_exist.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V45__deepseek_v4_models.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V46__stt_default_enabled.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V47__agent_max_iterations_100.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V48__agent_max_iterations_and_agents_md_tools.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V49__reenable_write_edit_file_tools.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V4__agent_thinking_level.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V50__fix_agents_md_tool_guidance_concat.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V51__remove_write_edit_file_from_toolguard.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V52__feishu_default_connection_mode.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V54__channel_names_to_chinese_for_zh_locale.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V55__provider_enabled.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V56__volcengine_plan_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V57__cron_run_delivery_status.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V59__approval_chat_origin_snapshot.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V5__conversation_parent.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V60__fix_invalid_workspace_member_roles.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V61__agent_creator_user_id.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V62__cron_job_workspace_id.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V64__cleanup_unused_channel_seeds.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V65__seed_tasks_conversation_per_workspace.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V66__add_model_capabilities.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V69__cron_job_dedup_unique.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V70__cron_job_dedup_safety.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V71__hunyuan_3d_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V72__hunyuan_3d_model_config.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V73__add_skill_secret.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V74__shedlock_table.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V75__model_config_request_timeout.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V76__personal_access_token.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V78__feature_flag.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V79__wiki_image_caption_cache.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V7__wiki_last_processed_hash.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V80__wiki_raw_material_mime_type.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V81__refresh_bailian_qwen_catalog.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V82__wiki_hot_cache.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V84__widen_raw_material_mime_type.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V85__ckjia_mcp_seed.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V87__skill_usage_stat.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V88__convert_default_agent_icons_to_pixelart.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V89__chatgpt_oauth_model_discovery.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V8__wiki_raw_progress.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V90__zhipu_coding_plan_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V91__widen_message_and_skill_content.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V93__xiaomi_mimo_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V94__register_office_render_tools.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V95__wiki_raw_material_cancel.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V97__workflow_purge_tombstones.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V98__trigger_last_error.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V99__dashscope_compat_provider.sql create mode 100644 mateclaw-server/src/main/resources/db/migration/postgresql/V9__usage_cache_tokens.sql diff --git a/mateclaw-server/src/main/resources/application-kingbase.yml b/mateclaw-server/src/main/resources/application-kingbase.yml index c570cba21..18aba23a8 100644 --- a/mateclaw-server/src/main/resources/application-kingbase.yml +++ b/mateclaw-server/src/main/resources/application-kingbase.yml @@ -69,6 +69,9 @@ mybatis-plus: mate: wiki: require-allowed-roots: true + allowed-source-roots: ${MATE_WIKI_ALLOWED_SOURCE_ROOTS:} + watcher-enabled: ${MATE_WIKI_WATCHER_ENABLED:false} + watcher-interval-ms: ${MATE_WIKI_WATCHER_INTERVAL_MS:300000} # Production hardening: lock down the Swagger UI / OpenAPI document so it is not # anonymously browsable. Requires a global admin (ROLE_ADMIN); SecurityConfig diff --git a/mateclaw-server/src/main/resources/application-postgres.yml b/mateclaw-server/src/main/resources/application-postgres.yml index 38a3bc21d..c69a33467 100644 --- a/mateclaw-server/src/main/resources/application-postgres.yml +++ b/mateclaw-server/src/main/resources/application-postgres.yml @@ -29,13 +29,18 @@ spring: connection-init-sql: SET search_path TO mateclaw flyway: - # PostgreSQL and KingbaseES share the same migration tree - # (db/migration/kingbase) because they use the same SQL dialect. + # PostgreSQL has its own migration tree (db/migration/postgresql), + # forked from the KingbaseES tree (which shares PostgreSQL's SQL dialect). + # The fork lets PostgreSQL-only optimizations (e.g. TEXT -> JSONB) diverge + # from KingbaseES without affecting Kingbase deployments. At fork time the + # two trees are byte-identical, so switching locations is transparent for + # existing PostgreSQL deployments: Flyway tracks version + checksum, not + # the classpath location, and identical scripts keep identical checksums. url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10 user: ${DB_USERNAME:postgres} password: ${DB_PASSWORD:postgres} locations: - - classpath:db/migration/kingbase + - classpath:db/migration/postgresql # Ensure the target schema exists before migrating (PostgreSQL won't # auto-create a non-public schema). init-sqls: diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V100__multimodal_default_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V100__multimodal_default_models.sql new file mode 100644 index 000000000..2718fe42f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V100__multimodal_default_models.sql @@ -0,0 +1,16 @@ +-- V100: System-level defaults for vision and video sidecar routing. +-- When the agent's primary model lacks the modality required by an attachment, +-- the runtime delegates a single caption call to the model recorded here. +-- Empty value = not configured; the UI then asks the user to pick one. +-- Setting value stores mate_model_config.id as a string (provider+model_name pairs are not unique). +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +VALUES (1000002001, 'default.vision_model', '', + 'Default vision-capable model id (mate_model_config.id) used by sidecar router when primary model lacks VISION modality', + NOW(), NOW()) +ON CONFLICT (id) DO UPDATE SET setting_key = EXCLUDED.setting_key; + +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +VALUES (1000002002, 'default.video_model', '', + 'Default video-capable model id (mate_model_config.id) used by sidecar router when primary model lacks VIDEO modality', + NOW(), NOW()) +ON CONFLICT (id) DO UPDATE SET setting_key = EXCLUDED.setting_key; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V101__cleanup_blank_tool_guard_rule_id.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V101__cleanup_blank_tool_guard_rule_id.sql new file mode 100644 index 000000000..49960ed60 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V101__cleanup_blank_tool_guard_rule_id.sql @@ -0,0 +1,14 @@ +-- See the matching H2 file for context. This migration purges any +-- orphan rows that earlier releases persisted with a blank rule_id and +-- then installs a CHECK constraint so the schema itself rejects blank +-- rule_id, defending against any future code path that bypasses the +-- service-layer guard. CHECK constraints are enforced from MySQL 8.0.16 +-- onward; this project targets MySQL 8.0+ so the constraint is live. + +DELETE FROM mate_tool_guard_rule +WHERE (rule_id IS NULL OR LENGTH(TRIM(rule_id)) = 0) + AND (builtin IS NULL OR builtin = FALSE); + +ALTER TABLE mate_tool_guard_rule + ADD CONSTRAINT ck_tool_guard_rule_id_nonblank + CHECK (rule_id IS NOT NULL AND LENGTH(TRIM(rule_id)) > 0); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V102__agent_unique_name_per_workspace.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V102__agent_unique_name_per_workspace.sql new file mode 100644 index 000000000..39860707f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V102__agent_unique_name_per_workspace.sql @@ -0,0 +1,20 @@ +-- Enforce unique Agent name within a workspace. See H2 variant for context. +-- +-- Step 1 — rename pre-existing duplicates. PostgreSQL uses UPDATE ... FROM +-- instead of MySQL's UPDATE ... JOIN syntax, and || instead of CONCAT. +-- md5(random()::text) gives a unique suffix and is portable across both +-- PostgreSQL and KingbaseES (avoids the Kingbase-only SYS_GUID()). +UPDATE mate_agent t +SET name = '__mate_dup_v102__' || t.id || '__' || md5(random()::text) +FROM ( + SELECT workspace_id, name, MIN(id) AS keep_id + FROM mate_agent + GROUP BY workspace_id, name + HAVING COUNT(*) > 1 +) k +WHERE t.workspace_id = k.workspace_id + AND t.name = k.name + AND t.id <> k.keep_id; + +-- Step 2 — add the unique index, idempotent via IF NOT EXISTS (PostgreSQL-native). +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_workspace_name ON mate_agent (workspace_id, name); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V103__drop_fact_entity_ref.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V103__drop_fact_entity_ref.sql new file mode 100644 index 000000000..9f0ce9484 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V103__drop_fact_entity_ref.sql @@ -0,0 +1,2 @@ +-- Drop the dead mate_fact_entity_ref table. See H2 variant for context. +DROP TABLE IF EXISTS mate_fact_entity_ref; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V104__wiki_chunk_embedding_text_version.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V104__wiki_chunk_embedding_text_version.sql new file mode 100644 index 000000000..de54cbc3c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V104__wiki_chunk_embedding_text_version.sql @@ -0,0 +1,15 @@ +-- V104: track which input format a chunk's stored embedding was generated against. +-- The embedding input builder concatenates raw title / header breadcrumb / page +-- number alongside chunk content; bumping the builder's CURRENT_INPUT_VERSION +-- forces a re-embed pass without changing the model. NULL is treated as the +-- legacy content-only format and re-embedded lazily on the next pass. +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding_text_version' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN embedding_text_version VARCHAR(32) NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V105__wiki_transformation.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V105__wiki_transformation.sql new file mode 100644 index 000000000..e8e0a9ad1 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V105__wiki_transformation.sql @@ -0,0 +1,66 @@ +-- Reusable user-defined prompt templates ("transformations") that run over +-- a raw material's extracted text and persist the LLM output as an artifact +-- on the knowledge base. Templates can be flagged apply_default so the +-- ingestion pipeline runs them automatically once a raw material reaches +-- the completed state. Manual / agent-tool runs are also supported. + +CREATE TABLE IF NOT EXISTS mate_wiki_transformation ( + id BIGINT PRIMARY KEY, + + kb_id BIGINT NULL, + workspace_id BIGINT NOT NULL DEFAULT 1, + + name VARCHAR(64) NOT NULL, + title VARCHAR(255) NOT NULL, + description VARCHAR(1024), + + prompt_template TEXT NOT NULL, + + apply_default BOOLEAN NOT NULL DEFAULT FALSE, + model_id BIGINT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wtr_kb ON mate_wiki_transformation (kb_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wtr_ws ON mate_wiki_transformation (workspace_id, deleted); + +-- Unique name per KB (NULL kb_id rows compete in a shared "global" bucket). +-- MySQL treats NULL as distinct in unique indexes, so workspace-wide names +-- can technically collide; the service layer enforces uniqueness for the +-- NULL-kb_id case in software. +CREATE UNIQUE INDEX uk_wtr_kb_name ON mate_wiki_transformation (kb_id, name, deleted); + + +CREATE TABLE IF NOT EXISTS mate_wiki_transformation_run ( + id BIGINT PRIMARY KEY, + + transformation_id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + workspace_id BIGINT NOT NULL DEFAULT 1, + + input_kind VARCHAR(16) NOT NULL, + raw_id BIGINT NULL, + page_id BIGINT NULL, + + status VARCHAR(16) NOT NULL DEFAULT 'pending', + + output TEXT, + error VARCHAR(2048), + model_id BIGINT NULL, + + triggered_by VARCHAR(32) NOT NULL DEFAULT 'manual', + + started_at TIMESTAMP(3) NULL, + completed_at TIMESTAMP(3) NULL, + duration_ms BIGINT NULL, + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wtrn_tr ON mate_wiki_transformation_run (transformation_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wtrn_kb ON mate_wiki_transformation_run (kb_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wtrn_raw ON mate_wiki_transformation_run (raw_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V106__wiki_transformation_output_target.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V106__wiki_transformation_output_target.sql new file mode 100644 index 000000000..7109af40e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V106__wiki_transformation_output_target.sql @@ -0,0 +1,24 @@ +-- Two-part follow-up to V105 so a transformation's output can flow back +-- into the KB as a first-class artifact. See the h2 sibling migration for +-- the prose explanation. MySQL lacks ADD COLUMN IF NOT EXISTS, so each +-- column is guarded by an INFORMATION_SCHEMA check + prepared statement. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_target' + ) THEN + ALTER TABLE mate_wiki_transformation ADD COLUMN output_target VARCHAR(16) NOT NULL DEFAULT 'none'; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'output_page_id' + ) THEN + ALTER TABLE mate_wiki_transformation_run ADD COLUMN output_page_id BIGINT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V107__wiki_page_embedding.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V107__wiki_page_embedding.sql new file mode 100644 index 000000000..e1bca69eb --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V107__wiki_page_embedding.sql @@ -0,0 +1,33 @@ +-- Page-level embedding columns. See the h2 sibling for the prose +-- explanation. MySQL lacks ADD COLUMN IF NOT EXISTS, so each column +-- guarded by an INFORMATION_SCHEMA check + prepared statement. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN embedding BYTEA DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding_model' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN embedding_model VARCHAR(64) DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding_text_version' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN embedding_text_version VARCHAR(32) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V108__wiki_transformation_starter_pack.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V108__wiki_transformation_starter_pack.sql new file mode 100644 index 000000000..fd945575d --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V108__wiki_transformation_starter_pack.sql @@ -0,0 +1,212 @@ +-- Starter pack: 7 workspace-wide transformation templates. See h2 sibling +-- for the prose explanation. ON CONFLICT DO NOTHING so a re-run (e.g. via repair) +-- never clobbers user edits. + +INSERT INTO mate_wiki_transformation + (id, kb_id, workspace_id, name, title, description, prompt_template, + apply_default, model_id, enabled, output_target, create_time, update_time, deleted) +VALUES +(1000004001, NULL, 1, 'contract-risk-extract', '合同风险点提取', '逐条审查合同条款,标注风险等级、原文位置、AI 建议改写。配合企业场景 → 合同审查使用。', '你是一名企业法务审查员。从下面的合同文本中完整提取所有需要关注的风险条款,按以下结构输出 Markdown: + +## 风险条款清单 + +对每条值得审查的条款,输出三级标题: + +### <条款简称> +- **风险等级**:高 / 中 / 低 +- **条款类型**:赔偿 / 责任限制 / 付款 / 保密 / 竞业 / 终止 / 管辖 / 数据保护 / 其他 +- **原文位置**:第 X 条 / 第 Y 页(材料未标号时写「未标注」) +- **原文摘录**:用「」引用关键句 +- **风险描述**:≤ 50 字说明风险所在 +- **建议改写**:给出可直接采用的修订版本 + +## 总体评估 + +一段话总结这份合同的整体风险水位与签字建议(≤ 200 字)。 + +要求: +- 不要虚构原文没有的条款 +- 数字与条款编号保留原样 +- 中文输出,不要任何客套或元描述 + +合同标题:{title} + +合同正文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004002, NULL, 1, 'meeting-action-items', '会议纪要 → 行动项', '从会议纪要中穷尽抽取决议 + 行动项(owner / 截止日 / 验收标准),适合周会、决策会议。', '你是会议纪要分析助理。从下面的纪要中穷尽抽取所有行动项与决议,按以下结构输出 Markdown: + +## 决议清单 +按时间或重要性顺序列出每条明确决议;每条 ≤ 一句话。 + +## 行动项清单 + +| 序号 | 行动 | 负责人 | 截止日 | 验收标准 | +|---|---|---|---|---| + +要求: +- 「行动」用动词开头(如「提交」「完成」「对齐」) +- 负责人若未明确写「未指派」 +- 截止日若未明确写「未定」 +- 验收标准一句话写出「做完是什么样」 +- 不要把「讨论了 X」当作行动项 + +## 风险与依赖 +一句话列出会议中提到的潜在阻塞或跨团队依赖(≤ 5 条)。 + +要求:中文,无客套,无元描述。 + +会议主题:{title} + +纪要正文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004003, NULL, 1, 'customer-profile', '客户邮件 / 访谈画像', '把客户邮件、会议纪要、CRM 记录合成一份结构化客户画像,配合企业场景 → 客户情报使用。', '你是销售情报员。从下面的客户邮件 / CRM 记录 / 访谈中提取一份客户画像,按以下结构输出 Markdown: + +## 客户档案 +- **名称**: +- **行业 / 规模**: +- **当前阶段**:潜在 / 沟通中 / 谈判中 / 已成交(若无明确信号写「未知」) +- **决策链关键人**:列出姓名 + 角色 + 倾向 + +## 痛点与机会 +- 3-5 条关键痛点,每条带原文引用 +- 2-3 条潜在切入点 + +## 异议预判 +列出客户可能的反对意见 + 对应应对话术。 + +## 下一步建议 +- 3 条具体动作,按优先级排序,每条带「为什么现在做」 + +要求:不要发明文本没说的事;不确定时写「未提及」。 + +客户:{title} + +原文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004004, NULL, 1, 'competitor-update', '竞品动态摘要', '把新闻 / 产品 release / 招聘信号 / 客户提及合成一份竞品动态简报。', '你是市场情报员。从下面的材料中提取与竞争对手相关的动态,按以下结构输出 Markdown: + +## 涉及对手 +列出材料中提到的所有竞品公司或产品。 + +## 关键动态 + +按时间倒序,每条输出: + +### <对手 / 产品> · <动态简称> +- **类型**:新产品 / 招聘 / 融资 / 客户胜出 / 价格调整 / 团队变动 / 其他 +- **原文摘录**:「」引用 +- **来源**:网页 / 邮件 / 新闻渠道 +- **对我们的影响**:威胁 / 机会 / 中性,一句话说明 + +## 战术建议 +3 条针对性的应对动作,按优先级排序。 + +## 监控建议 +列出值得长期追踪的关键词或信号。 + +要求:中文,不发明内容,不确定时跳过。 + +材料:{title} + +原文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004005, NULL, 1, 'resume-structured-extract', '简历结构化', '把简历提取为标准化档案:教育、工作、技能、亮点。适合批量初筛。', '你是 HR 助理。把下面的简历提取为结构化档案: + +## 候选人信息 +- **姓名**: +- **当前职位**: +- **总工作年限**: +- **专业领域**: + +## 教育经历 + +| 学校 | 学位 / 专业 | 时间 | +|---|---|---| + +## 工作经历 + +按时间倒序,每段输出: + +### <公司> · <职位> · <时间> +- **职责摘要**:≤ 30 字 +- **关键产出**:≤ 3 条 bullet(量化优先) + +## 技能矩阵 + +| 技能 | 熟练度 | +|---|---| + +## 候选人亮点 +一段话归纳最值得关注的 3 件事(≤ 150 字)。 + +要求:不要发明文本没有的经历;不确定写「未提及」;中文输出。注意:不要把性别 / 年龄 / 户籍写进画像。 + +简历:{title} + +原文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004006, NULL, 1, 'incident-postmortem', '事故 5-Why 复盘', '从事故报告 / 时间线生成 5-Why 链 + 整改清单 + 相似事故关键词。适合 SRE / 运维团队。', '你是 SRE 事故复盘助理。从下面的事故报告 / 时间线中输出 5-Why 分析: + +## 事故概要 +- **现象**:1 句话 +- **影响范围**:用户数 / 系统 / 持续时间 +- **触发时间**: + +## 5 Whys 链 + +1. **现象**:… + **Why?** … +2. **Why?** … +3. **Why?** … +4. **Why?** … +5. **根因 (Why?)** … + +## 整改清单 + +| 序号 | 行动 | 负责团队 | 优先级 | 截止 | +|---|---|---|---|---| + +## 相似事故关联 +列出可能相关的历史事故关键词(用于后续 wiki 检索)。 + +## 复盘要点 +3 条最值得团队记住的教训。 + +要求:中文,技术准确,不发明数据。 + +事故:{title} + +报告: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0), + +(1000004007, NULL, 1, 'paper-imrad', '论文 IMRaD 摘要', '把论文 / 技术报告浓缩为 IMRaD 结构化摘要 + 关键术语表,适合研究型团队。', '你是学术摘要助理。把下面的论文 / 技术报告浓缩为 IMRaD 结构化摘要: + +## Introduction +解决什么问题,为什么重要(≤ 100 字) + +## Methods +使用什么方法 / 数据 / 模型(≤ 150 字) + +## Results +最重要的 3-5 个量化或定性结果(每条 ≤ 30 字) + +## Discussion +- **主要洞察**:1-2 句 +- **局限性**:1-2 条 +- **可复现性**:高 / 中 / 低,附 1 句理由 + +## 关键术语 +列出 5-8 个核心术语,每个加一句话定义。 + +要求:保留 LaTeX 公式(如有),不发明结果,中文写作。 + +论文:{title} + +原文: +{input_text}', FALSE, NULL, TRUE, 'page', NOW(), NOW(), 0) +ON CONFLICT (id) DO NOTHING; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V109__wiki_transformation_output_format.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V109__wiki_transformation_output_format.sql new file mode 100644 index 000000000..3da5170bc --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V109__wiki_transformation_output_format.sql @@ -0,0 +1,12 @@ +-- Output format declared on the template. See h2 sibling for the prose +-- explanation. MySQL needs the INFORMATION_SCHEMA guard pattern. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_format' + ) THEN + ALTER TABLE mate_wiki_transformation ADD COLUMN output_format VARCHAR(16) NOT NULL DEFAULT 'markdown'; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql new file mode 100644 index 000000000..1b2780e47 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql @@ -0,0 +1,34 @@ +-- V10: 声明式 Hook 系统 +-- mate_hook hook 定义(YAML 文件或 UI 写入) +-- mate_hook_run hook 触发审计 +-- (原本命名为 V9 但与 V9__usage_cache_tokens.sql 撞号;重命名为 V10 以共存) + +CREATE TABLE IF NOT EXISTS mate_hook ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description VARCHAR(512), + enabled BOOLEAN NOT NULL DEFAULT TRUE, + event_type VARCHAR(64) NOT NULL, + match_expression TEXT, + action_kind VARCHAR(32) NOT NULL, + action_config TEXT NOT NULL, + rate_limit_per_min INT DEFAULT 60, + timeout_ms INT DEFAULT 3000, + source VARCHAR(16) DEFAULT 'db', + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_hook_event_type ON mate_hook (event_type); +CREATE INDEX IF NOT EXISTS idx_hook_enabled ON mate_hook (enabled); + +CREATE TABLE IF NOT EXISTS mate_hook_run ( + id BIGINT NOT NULL PRIMARY KEY, + hook_id BIGINT NOT NULL, + event_type VARCHAR(64) NOT NULL, + status VARCHAR(16) NOT NULL, + duration_ms INT DEFAULT 0, + message VARCHAR(512), + created_at TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_hook_run_hook_id ON mate_hook_run (hook_id); +CREATE INDEX IF NOT EXISTS idx_hook_run_created ON mate_hook_run (created_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V110__wiki_transformation_run_tokens.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V110__wiki_transformation_run_tokens.sql new file mode 100644 index 000000000..83ea8ac9a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V110__wiki_transformation_run_tokens.sql @@ -0,0 +1,31 @@ +-- Record per-run token usage. See h2 sibling for prose explanation. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'input_tokens' + ) THEN + ALTER TABLE mate_wiki_transformation_run ADD COLUMN input_tokens BIGINT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'output_tokens' + ) THEN + ALTER TABLE mate_wiki_transformation_run ADD COLUMN output_tokens BIGINT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'total_tokens' + ) THEN + ALTER TABLE mate_wiki_transformation_run ADD COLUMN total_tokens BIGINT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V111__wiki_transformation_output_schema.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V111__wiki_transformation_output_schema.sql new file mode 100644 index 000000000..2c7287600 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V111__wiki_transformation_output_schema.sql @@ -0,0 +1,11 @@ +-- Optional JSON Schema column. See h2 sibling for the prose explanation. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_schema' + ) THEN + ALTER TABLE mate_wiki_transformation ADD COLUMN output_schema TEXT DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V112__skill_file.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V112__skill_file.sql new file mode 100644 index 000000000..13d402253 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V112__skill_file.sql @@ -0,0 +1,26 @@ +-- V112: persist skill bundle files (scripts/ + references/) in the database. +-- +-- Until now scripts/references only lived on the local filesystem of whichever +-- node handled the upload. Multi-instance deployments sharing one MySQL would +-- have the skill row visible everywhere but the script files only on one node, +-- so any other node attempting to run a skill script either failed or ran a +-- stale local copy. Treating the database as the canonical bundle store and +-- the filesystem as a materialized cache resolves that gap and matches the +-- existing pattern for SKILL.md (canonical in mate_skill.skill_content, +-- mirrored to disk by the workspace manager). +-- +-- TEXT (16MB) comfortably covers the per-file 1MB cap enforced by +-- ZipSkillFetcher and the 50MB total bundle cap. + +CREATE TABLE IF NOT EXISTS mate_skill_file ( + id BIGINT NOT NULL PRIMARY KEY, + skill_id BIGINT NOT NULL, + file_path VARCHAR(512) NOT NULL, + content TEXT, + content_size INT NOT NULL DEFAULT 0, + sha256 CHAR(64), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_skill_file_path ON mate_skill_file (skill_id, file_path); +CREATE INDEX IF NOT EXISTS idx_skill_file_skill ON mate_skill_file (skill_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V113__workspace_memory_search_and_async_indexes.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V113__workspace_memory_search_and_async_indexes.sql new file mode 100644 index 000000000..70555f60f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V113__workspace_memory_search_and_async_indexes.sql @@ -0,0 +1,15 @@ +-- Indexes for two recently-added read paths. Mirrors the H2 file in this +-- migration set; PostgreSQL supports CREATE INDEX IF NOT EXISTS natively. +-- +-- (1) idx_workspace_file_agent_filename — accelerates the memory search tool +-- on mate_workspace_file ("agent_id = ? AND filename LIKE 'prefix%' AND +-- content LIKE '%term%'"). +-- +-- (2) idx_async_task_conv_status — accelerates listActiveTasks(conversationId) +-- ("WHERE conversation_id = ? AND status IN ('pending', 'running')"). +-- The existing single-column idx_async_task_conv left the status filter +-- to a row scan; the compound resolves both in one index seek. + +CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_filename ON mate_workspace_file (agent_id, filename); + +CREATE INDEX IF NOT EXISTS idx_async_task_conv_status ON mate_async_task (conversation_id, status); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V114__conversation_pinned.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V114__conversation_pinned.sql new file mode 100644 index 000000000..62a01304b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V114__conversation_pinned.sql @@ -0,0 +1,12 @@ +-- Per-conversation pin flag. See the h2 sibling for the rationale. +-- MySQL has no ADD COLUMN IF NOT EXISTS; guard via INFORMATION_SCHEMA. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_conversation' AND column_name = 'pinned' + ) THEN + ALTER TABLE mate_conversation ADD COLUMN pinned INT DEFAULT 0; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V115__xai_grok_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V115__xai_grok_provider.sql new file mode 100644 index 000000000..4e1384bda --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V115__xai_grok_provider.sql @@ -0,0 +1,35 @@ +-- V115: register the xAI (Grok) provider plus its Grok 3 / Grok 4 model catalog. +-- +-- See the H2 copy for full background. The MySQL copy uses INSERT ... ON +-- DUPLICATE KEY UPDATE; the api_key column is intentionally omitted from the +-- update list so existing deployments that have already configured a key keep it. + +-- -- Provider -------------------------------------------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('xai', 'xAI (Grok)', 'xai-', 'OpenAIChatModel', '', 'https://api.x.ai/v1', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + api_key_prefix = EXCLUDED.api_key_prefix, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Model catalog --------------------------------------------------------- +-- IDs use the 1000000340-1000000343 block reserved for xAI so future Grok +-- additions can grow contiguously. +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000000340, 'Grok 4', 'xai', 'grok-4', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000341, 'Grok 4 Fast', 'xai', 'grok-4-fast', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000342, 'Grok 3', 'xai', 'grok-3', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000343, 'Grok 3 Mini', 'xai', 'grok-3-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V116__conversation_model.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V116__conversation_model.sql new file mode 100644 index 000000000..6eac1b08c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V116__conversation_model.sql @@ -0,0 +1,5 @@ +-- See the H2 file for context. KingbaseES (PostgreSQL) supports +-- ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS model_provider VARCHAR(64); +ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS model_name VARCHAR(128); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V117__skill_lifecycle.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V117__skill_lifecycle.sql new file mode 100644 index 000000000..4416fd348 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V117__skill_lifecycle.sql @@ -0,0 +1,25 @@ +-- See the H2 file for context. KingbaseES (PostgreSQL) supports +-- both ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS natively. +-- +-- Column types: archived_at / last_activity_at use TIMESTAMP(3) to match +-- mate_skill_usage_stat.last_loaded_at; lifecycle_state VARCHAR(16); +-- pinned SMALLINT. + +ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS lifecycle_state VARCHAR(16) DEFAULT 'active'; +ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS pinned BOOLEAN DEFAULT FALSE; +ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS archived_at TIMESTAMP(3) NULL; +ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS last_activity_at TIMESTAMP(3) NULL; + +CREATE INDEX IF NOT EXISTS idx_skill_lifecycle_state ON mate_skill (lifecycle_state); +CREATE INDEX IF NOT EXISTS idx_skill_last_activity_at ON mate_skill (last_activity_at); + +-- One-time backfill: existing rows take their newest usage tick as the +-- activity anchor. Rows with no usage stat stay NULL and fall through to +-- create_time at query time via the anchor() helper. +UPDATE mate_skill SET last_activity_at = ( + SELECT MAX(last_loaded_at) FROM mate_skill_usage_stat s + WHERE s.skill_name = mate_skill.name +) +WHERE last_activity_at IS NULL; + +UPDATE mate_skill SET lifecycle_state = 'active' WHERE lifecycle_state IS NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V118__purge_model_config_tombstones.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V118__purge_model_config_tombstones.sql new file mode 100644 index 000000000..d4a6371f9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V118__purge_model_config_tombstones.sql @@ -0,0 +1,15 @@ +-- V118: Purge residual deleted=1 rows from mate_model_config. +-- +-- V20 retired soft-delete project-wide and physically deleted every deleted=1 +-- row that existed at that point. V81 then re-introduced an isolated tombstone +-- on id=1000000172 (the bogus 'qwen3-plus' catalog entry) -- keeping the row +-- around as a logical audit trail. The runtime never re-uses ids and treats +-- deleted as a vestige now that @TableLogic is gone, so a tombstoned row +-- has no value beyond leaking into LambdaQueryWrapper queries that don't +-- explicitly filter deleted=0. +-- +-- The validateModel uniqueness check fix in #173 plugged one such leak; this +-- migration eliminates the underlying class of bug by aligning mate_model_config +-- with the V20 hard-delete posture. Idempotent: a no-op on databases without +-- tombstones. +DELETE FROM mate_model_config WHERE deleted = 1; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V119__channel_tool_support.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V119__channel_tool_support.sql new file mode 100644 index 000000000..28859d81c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V119__channel_tool_support.sql @@ -0,0 +1,32 @@ +-- V100__channel_tool_support.sql (KingbaseES dialect) +-- +-- Mirror of the H2 V100, adapted for KingbaseES (PostgreSQL-compatible). +-- PostgreSQL supports ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS +-- natively, so guards are not needed. + +-- 1. Add channel_id column if missing +ALTER TABLE mate_tool ADD COLUMN IF NOT EXISTS channel_id BIGINT NULL; + +-- 2. Add channel-id index if missing +CREATE INDEX IF NOT EXISTS idx_mate_tool_channel ON mate_tool (channel_id); + +-- 3. Deduplicate same-name rows before adding the unique index — keep +-- deleted=0 first, then most recently updated, then largest id. +DELETE FROM mate_tool +WHERE id IN ( + SELECT id FROM ( + SELECT id, + ROW_NUMBER() OVER ( + PARTITION BY name + ORDER BY + CASE WHEN deleted = 0 THEN 0 ELSE 1 END, + update_time DESC, + id DESC + ) AS rn + FROM mate_tool + ) ranked + WHERE rn > 1 +); + +-- 4. Add unique index on name if missing +CREATE UNIQUE INDEX IF NOT EXISTS uk_mate_tool_name ON mate_tool (name); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V11__skill_synthesis.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V11__skill_synthesis.sql new file mode 100644 index 000000000..f67e536d1 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V11__skill_synthesis.sql @@ -0,0 +1,23 @@ +-- V11: Auto Skill Synthesis (RFC-023) +-- Agent 自治创建 skill 后记录来源对话和安全扫描状态 +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'source_conversation_id' + ) THEN + ALTER TABLE mate_skill ADD COLUMN source_conversation_id VARCHAR(64) DEFAULT NULL; + END IF; +END $$; + +-- security_scan_status: NULL(旧数据/手动创建) / PASSED / FAILED +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'security_scan_status' + ) THEN + ALTER TABLE mate_skill ADD COLUMN security_scan_status VARCHAR(16) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql new file mode 100644 index 000000000..8d855685b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql @@ -0,0 +1,75 @@ +-- Persistent goal — see h2/V120__agent_goal.sql for full design notes. +-- +-- Kingbase/PostgreSQL differences vs H2: +-- 1. CLOB -> TEXT +-- 2. TIMESTAMP -> TIMESTAMP(3) for millisecond precision matching V117 +-- 3. BOOLEAN -> SMALLINT +-- 4. H2 uses a PREDICATE unique index for "one active goal per +-- conversation"; PostgreSQL does not support filtered unique indexes, +-- so we emulate it with a STORED generated column that is NULL for +-- non-active rows + a plain unique index. NULLs are excluded from +-- uniqueness enforcement by PostgreSQL's default index semantics. + +CREATE TABLE IF NOT EXISTS mate_agent_goal ( + id BIGINT NOT NULL, + conversation_id VARCHAR(64) NOT NULL, + agent_id BIGINT NOT NULL, + workspace_id BIGINT NOT NULL, + created_by VARCHAR(64) NOT NULL, + + title VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + exit_criteria TEXT NULL, + success_check_prompt TEXT NULL, + + -- DB values are always lowercase (active|paused|completed|abandoned| + -- exhausted) — enforced by the GoalStatus enum's @EnumValue + -- annotation. The active_conv_key generated column below depends on + -- this convention; any uppercase write would defeat uniqueness. + status VARCHAR(16) NOT NULL DEFAULT 'active', + + turn_budget INT NOT NULL DEFAULT 20, + turns_used INT NOT NULL DEFAULT 0, + llm_call_budget INT NOT NULL DEFAULT 200, + agent_llm_calls_used INT NOT NULL DEFAULT 0, + eval_llm_calls_used INT NOT NULL DEFAULT 0, + + progress_summary TEXT NULL, + completion_score DOUBLE PRECISION NULL, + last_evaluation_at TIMESTAMP(3) NULL, + + auto_followup_enabled BOOLEAN NOT NULL DEFAULT FALSE, + followup_cooldown_seconds INT NOT NULL DEFAULT 0, + last_followup_at TIMESTAMP(3) NULL, + + -- Virtual generated column: NULL for non-active or deleted rows so + -- they fall out of the unique-index check. PostgreSQL ignores NULL keys + -- for uniqueness, giving us "at most one active row per conversation". + active_conv_key VARCHAR(80) + GENERATED ALWAYS AS ( + CASE WHEN status = 'active' AND deleted = 0 + THEN conversation_id ELSE NULL END + ) STORED, + + version INT NOT NULL DEFAULT 0, + deleted SMALLINT NOT NULL DEFAULT 0, + create_time TIMESTAMP(3) NOT NULL, + update_time TIMESTAMP(3) NOT NULL, + + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_goal_active_conv ON mate_agent_goal (active_conv_key); +CREATE INDEX IF NOT EXISTS idx_agent_goal_conv ON mate_agent_goal (conversation_id, status); +CREATE INDEX IF NOT EXISTS idx_agent_goal_status ON mate_agent_goal (status, last_evaluation_at); +CREATE INDEX IF NOT EXISTS idx_agent_goal_owner ON mate_agent_goal (created_by, status); + +CREATE TABLE IF NOT EXISTS mate_agent_goal_event ( + id BIGINT NOT NULL, + goal_id BIGINT NOT NULL, + event_type VARCHAR(32) NOT NULL, + message_id BIGINT NULL, + detail_json TEXT NULL, + create_time TIMESTAMP(3) NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX IF NOT EXISTS idx_agent_goal_event_goal ON mate_agent_goal_event (goal_id, id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V121__tool_disclosure_tier.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V121__tool_disclosure_tier.sql new file mode 100644 index 000000000..590ccde58 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V121__tool_disclosure_tier.sql @@ -0,0 +1,20 @@ +-- V121__tool_disclosure_tier.sql (KingbaseES dialect) +-- +-- Mirror of the H2 V121, adapted for KingbaseES (PostgreSQL-compatible). +-- PostgreSQL supports ADD COLUMN IF NOT EXISTS natively. +-- +-- See the H2 file for the column semantics. + +-- 1. mate_tool.disclosure_tier (default 'core') +ALTER TABLE mate_tool ADD COLUMN IF NOT EXISTS disclosure_tier VARCHAR(16) DEFAULT 'core'; + +-- 2. mate_mcp_server.disclosure_tier (default 'core' — MCP tools stay directly +-- callable; an admin can move a noisy server to extension) +ALTER TABLE mate_mcp_server ADD COLUMN IF NOT EXISTS disclosure_tier VARCHAR(16) DEFAULT 'core'; + +-- 3. Seed the heavy generative / browser tools as extension. +-- mate_tool.name stores the Java class name (not the @Tool function name). +UPDATE mate_tool +SET disclosure_tier = 'extension' +WHERE name IN ('ImageGenerateTool', 'MusicGenerateTool', 'VideoGenerateTool', 'Model3dGenerateTool', 'BrowserUseTool') + AND (disclosure_tier IS NULL OR disclosure_tier = 'core'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V122__fix_generative_tool_tier_names.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V122__fix_generative_tool_tier_names.sql new file mode 100644 index 000000000..75b8d235c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V122__fix_generative_tool_tier_names.sql @@ -0,0 +1,13 @@ +-- V122__fix_generative_tool_tier_names.sql (MySQL dialect) +-- +-- Corrective migration. The first cut of V121 seeded the generative / browser +-- tools as extension using their @Tool function names (image_generate, ...), +-- but mate_tool.name stores the Java class name (ImageGenerateTool, ...), so the +-- UPDATE matched no rows on databases that ran that early version. Re-apply the +-- seed by class name. Idempotent: only promotes core → extension and leaves any +-- admin-set value untouched. + +UPDATE mate_tool +SET disclosure_tier = 'extension' +WHERE name IN ('ImageGenerateTool', 'MusicGenerateTool', 'VideoGenerateTool', 'Model3dGenerateTool', 'BrowserUseTool') + AND (disclosure_tier IS NULL OR disclosure_tier = 'core'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V123__conversation_progress_ledger.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V123__conversation_progress_ledger.sql new file mode 100644 index 000000000..6993f5fc7 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V123__conversation_progress_ledger.sql @@ -0,0 +1,5 @@ +-- V100: per-conversation progress ledger (see the H2 copy for full background). +-- +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS progress_ledger TEXT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V124__agent_max_iterations_150.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V124__agent_max_iterations_150.sql new file mode 100644 index 000000000..a8a552077 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V124__agent_max_iterations_150.sql @@ -0,0 +1,6 @@ +-- V124: bump default agents' max_iterations 100 → 150 (see H2 copy for full +-- background). Same idempotent UPDATE — H2 and MySQL accept identical syntax +-- for this UPDATE so no dialect-specific guard is needed. + +UPDATE mate_agent SET max_iterations = 150 +WHERE id IN (1000000001, 1000000002, 1000000003) AND max_iterations = 100; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V125__agent_workspace_base_path.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V125__agent_workspace_base_path.sql new file mode 100644 index 000000000..a434a987f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V125__agent_workspace_base_path.sql @@ -0,0 +1,4 @@ +-- V125: Add workspace_base_path column to mate_agent for Agent-level directory override. +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS workspace_base_path VARCHAR(512) DEFAULT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V126__agent_binding_disabled_flags.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V126__agent_binding_disabled_flags.sql new file mode 100644 index 000000000..adabf9969 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V126__agent_binding_disabled_flags.sql @@ -0,0 +1,13 @@ +-- V126: Two binding-mode flags on mate_agent (KingbaseES). +-- +-- skills_disabled / tools_disabled flip the "zero binding rows" semantic from +-- "inherit every globally-enabled capability" to "this agent has explicitly +-- opted out". Without these columns, an operator who wanted an agent with no +-- skills had to bind a dummy skill — otherwise the runtime fell back to the +-- global default and every skill's catalog entry got injected into the system +-- prompt (issue #184). +-- +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS skills_disabled BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS tools_disabled BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V127__approval_auto_grant.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V127__approval_auto_grant.sql new file mode 100644 index 000000000..399a239fb --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V127__approval_auto_grant.sql @@ -0,0 +1,25 @@ +-- V127: Approval auto-grant table (MySQL dialect). +-- Idempotent: outer CREATE TABLE uses IF NOT EXISTS; inline KEY clauses +-- only execute on first creation, so re-running this migration is safe. +CREATE TABLE IF NOT EXISTS mate_approval_grant ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + scope_type VARCHAR(32) NOT NULL, + scope_id VARCHAR(64) NOT NULL, + tool_name VARCHAR(128) DEFAULT NULL, + rule_id VARCHAR(128) DEFAULT NULL, + max_severity VARCHAR(16) NOT NULL, + grant_kind VARCHAR(24) NOT NULL, + expire_at TIMESTAMP DEFAULT NULL, + granted_by BIGINT NOT NULL, + granted_at TIMESTAMP NOT NULL, + revoked SMALLINT NOT NULL DEFAULT 0, + revoked_by BIGINT DEFAULT NULL, + revoked_at TIMESTAMP DEFAULT NULL, + note VARCHAR(500) DEFAULT NULL, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_grant_scope ON mate_approval_grant (workspace_id, scope_type, scope_id, tool_name, revoked, deleted); +CREATE INDEX IF NOT EXISTS idx_grant_expire ON mate_approval_grant (expire_at, revoked, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V128__approval_resolution_log.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V128__approval_resolution_log.sql new file mode 100644 index 000000000..af437a349 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V128__approval_resolution_log.sql @@ -0,0 +1,25 @@ +-- V128: Approval resolution log table (MySQL dialect). +CREATE TABLE IF NOT EXISTS mate_approval_resolution_log ( + id BIGINT NOT NULL PRIMARY KEY, + -- Nullable: HARD_BLOCK can fire before workspace resolution; see H2 migration + -- for full rationale. Per-workspace Dashboard queries filter on workspace_id + -- and skip null rows; the global HARD_BLOCK panel surfaces them. + workspace_id BIGINT DEFAULT NULL, + conversation_id VARCHAR(128) DEFAULT NULL, + agent_id VARCHAR(64) DEFAULT NULL, + user_id VARCHAR(64) DEFAULT NULL, + tool_call_id VARCHAR(64) DEFAULT NULL, + tool_name VARCHAR(128) NOT NULL, + max_severity VARCHAR(16) DEFAULT NULL, + rule_ids VARCHAR(512) DEFAULT NULL, + decision_source VARCHAR(24) NOT NULL, + grant_id BIGINT DEFAULT NULL, + pending_id VARCHAR(32) DEFAULT NULL, + args_preview VARCHAR(500) DEFAULT NULL, + note VARCHAR(500) DEFAULT NULL, + create_time TIMESTAMP NOT NULL, + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_resolution_workspace_time ON mate_approval_resolution_log (workspace_id, create_time); +CREATE INDEX IF NOT EXISTS idx_resolution_grant ON mate_approval_resolution_log (grant_id); +CREATE INDEX IF NOT EXISTS idx_resolution_pending ON mate_approval_resolution_log (pending_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V129__wiki_page_broken_links.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V129__wiki_page_broken_links.sql new file mode 100644 index 000000000..a2a2de8d4 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V129__wiki_page_broken_links.sql @@ -0,0 +1,7 @@ +-- V129: Persisted wikilink lint state — KingbaseES dialect. +-- +-- See h2/V129__wiki_page_broken_links.sql for column semantics. +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS broken_links TEXT DEFAULT NULL; +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS broken_links_scanned_at TIMESTAMP(3) DEFAULT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V12__wiki_chunk_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V12__wiki_chunk_table.sql new file mode 100644 index 000000000..5276d4344 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V12__wiki_chunk_table.sql @@ -0,0 +1,22 @@ +-- V12: Wiki chunk persistence (RFC-013 minimal slice → enables RFC-011 embedding) +CREATE TABLE IF NOT EXISTS mate_wiki_chunk ( + id BIGINT NOT NULL PRIMARY KEY, + kb_id BIGINT NOT NULL, + raw_id BIGINT NOT NULL, + ordinal INT NOT NULL, + content TEXT NOT NULL, + char_count INT NOT NULL, + start_offset INT NOT NULL, + end_offset INT NOT NULL, + content_hash VARCHAR(64) NOT NULL, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- MySQL lacks CREATE INDEX IF NOT EXISTS; use INFORMATION_SCHEMA.STATISTICS guard instead. +CREATE INDEX IF NOT EXISTS idx_wiki_chunk_kb ON mate_wiki_chunk (kb_id); + +CREATE INDEX IF NOT EXISTS idx_wiki_chunk_raw ON mate_wiki_chunk (raw_id); + +CREATE INDEX IF NOT EXISTS idx_wiki_chunk_hash ON mate_wiki_chunk (content_hash); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V130__agent_primary_kb.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V130__agent_primary_kb.sql new file mode 100644 index 000000000..b2e534736 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V130__agent_primary_kb.sql @@ -0,0 +1,26 @@ +-- V129: Store the per-agent primary wiki KB on mate_agent (KingbaseES). +-- +-- Knowledge bases remain workspace-shared; this field only chooses the +-- default KB for wiki tools when no kbName/kbId is specified. + +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS primary_kb_id BIGINT DEFAULT NULL; +CREATE INDEX IF NOT EXISTS idx_agent_primary_kb ON mate_agent (primary_kb_id); + +UPDATE mate_agent a +SET primary_kb_id = ( + SELECT kb.id + FROM mate_wiki_knowledge_base kb + WHERE kb.agent_id = a.id + AND (kb.workspace_id IS NULL OR kb.workspace_id = a.workspace_id) + AND kb.deleted = 0 + ORDER BY kb.update_time DESC + LIMIT 1 +) +WHERE a.primary_kb_id IS NULL + AND EXISTS ( + SELECT 1 + FROM mate_wiki_knowledge_base kb + WHERE kb.agent_id = a.id + AND (kb.workspace_id IS NULL OR kb.workspace_id = a.workspace_id) + AND kb.deleted = 0 + ); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V131__claude_48_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V131__claude_48_models.sql new file mode 100644 index 000000000..2e1b15d20 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V131__claude_48_models.sql @@ -0,0 +1,36 @@ +-- Add Claude Opus 4.8 (regular + -fast variant) model entries to +-- mate_model_config for existing deployments. New installs pick these up via +-- DatabaseBootstrapRunner from data-mysql-{en,zh}.sql; this migration covers +-- operators who already have earlier Flyway versions applied. +-- +-- Claude 4.8 inherits 4.7's strict API contract: temperature / top_p / top_k +-- must be NULL (otherwise HTTP 400), and the "xhigh" thinking tier is +-- available. Both are handled in AnthropicChatModelBuilder via the +-- isClaude47OrLater() detector. +-- +-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert. +-- Same V number is used in h2/ for cross-dialect parity. + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES +-- Direct Anthropic +(1000000290, 'Claude Opus 4.8', 'anthropic', 'claude-opus-4-8', 'Anthropic Claude Opus 4.8 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000291, 'Claude Opus 4.8 Fast', 'anthropic', 'claude-opus-4-8-fast', 'Claude Opus 4.8 fast variant (higher output speed, 2x pricing)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +-- OpenRouter passthrough +(1000000292, 'Claude Opus 4.8', 'openrouter', 'anthropic/claude-opus-4-8', 'Claude Opus 4.8 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000293, 'Claude Opus 4.8 Fast', 'openrouter', 'anthropic/claude-opus-4-8-fast', 'Claude Opus 4.8 fast variant via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +-- Claude Code OAuth (Pro/Max subscription) +(1000000294, 'Claude Opus 4.8', 'anthropic-claude-code', 'claude-opus-4-8', 'Claude Opus 4.8 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + provider = EXCLUDED.provider, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + temperature = EXCLUDED.temperature, + max_tokens = EXCLUDED.max_tokens, + top_p = EXCLUDED.top_p, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V132__memory_consolidation_cron_tier_discipline.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V132__memory_consolidation_cron_tier_discipline.sql new file mode 100644 index 000000000..dc961ad07 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V132__memory_consolidation_cron_tier_discipline.sql @@ -0,0 +1,13 @@ +-- Update the daily "memory consolidation" cron prompt on existing databases so it +-- keeps project-specific volatile facts (codenames, tech stacks, per-project +-- decisions) out of the always-on MEMORY.md. Seed scripts only run on fresh +-- installs, so existing rows need this data migration to pick up the new wording. +-- Scoped to the original default text so user-edited prompts are left untouched. + +UPDATE mate_cron_job +SET trigger_message = '请回顾你最近的 memory/ 日记文件,将反复出现的重要信息(用户偏好、稳定事实、经验教训、工作流)提炼整合到 MEMORY.md 中。注意:MEMORY.md 会被注入每一次对话,只整合跨项目长期稳定的信息;具体项目的代号、名称、技术栈、仓库、单项目的指标/预算/团队/上线日期或只对某个项目成立的决策等易变事实,不要写入 MEMORY.md(会随项目切换互相冲突、导致张冠李戴),应留在 daily note 或通过结构化 project 记忆维护。判定口诀:换一个项目后仍成立才进 MEMORY.md。保留日记原文不动,只更新 MEMORY.md。完成后简要说明做了哪些整合。' +WHERE trigger_message = '请回顾你最近的 memory/ 日记文件,将反复出现的重要信息(用户偏好、稳定事实、经验教训、工作流)提炼整合到 MEMORY.md 中。保留日记原文不动,只更新 MEMORY.md。完成后简要说明做了哪些整合。'; + +UPDATE mate_cron_job +SET trigger_message = 'Review your recent memory/ daily note files and consolidate recurring important information (user preferences, stable facts, lessons learned, workflows) into MEMORY.md. Note: MEMORY.md is injected into every conversation, so only consolidate cross-project, long-term stable information; do NOT write project-specific volatile facts into MEMORY.md (project codenames, names, tech stacks, repos, a single project''s metrics/budget/team/launch date, or decisions that hold only for one project) — they conflict across projects and cause mix-ups. Keep those in the daily note or maintain them via structured project memory. Rule of thumb: only facts that still hold after switching projects belong in MEMORY.md. Keep the original daily notes intact, only update MEMORY.md. Briefly describe what consolidations were made.' +WHERE trigger_message = 'Review your recent memory/ daily note files and consolidate recurring important information (user preferences, stable facts, lessons learned, workflows) into MEMORY.md. Keep the original daily notes intact, only update MEMORY.md. Briefly describe what consolidations were made.'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V133__wiki_agent_page_type_permission.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V133__wiki_agent_page_type_permission.sql new file mode 100644 index 000000000..7710d8638 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V133__wiki_agent_page_type_permission.sql @@ -0,0 +1,27 @@ +-- V133: Per-agent, per-KB, per-pageType permission for wiki tools. +-- Read permission filters retrieval/listing; write permission gates the +-- create/compile/delete/archive/enrich/transformation tools. A row with +-- page_type='*' is the agent's KB-wide default; an exact page_type row is +-- more specific and wins over '*'. Unconfigured (no rows) falls back to the +-- KB-level defaultReadPolicy stored in the KB config. + +CREATE TABLE IF NOT EXISTS mate_wiki_agent_page_type_permission ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + page_type VARCHAR(64) NOT NULL, + -- SMALLINT (not BOOLEAN): the entity fields WikiAgentPageTypePermissionEntity + -- .can{Read,Create,Update,Delete} are Integer (1/0). Vanilla PostgreSQL cannot + -- map a BOOLEAN into a JDBC int, so these must stay integer-typed. Do not + -- "normalize" to BOOLEAN to match the other flag columns. + can_read SMALLINT NOT NULL DEFAULT 1, + can_create SMALLINT NOT NULL DEFAULT 0, + can_update SMALLINT NOT NULL DEFAULT 0, + can_delete SMALLINT NOT NULL DEFAULT 0, + write_policy VARCHAR(32) NOT NULL DEFAULT 'approval_required', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_agent_ptperm ON mate_wiki_agent_page_type_permission (agent_id, kb_id, page_type, deleted); +CREATE INDEX IF NOT EXISTS idx_wiki_ptperm_agent_kb ON mate_wiki_agent_page_type_permission (agent_id, kb_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql new file mode 100644 index 000000000..845f9510a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql @@ -0,0 +1,81 @@ +-- V134: KB-scoped pageType profile + structured page metadata columns. +-- See the H2 file for the design rationale. PostgreSQL/KingbaseES uses a STORED +-- generated column for the "one enabled profile per KB" constraint, and an +-- information_schema guard for each idempotent ADD COLUMN (PostgreSQL has no +-- ADD COLUMN IF NOT EXISTS). + +CREATE TABLE IF NOT EXISTS mate_wiki_page_type_profile ( + id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + name VARCHAR(128) NOT NULL, + version INT NOT NULL DEFAULT 1, + config_json TEXT NOT NULL, + -- SMALLINT (not BOOLEAN): WikiPageTypeProfileEntity.enabled is Integer (1/0). + -- Vanilla PostgreSQL cannot map a BOOLEAN into a JDBC int. The generated + -- column below compares enabled = 1 accordingly. Do not switch to BOOLEAN. + enabled SMALLINT NOT NULL DEFAULT 1, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0, + -- Yields kb_id only for the live-enabled row; NULL otherwise. PostgreSQL + -- ignores NULL keys for uniqueness, giving "at most one enabled per KB". + enabled_kb BIGINT + GENERATED ALWAYS AS ( + CASE WHEN enabled = 1 AND deleted = 0 THEN kb_id ELSE NULL END + ) STORED, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_ptprofile_name ON mate_wiki_page_type_profile (kb_id, name, deleted); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_ptprofile_enabled ON mate_wiki_page_type_profile (enabled_kb); +CREATE INDEX IF NOT EXISTS idx_wiki_ptprofile_kb ON mate_wiki_page_type_profile (kb_id, enabled, deleted); + +-- Structured page metadata columns (idempotent adds). +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_json' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN metadata_json TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_validation_status' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_status VARCHAR(32); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_validation_json' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_json TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'template_key' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN template_key VARCHAR(128); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'profile_version' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN profile_version INT; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql new file mode 100644 index 000000000..fe6bdd70d --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql @@ -0,0 +1,58 @@ +-- V135: Layered knowledge (fact / experience) + page dependency graph. +-- See the H2 file for rationale. MySQL uses INFORMATION_SCHEMA guards for the +-- idempotent column adds. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'knowledge_layer' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN knowledge_layer VARCHAR(16); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'depends_on_json' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN depends_on_json TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'stale' + ) THEN + -- SMALLINT (not BOOLEAN): WikiPageEntity.stale is Integer (1/0). Vanilla + -- PostgreSQL cannot map a BOOLEAN into a JDBC int. Do not switch to BOOLEAN. + ALTER TABLE mate_wiki_page ADD COLUMN stale SMALLINT NOT NULL DEFAULT 0; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'stale_reason_json' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN stale_reason_json TEXT; + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS mate_wiki_page_dependency ( + id BIGINT NOT NULL PRIMARY KEY, + kb_id BIGINT NOT NULL, + page_id BIGINT NOT NULL, + depends_on_page_id BIGINT NOT NULL, + dependency_type VARCHAR(32) NOT NULL DEFAULT 'fact', + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_page_dep ON mate_wiki_page_dependency (page_id, depends_on_page_id, dependency_type, deleted); +CREATE INDEX IF NOT EXISTS idx_wiki_page_dep_reverse ON mate_wiki_page_dependency (kb_id, depends_on_page_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql new file mode 100644 index 000000000..d022e0948 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql @@ -0,0 +1,57 @@ +-- V136: Wiki pipeline runtime — definitions, runs, and per-step runs. +-- See the H2 file for design rationale. + +CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_definition ( + id BIGINT NOT NULL PRIMARY KEY, + kb_id BIGINT NOT NULL, + name VARCHAR(128) NOT NULL, + owner_agent_id BIGINT NOT NULL, + trigger_type VARCHAR(32) NOT NULL, + trigger_config_json TEXT, + steps_json TEXT NOT NULL, + dedup_window_seconds INT NOT NULL DEFAULT 0, + -- SMALLINT (not BOOLEAN): WikiPipelineDefinitionEntity.enabled is Integer (1/0). + -- Vanilla PostgreSQL cannot map a BOOLEAN into a JDBC int. Do not switch to BOOLEAN. + enabled SMALLINT NOT NULL DEFAULT 1, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_pipeline_def_name ON mate_wiki_pipeline_definition (kb_id, name, deleted); +CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_def_trigger ON mate_wiki_pipeline_definition (kb_id, trigger_type, enabled, deleted); + +CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_run ( + id BIGINT NOT NULL PRIMARY KEY, + definition_id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + status VARCHAR(16) NOT NULL, + trigger_type VARCHAR(32) NOT NULL, + trigger_subject VARCHAR(128) NOT NULL, + trigger_bucket VARCHAR(64) NOT NULL, + trigger_payload_json TEXT, + input_json TEXT, + output_json TEXT, + error_message VARCHAR(2048), + started_at TIMESTAMP(3), + finished_at TIMESTAMP(3), + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_pipeline_run_dedup ON mate_wiki_pipeline_run (definition_id, trigger_type, trigger_subject, trigger_bucket, deleted); +CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_run_def ON mate_wiki_pipeline_run (definition_id, status); + +CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_step_run ( + id BIGINT NOT NULL PRIMARY KEY, + run_id BIGINT NOT NULL, + step_id VARCHAR(128) NOT NULL, + executor VARCHAR(32) NOT NULL, + status VARCHAR(16) NOT NULL, + input_json TEXT, + output_json TEXT, + error_message VARCHAR(2048), + started_at TIMESTAMP(3), + finished_at TIMESTAMP(3), + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_step_run ON mate_wiki_pipeline_step_run (run_id, status); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V137__memory_owner_scope.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V137__memory_owner_scope.sql new file mode 100644 index 000000000..ed7a00337 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V137__memory_owner_scope.sql @@ -0,0 +1,107 @@ +-- V137: Per-owner memory isolation with a three-state visibility scope (MySQL). +-- +-- See the H2 counterpart for the full rationale. MySQL has no +-- "ADD COLUMN IF NOT EXISTS", so each column/index is guarded with an +-- INFORMATION_SCHEMA existence check + prepared statement for idempotency. +-- +-- Existing rows are backfilled to scope='TEAM' by the NOT NULL DEFAULT so that +-- upgrading does NOT hide previously-shared memory. + +-- ---------- mate_workspace_file ---------- +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_workspace_file' AND column_name = 'owner_key' + ) THEN + ALTER TABLE mate_workspace_file ADD COLUMN owner_key VARCHAR(128) NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_workspace_file' AND column_name = 'scope' + ) THEN + ALTER TABLE mate_workspace_file ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM'; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_workspace_file_scope_owner ON mate_workspace_file (agent_id, scope, owner_key); + +-- Shared rows use the '' sentinel (not NULL) so the unique index below treats +-- one shared row per filename as a single slot (NULLs are distinct in unique indexes). +UPDATE mate_workspace_file SET owner_key = '' WHERE owner_key IS NULL; + +-- De-duplicate before adding the unique index: the table never had a unique +-- constraint and the service layer was check-then-insert, so historical +-- duplicates may exist. Keep the most recently inserted row per +-- (agent_id, filename, owner_key); drop the rest. The extra derived-table wrap +-- is required so MySQL doesn't reject selecting from the table being deleted. +-- +-- IRREVERSIBLE: this keeps MAX(id) (newest row) and PERMANENTLY deletes the +-- other rows in a duplicate group — their content / enabled / sort_order are +-- not preserved or merged. Duplicates are NOT expected (every write path is +-- check-then-insert), so this is a safety net to guarantee the index builds, +-- not a routine merge. If a deployment knowingly relies on duplicate rows, +-- reconcile them manually before upgrading. +DELETE FROM mate_workspace_file +WHERE id NOT IN ( + SELECT keep_id FROM ( + SELECT MAX(id) AS keep_id + FROM mate_workspace_file + GROUP BY agent_id, filename, owner_key + ) t +); + +-- One row per (agent, filename, owner): one shared row + one row per PERSONAL +-- owner. Hardens the check-then-insert in saveFile/saveMemoryFile against +-- concurrent / multi-node duplicates. +CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_file_owner ON mate_workspace_file (agent_id, filename, owner_key); + +-- ---------- mate_memory_recall ---------- +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_memory_recall' AND column_name = 'owner_key' + ) THEN + ALTER TABLE mate_memory_recall ADD COLUMN owner_key VARCHAR(128) NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_memory_recall' AND column_name = 'scope' + ) THEN + ALTER TABLE mate_memory_recall ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM'; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_memory_recall_scope_owner ON mate_memory_recall (agent_id, scope, owner_key); + +-- ---------- mate_fact ---------- +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_fact' AND column_name = 'owner_key' + ) THEN + ALTER TABLE mate_fact ADD COLUMN owner_key VARCHAR(128) NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_fact' AND column_name = 'scope' + ) THEN + ALTER TABLE mate_fact ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM'; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_fact_scope_owner ON mate_fact (agent_id, scope, owner_key); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V138__rename_search_tool_to_web_search.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V138__rename_search_tool_to_web_search.sql new file mode 100644 index 000000000..835cf0300 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V138__rename_search_tool_to_web_search.sql @@ -0,0 +1,7 @@ +-- Rename the built-in web-search tool from "search" to "web_search". +-- DashScope's native protocol reserves the function name "search" and rejects any +-- request that declares a tool with that name ("InvalidParameter: Tool names are not +-- allowed to be [search]"), which broke tool use for every qwen/DashScope-native model +-- that had this tool bound. Migrate existing agent bindings to the new name so they +-- keep resolving after the tool was renamed in code. Idempotent. +UPDATE mate_agent_tool SET tool_name = 'web_search' WHERE tool_name = 'search'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V139__mcp_default_read_timeout_60s.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V139__mcp_default_read_timeout_60s.sql new file mode 100644 index 000000000..541bc6dc4 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V139__mcp_default_read_timeout_60s.sql @@ -0,0 +1,8 @@ +-- Raise the default per-request (read) timeout for MCP servers from 30s to 60s. +-- A 30s ceiling cut off MCP tools whose single callTool round-trip legitimately +-- runs longer (data-heavy or compute-heavy tools), surfacing as a request timeout +-- with no retry. The application layer already falls back to 60s when the column +-- is null; this aligns the schema default so the value is consistent everywhere. +-- Only changes the column default for newly inserted rows — existing rows keep +-- whatever value they were given. Idempotent. +ALTER TABLE mate_mcp_server ALTER COLUMN read_timeout_seconds SET DEFAULT 60; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V13__wiki_chunk_embedding.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V13__wiki_chunk_embedding.sql new file mode 100644 index 000000000..e5e16dac7 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V13__wiki_chunk_embedding.sql @@ -0,0 +1,21 @@ +-- V13: Add embedding column to mate_wiki_chunk (RFC-011 Phase 2) +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN embedding BYTEA DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding_model' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN embedding_model VARCHAR(64) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V140__goal_criteria_checklist.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V140__goal_criteria_checklist.sql new file mode 100644 index 000000000..1819b8687 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V140__goal_criteria_checklist.sql @@ -0,0 +1,19 @@ +-- V140: Structured, checkable criteria for goals (MySQL). +-- +-- See the H2 counterpart for the full rationale. MySQL has no +-- "ADD COLUMN IF NOT EXISTS", so the column is guarded with an +-- INFORMATION_SCHEMA existence check + prepared statement for idempotency. +-- +-- The column holds the goal's checklist as JSON: +-- [{ "id": "C1", "text": "...", "passed": false, "evidence": "" }, ...] +-- Additive and nullable, so existing goals load unchanged (a NULL list +-- bootstraps on first evaluation). +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_agent_goal' AND column_name = 'criteria' + ) THEN + ALTER TABLE mate_agent_goal ADD COLUMN criteria TEXT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V141__agent_wiki_kb_scope.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V141__agent_wiki_kb_scope.sql new file mode 100644 index 000000000..d11f89d8b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V141__agent_wiki_kb_scope.sql @@ -0,0 +1,24 @@ +-- V141: Per-agent knowledge base access scope for wiki tools (KingbaseES / PostgreSQL 兼容). +-- +-- Knowledge bases are workspace-shared, so by default every agent in a +-- workspace can reach every KB in it. This table lets an operator pin an +-- agent to a subset of KBs: once at least one enabled row exists for an +-- agent, the wiki tools (list/search/read/write) can only see and target +-- those KBs. No rows for an agent = unrestricted (workspace-wide), which +-- keeps every pre-existing agent behaving exactly as before. +-- +-- The default KB an agent's wiki tools fall back to when no kbId/kbName is +-- given still lives on mate_agent.primary_kb_id; this table only narrows the +-- visible set, and the primary is expected to be one of the scoped KBs. + +CREATE TABLE IF NOT EXISTS mate_agent_wiki_kb ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_wiki_kb ON mate_agent_wiki_kb (agent_id, kb_id, deleted); +CREATE INDEX IF NOT EXISTS idx_agent_wiki_kb_agent ON mate_agent_wiki_kb (agent_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V142__wiki_transformation_target_page_type.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V142__wiki_transformation_target_page_type.sql new file mode 100644 index 000000000..dd693ee08 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V142__wiki_transformation_target_page_type.sql @@ -0,0 +1,13 @@ +-- Optional target pageType for a transformation whose output_target='page'. +-- See the h2 sibling migration for the prose explanation. +-- MySQL INFORMATION_SCHEMA guard converted to plpgsql DO block. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_transformation' AND column_name = 'target_page_type' + ) THEN + ALTER TABLE mate_wiki_transformation ADD COLUMN target_page_type VARCHAR(64) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V143__register_code_execute_tool.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V143__register_code_execute_tool.sql new file mode 100644 index 000000000..5c4c0f60f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V143__register_code_execute_tool.sql @@ -0,0 +1,6 @@ +-- V143: Register CodeExecuteTool as a built-in tool. +-- ON DUPLICATE KEY UPDATE is the MySQL idempotent upsert. +-- Converted to: ON CONFLICT DO UPDATE with EXCLUDED references (KingbaseES / PostgreSQL). +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000023, 'CodeExecuteTool', 'Code Execute', 'Execute a snippet of code (python, bash, or node) that the agent writes on the fly. Lets a documentation-only skill be acted on by running the code its instructions describe. Dangerous operations trigger approval.', 'builtin', 'codeExecuteTool', '🧑‍💻', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V144__ckjia_mcp_fix_production_endpoint.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V144__ckjia_mcp_fix_production_endpoint.sql new file mode 100644 index 000000000..26576cc80 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V144__ckjia_mcp_fix_production_endpoint.sql @@ -0,0 +1,28 @@ +-- Fix the ckjia-shopping MCP seed to its real production endpoint. +-- +-- V85 seeded a dev/test placeholder (sse + http://localhost:8085/sse + +-- "Bearer ${CKJIA_MCP_KEY}"), which can never connect out of the box, so the +-- 参考价 / price-comparison skill stayed unusable until an admin hand-edited it. +-- The official CKJIA SaaS endpoint is Streamable HTTP at +-- https://m.ckjia.com/api/ai/mcp and needs no Authorization header. +-- +-- Also raises both timeouts to 60s: the price-aggregation round-trip +-- (multi-platform search) legitimately runs longer than the old 30s ceiling. +-- +-- SAFETY: only rewrites rows that still carry the untouched dev placeholder +-- URL, so an admin who already pointed ckjia-shopping at a private CKJIA +-- deployment (or the SaaS URL) is left completely alone. Idempotent — after it +-- runs the URL no longer matches the WHERE clause. `enabled` is deliberately +-- not changed: the server stays opt-in. +UPDATE mate_mcp_server +SET transport = 'streamable_http', + url = 'https://m.ckjia.com/api/ai/mcp', + headers_json = NULL, + connect_timeout_seconds = 60, + read_timeout_seconds = 60, + last_status = 'disconnected', + last_error = NULL, + description = 'CKJIA price comparison MCP server (Streamable HTTP). Disabled by default — enable it in Settings > MCP Connections to use the 参考价 shopping skill.', + update_time = NOW() +WHERE name = 'ckjia-shopping' + AND url = 'http://localhost:8085/sse'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V145__claude_fable_5_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V145__claude_fable_5_models.sql new file mode 100644 index 000000000..e3a7c7ea8 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V145__claude_fable_5_models.sql @@ -0,0 +1,37 @@ +-- Add Claude Fable 5 model entries to mate_model_config. Unlike earlier Claude +-- families, the Fable rows live ONLY here, not in the data-kingbase-{en,zh}.sql +-- seed: Flyway runs every version (V1..) on a fresh database, so the migration +-- seeds new installs and upgrades existing deployments alike. The trade-off is +-- that the description below is English-only (seed files carry localized copy). +-- +-- Fable 5 is a reasoning-first model with a 1M-token context window and native +-- vision input. It follows the same strict API contract as Claude 4.7+: +-- temperature / top_p / top_k must be NULL (otherwise HTTP 400), and the +-- "xhigh" adaptive thinking tier is available. Both are handled in +-- AnthropicChatModelBuilder via the isClaudeFable() / isClaude47OrLater() +-- detectors. Vision capability is resolved in ModelCapabilityService. +-- +-- ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert. +-- Same V number is used in h2/ for cross-dialect parity. + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES +-- Direct Anthropic +(1000000300, 'Claude Fable 5', 'anthropic', 'claude-fable-5', 'Anthropic Claude Fable 5 (1M context, vision, xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +-- OpenRouter passthrough +(1000000301, 'Claude Fable 5', 'openrouter', 'anthropic/claude-fable-5', 'Claude Fable 5 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +-- Claude Code OAuth (Pro/Max subscription) +(1000000302, 'Claude Fable 5', 'anthropic-claude-code', 'claude-fable-5', 'Claude Fable 5 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + provider = EXCLUDED.provider, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + temperature = EXCLUDED.temperature, + max_tokens = EXCLUDED.max_tokens, + top_p = EXCLUDED.top_p, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V146__wiki_kb_watcher_enabled.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V146__wiki_kb_watcher_enabled.sql new file mode 100644 index 000000000..a6965ac21 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V146__wiki_kb_watcher_enabled.sql @@ -0,0 +1,14 @@ +-- Per-KB source-watcher toggle. See the h2 sibling for the prose explanation. +-- MySQL INFORMATION_SCHEMA guard converted to plpgsql DO block. +-- SMALLINT (not BOOLEAN): WikiKnowledgeBaseEntity.watcherEnabled is Integer (1/0). +-- Vanilla PostgreSQL cannot map a BOOLEAN into a JDBC int. Do not switch to BOOLEAN. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'watcher_enabled' + ) THEN + ALTER TABLE mate_wiki_knowledge_base ADD COLUMN watcher_enabled SMALLINT NOT NULL DEFAULT 0; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V147__wiki_page_aliases.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V147__wiki_page_aliases.sql new file mode 100644 index 000000000..a2e039452 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V147__wiki_page_aliases.sql @@ -0,0 +1,5 @@ +-- V147: Page aliases — KingbaseES dialect. +-- +-- See h2/V147__wiki_page_aliases.sql for column semantics. +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS aliases TEXT DEFAULT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql new file mode 100644 index 000000000..2f88fff7b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql @@ -0,0 +1,31 @@ +-- mate_wiki_entity: canonical named-entity nodes extracted from source chunks. +-- See the H2 file for the design rationale. + +CREATE TABLE IF NOT EXISTS mate_wiki_entity ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + + canonical_name VARCHAR(256) NOT NULL, + normalized_key VARCHAR(256) NOT NULL, + type VARCHAR(32) NOT NULL, + + aliases_json TEXT, + description TEXT, + salience DECIMAL(5, 4), + mention_count INT NOT NULL DEFAULT 0, + + embedding BYTEA, + embedding_model VARCHAR(64), + + computed_hash VARCHAR(64), + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted SMALLINT NOT NULL DEFAULT 0 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uk_we_key + ON mate_wiki_entity (kb_id, normalized_key, type, deleted); +CREATE INDEX IF NOT EXISTS idx_we_kb ON mate_wiki_entity (kb_id, deleted); +CREATE INDEX IF NOT EXISTS idx_we_salience ON mate_wiki_entity (kb_id, salience DESC); +CREATE INDEX IF NOT EXISTS idx_we_type ON mate_wiki_entity (kb_id, type, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V149__wiki_entity_mention.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V149__wiki_entity_mention.sql new file mode 100644 index 000000000..d989b2810 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V149__wiki_entity_mention.sql @@ -0,0 +1,25 @@ +-- mate_wiki_entity_mention: links a canonical entity to a source occurrence. +-- See the H2 file for the design rationale. + +CREATE TABLE IF NOT EXISTS mate_wiki_entity_mention ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + entity_id BIGINT NOT NULL, + chunk_id BIGINT, + page_id BIGINT, + + surface_form VARCHAR(256), + char_offset INT, + confidence DECIMAL(4, 3), + evidence TEXT, + source VARCHAR(32), + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted SMALLINT NOT NULL DEFAULT 0 +); + +CREATE INDEX IF NOT EXISTS idx_wem_entity ON mate_wiki_entity_mention (entity_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wem_chunk ON mate_wiki_entity_mention (chunk_id); +CREATE INDEX IF NOT EXISTS idx_wem_page ON mate_wiki_entity_mention (page_id); +CREATE INDEX IF NOT EXISTS idx_wem_kb ON mate_wiki_entity_mention (kb_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V14__embedding_model_config.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V14__embedding_model_config.sql new file mode 100644 index 000000000..4e42fde9e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V14__embedding_model_config.sql @@ -0,0 +1,36 @@ +-- V14: Embedding model UI config (对标 Dify) +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_config' AND column_name = 'model_type' + ) THEN + ALTER TABLE mate_model_config ADD COLUMN model_type VARCHAR(32) DEFAULT 'chat'; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'embedding_model_id' + ) THEN + ALTER TABLE mate_wiki_knowledge_base ADD COLUMN embedding_model_id BIGINT DEFAULT NULL; + END IF; +END $$; + +-- 播种 DashScope embedding(与 chat 模型共享 provider apiKey) +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES (1000001001, 'Text Embedding v3', 'dashscope', 'text-embedding-v3', 'DashScope 通义千问 v3 通用文本向量模型(1024 维)', 0, 0, 0, TRUE, TRUE, TRUE, 'embedding', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET model_type = 'embedding'; + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES (1000001002, 'Text Embedding v2', 'dashscope', 'text-embedding-v2', 'DashScope 通义千问 v2 文本向量模型(1536 维)', 0, 0, 0, TRUE, TRUE, FALSE, 'embedding', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET model_type = 'embedding'; + +-- 系统默认 embedding 模型(id 必须显式指定,与 chat 段 100000xxxx 错开) +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +VALUES (1000001100, 'embedding.default.model.id', '1000001001', + 'Default embedding model id for wiki semantic search', NOW(), NOW()) +ON CONFLICT (id) DO UPDATE SET setting_value = EXCLUDED.setting_value; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V150__wiki_entity_relation.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V150__wiki_entity_relation.sql new file mode 100644 index 000000000..df1fbfe00 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V150__wiki_entity_relation.sql @@ -0,0 +1,27 @@ +-- mate_wiki_entity_relation: directed subject -> predicate -> object triples +-- between canonical entities. See the H2 file for the design rationale. + +CREATE TABLE IF NOT EXISTS mate_wiki_entity_relation ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + + subject_entity_id BIGINT NOT NULL, + predicate VARCHAR(64) NOT NULL, + object_entity_id BIGINT NOT NULL, + + evidence TEXT, + confidence DECIMAL(4, 3), + source VARCHAR(32), + evidence_chunk_id BIGINT, + computed_hash VARCHAR(64), + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted SMALLINT NOT NULL DEFAULT 0 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uk_wer_triple + ON mate_wiki_entity_relation (kb_id, subject_entity_id, predicate, object_entity_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wer_subject ON mate_wiki_entity_relation (kb_id, subject_entity_id); +CREATE INDEX IF NOT EXISTS idx_wer_object ON mate_wiki_entity_relation (kb_id, object_entity_id); +CREATE INDEX IF NOT EXISTS idx_wer_kb ON mate_wiki_entity_relation (kb_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V151__webchat_session_id.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V151__webchat_session_id.sql new file mode 100644 index 000000000..36c7f1719 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V151__webchat_session_id.sql @@ -0,0 +1,3 @@ +-- See the H2 copy for context. KingbaseES (PostgreSQL) supports +-- ADD COLUMN IF NOT EXISTS natively. +ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS webchat_session_id VARCHAR(64); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V152__webchat_archive_and_revocation.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V152__webchat_archive_and_revocation.sql new file mode 100644 index 000000000..5a77a682a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V152__webchat_archive_and_revocation.sql @@ -0,0 +1,19 @@ +-- V148: webchat visitor-session archive flag + visitor-token revocation registry (KingbaseES / PostgreSQL). +-- See the H2 copy for full context. + +ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS archived INT NOT NULL DEFAULT 0; + +CREATE TABLE IF NOT EXISTS webchat_revoked_visitor ( + id BIGINT NOT NULL PRIMARY KEY, + channel_id BIGINT NOT NULL, + visitor_id VARCHAR(128) NOT NULL, + revoked_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + reason VARCHAR(255), + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_webchat_revoked_visitor + ON webchat_revoked_visitor (channel_id, visitor_id, deleted); +CREATE INDEX IF NOT EXISTS idx_webchat_revoked_visitor_lookup + ON webchat_revoked_visitor (channel_id, visitor_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V153__zhipu_glm_5_2.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V153__zhipu_glm_5_2.sql new file mode 100644 index 000000000..45a932732 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V153__zhipu_glm_5_2.sql @@ -0,0 +1,18 @@ +-- V153: add the GLM-5.2 flagship to the native Zhipu (BigModel / Z.AI) +-- providers. See the H2 copy for full background. Adds glm-5.2 to all four +-- existing Zhipu providers (standard + coding plan, China + International). +-- Aggregator platforms (Volcano Ark, DashScope / Bailian, ModelScope) do not +-- host GLM-5.2 yet and are intentionally left untouched. +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000000214, 'GLM-5.2', 'zhipu-cn', 'glm-5.2', '最新旗舰模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000224, 'GLM-5.2', 'zhipu-intl', 'glm-5.2', 'Latest flagship model (International)', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000238, 'GLM-5.2 Coding', 'zhipu-cn-codingplan', 'glm-5.2', '智谱编码套餐 — GLM-5.2 最新旗舰', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000239, 'GLM-5.2 Coding', 'zhipu-intl-codingplan', 'glm-5.2', 'Zhipu Coding Plan — GLM-5.2 latest flagship (International)', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V154__agent_wiki_disabled.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V154__agent_wiki_disabled.sql new file mode 100644 index 000000000..62c9db241 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V154__agent_wiki_disabled.sql @@ -0,0 +1,3 @@ +-- V154: Wiki/knowledge-base opt-out flag on mate_agent (issue #304). +-- Mirrors skills_disabled / tools_disabled. Defaults to FALSE. +ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS wiki_disabled BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V155__plan_conversation_id.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V155__plan_conversation_id.sql new file mode 100644 index 000000000..084fecf53 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V155__plan_conversation_id.sql @@ -0,0 +1,3 @@ +-- V155: Link a plan to the conversation/run that produced it (see H2 file for +-- context). KingbaseES (PostgreSQL-compatible) supports ADD COLUMN IF NOT EXISTS. +ALTER TABLE mate_plan ADD COLUMN IF NOT EXISTS conversation_id VARCHAR(64); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V156__sub_plan_assigned_agent.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V156__sub_plan_assigned_agent.sql new file mode 100644 index 000000000..e9bdb410d --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V156__sub_plan_assigned_agent.sql @@ -0,0 +1,3 @@ +-- V156: Per-step agent delegation for plan-execute (see H2 file for context). +-- KingbaseES (PostgreSQL-compatible) supports ADD COLUMN IF NOT EXISTS. +ALTER TABLE mate_sub_plan ADD COLUMN IF NOT EXISTS assigned_agent_id BIGINT; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V157__register_session_list_tool.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V157__register_session_list_tool.sql new file mode 100644 index 000000000..3383b7066 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V157__register_session_list_tool.sql @@ -0,0 +1,10 @@ +-- V157: Register SessionListTool as a built-in tool. +-- Mirrors DelegateAgentTool (spawn) so the delegation tools surface together in +-- the tool picker and AvailableToolService. SessionListTool is read-only and +-- core-tier (auto-available even without this row); this row gives it a name, +-- icon and admin toggle in the UI. +-- Idempotent: ON CONFLICT keeps the row in sync if it already exists. + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000024, 'SessionListTool', 'Sub-Agent List', 'List the live sub-agents spawned from the current conversation: id, target agent, depth, status, phase, tool-call count, elapsed time and goal. Read-only; lets a parent agent check delegated children before deciding to wait, follow up, or proceed.', 'builtin', 'sessionListTool', '🧭', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, tool_type=EXCLUDED.tool_type, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, enabled=EXCLUDED.enabled, builtin=EXCLUDED.builtin, update_time=EXCLUDED.update_time, deleted=EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V158__register_session_send_tool.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V158__register_session_send_tool.sql new file mode 100644 index 000000000..244837e11 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V158__register_session_send_tool.sql @@ -0,0 +1,9 @@ +-- V158: Register SessionSendTool as a built-in tool. +-- Completes the spawn/send/list delegation triad in the tool picker alongside +-- DelegateAgentTool (spawn) and SessionListTool (list). Core-tier and already +-- auto-available without this row; the row only adds UI metadata. +-- Idempotent: ON CONFLICT keeps the row in sync if it already exists. + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000025, 'SessionSendTool', 'Sub-Agent Send', 'Send a follow-up message to a sub-agent you previously delegated to, continuing its existing session (so it still remembers the earlier task) instead of starting fresh. Pass the session_id returned by delegateToAgent.', 'builtin', 'sessionSendTool', '✉️', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, tool_type=EXCLUDED.tool_type, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, enabled=EXCLUDED.enabled, builtin=EXCLUDED.builtin, update_time=EXCLUDED.update_time, deleted=EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V159__sso_external_identity.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V159__sso_external_identity.sql new file mode 100644 index 000000000..2dfa8336e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V159__sso_external_identity.sql @@ -0,0 +1,37 @@ +-- V159: SSO (single sign-on) infrastructure — external identity link table, +-- OAuth2 state store, and mate_user.password relaxation. +-- See the H2 file for the design rationale. + +CREATE TABLE IF NOT EXISTS mate_user_external_identity ( + id BIGINT PRIMARY KEY, + user_id BIGINT NOT NULL, + + provider VARCHAR(32) NOT NULL, + external_id VARCHAR(128) NOT NULL, + union_id VARCHAR(128), + external_name VARCHAR(128), + external_avatar VARCHAR(512), + external_email VARCHAR(128), + + last_login_at TIMESTAMP(3), + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted SMALLINT NOT NULL DEFAULT 0 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uk_sso_provider_external + ON mate_user_external_identity (provider, external_id); +CREATE UNIQUE INDEX IF NOT EXISTS uk_sso_provider_union + ON mate_user_external_identity (provider, union_id); +CREATE INDEX IF NOT EXISTS idx_sso_user_provider + ON mate_user_external_identity (user_id, provider); + +CREATE TABLE IF NOT EXISTS sso_state ( + token VARCHAR(128) PRIMARY KEY, + kind VARCHAR(8) NOT NULL, + provider VARCHAR(32), + consumed SMALLINT NOT NULL DEFAULT 0, + created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +ALTER TABLE mate_user ALTER COLUMN password DROP NOT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V15__purge_unavailable_dashscope_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V15__purge_unavailable_dashscope_models.sql new file mode 100644 index 000000000..482f98e76 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V15__purge_unavailable_dashscope_models.sql @@ -0,0 +1,7 @@ +-- V15: Purge DashScope model seed rows that are unavailable on the native protocol +DELETE FROM mate_model_config +WHERE id IN (1000000170, 1000000171) + AND provider = 'dashscope' + AND builtin = TRUE; +-- 1000000170 = qwen3.5-plus +-- 1000000171 = qwen3.5-max diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V16__purge_dot_version_dashscope_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V16__purge_dot_version_dashscope_models.sql new file mode 100644 index 000000000..d58984913 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V16__purge_dot_version_dashscope_models.sql @@ -0,0 +1,8 @@ +-- V16: Broaden the DashScope native-protocol purge (see V15). +DELETE FROM mate_model_config +WHERE provider = 'dashscope' + AND (model_name LIKE 'qwen1.%' + OR model_name LIKE 'qwen2.%' + OR model_name LIKE 'qwen3.%' + OR model_name LIKE 'qwen4.%' + OR model_name LIKE 'qwen5.%'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql new file mode 100644 index 000000000..910fea030 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql @@ -0,0 +1,630 @@ +-- MateClaw 数据库初始化脚本(KingbaseES / PostgreSQL 兼容) + +-- 用户表 +CREATE TABLE IF NOT EXISTS mate_user ( + id BIGINT NOT NULL PRIMARY KEY, + username VARCHAR(64) NOT NULL UNIQUE, + password VARCHAR(200) NOT NULL, + nickname VARCHAR(64), + avatar VARCHAR(256), + email VARCHAR(128), + role VARCHAR(32) NOT NULL DEFAULT 'user', + enabled BOOLEAN NOT NULL DEFAULT TRUE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- Agent 配置表 +CREATE TABLE IF NOT EXISTS mate_agent ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description TEXT, + agent_type VARCHAR(32) NOT NULL DEFAULT 'react', + system_prompt TEXT, + model_name VARCHAR(128), + max_iterations INT NOT NULL DEFAULT 10, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + icon VARCHAR(256), + tags VARCHAR(256), + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 模型配置表 +CREATE TABLE IF NOT EXISTS mate_model_config ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + provider VARCHAR(64) NOT NULL DEFAULT 'dashscope', + model_name VARCHAR(128) NOT NULL, + description TEXT, + temperature DOUBLE PRECISION, + max_tokens INT, + top_p DOUBLE PRECISION, + builtin BOOLEAN NOT NULL DEFAULT TRUE, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + is_default BOOLEAN NOT NULL DEFAULT FALSE, + max_input_tokens INT DEFAULT 0, + enable_search BOOLEAN DEFAULT FALSE, + search_strategy VARCHAR(32) DEFAULT NULL, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_model_config_model_name ON mate_model_config (model_name); + +-- 模型 Provider 表 +CREATE TABLE IF NOT EXISTS mate_model_provider ( + provider_id VARCHAR(64) NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + api_key_prefix VARCHAR(32), + chat_model VARCHAR(64), + api_key VARCHAR(512), + base_url VARCHAR(512), + generate_kwargs TEXT, + is_custom BOOLEAN NOT NULL DEFAULT FALSE, + is_local BOOLEAN NOT NULL DEFAULT FALSE, + support_model_discovery BOOLEAN NOT NULL DEFAULT FALSE, + support_connection_check BOOLEAN NOT NULL DEFAULT FALSE, + freeze_url BOOLEAN NOT NULL DEFAULT FALSE, + require_api_key BOOLEAN NOT NULL DEFAULT TRUE, + auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key', + oauth_access_token TEXT, + oauth_refresh_token TEXT, + oauth_expires_at BIGINT, + oauth_account_id VARCHAR(128), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); + +-- 系统设置表 +CREATE TABLE IF NOT EXISTS mate_system_setting ( + id BIGINT NOT NULL PRIMARY KEY, + setting_key VARCHAR(128) NOT NULL UNIQUE, + setting_value TEXT, + description VARCHAR(256), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); + +-- 技能表 +CREATE TABLE IF NOT EXISTS mate_skill ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description TEXT, + skill_type VARCHAR(32) NOT NULL DEFAULT 'dynamic', + icon VARCHAR(256), + version VARCHAR(32), + author VARCHAR(64), + config_json TEXT, + source_code TEXT, + skill_content TEXT, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + builtin BOOLEAN NOT NULL DEFAULT FALSE, + tags VARCHAR(256), + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 工具表 +CREATE TABLE IF NOT EXISTS mate_tool ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + display_name VARCHAR(128), + description TEXT, + tool_type VARCHAR(32) NOT NULL DEFAULT 'builtin', + bean_name VARCHAR(128), + icon VARCHAR(256), + mcp_endpoint VARCHAR(256), + params_schema TEXT, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + builtin BOOLEAN NOT NULL DEFAULT FALSE, + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 渠道表 +CREATE TABLE IF NOT EXISTS mate_channel ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + channel_type VARCHAR(32) NOT NULL, + agent_id BIGINT, + bot_prefix VARCHAR(64), + config_json TEXT, + enabled BOOLEAN NOT NULL DEFAULT FALSE, + description VARCHAR(256), + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 会话表 +CREATE TABLE IF NOT EXISTS mate_conversation ( + id BIGINT NOT NULL PRIMARY KEY, + conversation_id VARCHAR(64) NOT NULL UNIQUE, + title VARCHAR(256), + agent_id BIGINT, + username VARCHAR(64), + message_count INT NOT NULL DEFAULT 0, + last_message TEXT, + last_active_time TIMESTAMP, + stream_status VARCHAR(16) NOT NULL DEFAULT 'idle', + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_conversation_username ON mate_conversation (username); + +-- 消息表 +CREATE TABLE IF NOT EXISTS mate_message ( + id BIGINT NOT NULL PRIMARY KEY, + conversation_id VARCHAR(64) NOT NULL, + role VARCHAR(32) NOT NULL, + content TEXT, + content_parts TEXT, + tool_name VARCHAR(128), + token_usage INT, + prompt_tokens INT DEFAULT 0, + completion_tokens INT DEFAULT 0, + runtime_model VARCHAR(128), + runtime_provider VARCHAR(64), + status VARCHAR(32) NOT NULL DEFAULT 'completed', + metadata TEXT, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +COMMENT ON COLUMN mate_message.metadata IS '存储 toolCalls, plan, currentPhase, pendingApproval 等元数据'; +CREATE INDEX IF NOT EXISTS idx_message_conversation ON mate_message (conversation_id); +CREATE INDEX IF NOT EXISTS idx_message_conv_time ON mate_message (conversation_id, create_time); + +-- 执行计划表 +CREATE TABLE IF NOT EXISTS mate_plan ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id VARCHAR(64), + goal TEXT, + status VARCHAR(32) NOT NULL DEFAULT 'pending', + total_steps INT NOT NULL DEFAULT 0, + completed_steps INT NOT NULL DEFAULT 0, + summary TEXT, + start_time TIMESTAMP, + end_time TIMESTAMP, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 子计划步骤表 +CREATE TABLE IF NOT EXISTS mate_sub_plan ( + id BIGINT NOT NULL PRIMARY KEY, + plan_id BIGINT NOT NULL, + step_index INT NOT NULL, + description TEXT, + status VARCHAR(32) NOT NULL DEFAULT 'pending', + result TEXT, + start_time TIMESTAMP, + end_time TIMESTAMP, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_sub_plan_plan_id ON mate_sub_plan (plan_id); + +-- 定时任务表 +CREATE TABLE IF NOT EXISTS mate_cron_job ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + cron_expression VARCHAR(128) NOT NULL, + timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai', + agent_id BIGINT NOT NULL, + task_type VARCHAR(16) NOT NULL DEFAULT 'text', + trigger_message TEXT, + request_body TEXT, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + next_run_time TIMESTAMP, + last_run_time TIMESTAMP, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 渠道会话存储表 +CREATE TABLE IF NOT EXISTS mate_channel_session ( + id BIGINT NOT NULL PRIMARY KEY, + conversation_id VARCHAR(128) NOT NULL UNIQUE, + channel_type VARCHAR(32) NOT NULL, + target_id VARCHAR(512) NOT NULL, + sender_id VARCHAR(128), + sender_name VARCHAR(128), + channel_id BIGINT, + last_active_time TIMESTAMP NOT NULL, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_channel_session_type ON mate_channel_session (channel_type); +CREATE INDEX IF NOT EXISTS idx_channel_session_channel_id ON mate_channel_session (channel_id); + +-- 工作区文件表(Agent 级 Markdown 文档管理) +CREATE TABLE IF NOT EXISTS mate_workspace_file ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + filename VARCHAR(256) NOT NULL, + content TEXT, + file_size BIGINT NOT NULL DEFAULT 0, + enabled BOOLEAN NOT NULL DEFAULT FALSE, + sort_order INT NOT NULL DEFAULT 0, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_workspace_file_agent ON mate_workspace_file (agent_id); +CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_enabled ON mate_workspace_file (agent_id, enabled); + +-- ==================== MCP Server 管理 ==================== + +CREATE TABLE IF NOT EXISTS mate_mcp_server ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description TEXT, + transport VARCHAR(32) NOT NULL DEFAULT 'stdio', + url VARCHAR(512), + headers_json TEXT, + command VARCHAR(512), + args_json TEXT, + env_json TEXT, + cwd VARCHAR(512), + enabled BOOLEAN NOT NULL DEFAULT TRUE, + connect_timeout_seconds INT NOT NULL DEFAULT 30, + read_timeout_seconds INT NOT NULL DEFAULT 30, + last_status VARCHAR(32) NOT NULL DEFAULT 'disconnected', + last_error TEXT, + last_connected_time TIMESTAMP, + tool_count INT NOT NULL DEFAULT 0, + builtin BOOLEAN NOT NULL DEFAULT FALSE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_mcp_server_enabled ON mate_mcp_server (enabled); + +-- ==================== 工具安全治理(ToolGuard) ==================== + +-- 工具审批表 +CREATE TABLE IF NOT EXISTS mate_tool_approval ( + id BIGINT NOT NULL PRIMARY KEY, + pending_id VARCHAR(32) NOT NULL UNIQUE, + conversation_id VARCHAR(128) NOT NULL, + user_id VARCHAR(64), + agent_id VARCHAR(64), + channel_type VARCHAR(32), + requester_name VARCHAR(128), + reply_target VARCHAR(512), + tool_name VARCHAR(128) NOT NULL, + tool_arguments TEXT, + tool_call_payload TEXT, + tool_call_hash VARCHAR(64), + sibling_tool_calls TEXT, + summary TEXT, + findings_json TEXT, + max_severity VARCHAR(16), + status VARCHAR(32) NOT NULL DEFAULT 'PENDING', + resolved_by VARCHAR(64), + created_at TIMESTAMP NOT NULL, + resolved_at TIMESTAMP, + expire_at TIMESTAMP, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_tool_approval_conv ON mate_tool_approval (conversation_id); +CREATE INDEX IF NOT EXISTS idx_tool_approval_status ON mate_tool_approval (status); +CREATE INDEX IF NOT EXISTS idx_tool_approval_pending_id ON mate_tool_approval (pending_id); + +-- 安全规则表 +CREATE TABLE IF NOT EXISTS mate_tool_guard_rule ( + id BIGINT NOT NULL PRIMARY KEY, + rule_id VARCHAR(64) NOT NULL UNIQUE, + name VARCHAR(128) NOT NULL, + description TEXT, + tool_name VARCHAR(128), + param_name VARCHAR(128), + category VARCHAR(64) NOT NULL, + severity VARCHAR(16) NOT NULL, + decision VARCHAR(16) NOT NULL DEFAULT 'NEEDS_APPROVAL', + pattern VARCHAR(512) NOT NULL, + exclude_pattern VARCHAR(512), + remediation TEXT, + builtin BOOLEAN NOT NULL DEFAULT FALSE, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + priority INT NOT NULL DEFAULT 100, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- 安全全局配置表 +CREATE TABLE IF NOT EXISTS mate_tool_guard_config ( + id BIGINT NOT NULL PRIMARY KEY, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + guard_scope VARCHAR(32) NOT NULL DEFAULT 'all', + guarded_tools_json TEXT, + denied_tools_json TEXT, + file_guard_enabled BOOLEAN NOT NULL DEFAULT TRUE, + sensitive_paths_json TEXT, + audit_enabled BOOLEAN NOT NULL DEFAULT TRUE, + audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO', + audit_retention_days INT NOT NULL DEFAULT 90, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); + +-- 安全审计日志表 +CREATE TABLE IF NOT EXISTS mate_tool_guard_audit_log ( + id BIGINT NOT NULL PRIMARY KEY, + conversation_id VARCHAR(128), + agent_id VARCHAR(64), + user_id VARCHAR(64), + channel_type VARCHAR(32), + tool_name VARCHAR(128) NOT NULL, + tool_params_json TEXT, + decision VARCHAR(16) NOT NULL, + max_severity VARCHAR(16), + findings_json TEXT, + pending_id VARCHAR(32), + replay_payload_hash VARCHAR(64), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_guard_audit_conv ON mate_tool_guard_audit_log (conversation_id); +CREATE INDEX IF NOT EXISTS idx_guard_audit_time ON mate_tool_guard_audit_log (create_time); + +-- ==================== 外部数据源 ==================== + +CREATE TABLE IF NOT EXISTS mate_datasource ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description VARCHAR(512), + db_type VARCHAR(32) NOT NULL, + host VARCHAR(256) NOT NULL, + port INT NOT NULL, + database_name VARCHAR(128) NOT NULL, + username VARCHAR(128), + password VARCHAR(512), + extra_params VARCHAR(512), + schema_name VARCHAR(128), + enabled BOOLEAN NOT NULL DEFAULT TRUE, + last_test_time TIMESTAMP, + last_test_ok BOOLEAN, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +-- ==================== 异步任务(视频/图片生成等长耗时操作) ==================== + +CREATE TABLE IF NOT EXISTS mate_async_task ( + id BIGINT NOT NULL PRIMARY KEY, + task_id VARCHAR(64) NOT NULL, + task_type VARCHAR(32) NOT NULL, + status VARCHAR(16) NOT NULL DEFAULT 'pending', + conversation_id VARCHAR(128), + message_id BIGINT, + provider_name VARCHAR(64), + provider_task_id VARCHAR(128), + request_json TEXT, + result_json TEXT, + error_message VARCHAR(512), + progress INT DEFAULT 0, + created_by VARCHAR(64), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_async_task_taskid ON mate_async_task (task_id); +CREATE INDEX IF NOT EXISTS idx_async_task_conv ON mate_async_task (conversation_id); +CREATE INDEX IF NOT EXISTS idx_async_task_status ON mate_async_task (status); + +-- ==================== 记忆召回追踪 ==================== + +CREATE TABLE IF NOT EXISTS mate_memory_recall ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + filename VARCHAR(256) NOT NULL, + snippet_hash VARCHAR(64), + snippet_preview VARCHAR(512), + recall_count INT NOT NULL DEFAULT 0, + daily_count INT NOT NULL DEFAULT 0, + query_hashes TEXT, + score DOUBLE PRECISION NOT NULL DEFAULT 0.0, + last_recalled_at TIMESTAMP, + promoted BOOLEAN NOT NULL DEFAULT FALSE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_memory_recall_agent ON mate_memory_recall (agent_id); +CREATE INDEX IF NOT EXISTS idx_memory_recall_agent_file ON mate_memory_recall (agent_id, filename); +CREATE INDEX IF NOT EXISTS idx_memory_recall_score ON mate_memory_recall (agent_id, score); +CREATE INDEX IF NOT EXISTS idx_memory_recall_candidates ON mate_memory_recall (agent_id, promoted, deleted); + +-- ==================== Wiki 知识库 ==================== + +CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + description TEXT, + agent_id BIGINT, + config_content TEXT, + source_directory VARCHAR(512), + status VARCHAR(32) NOT NULL DEFAULT 'active', + page_count INT NOT NULL DEFAULT 0, + raw_count INT NOT NULL DEFAULT 0, + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wiki_kb_agent ON mate_wiki_knowledge_base (agent_id); + +CREATE TABLE IF NOT EXISTS mate_wiki_raw_material ( + id BIGINT NOT NULL PRIMARY KEY, + kb_id BIGINT NOT NULL, + title VARCHAR(256) NOT NULL, + source_type VARCHAR(32) NOT NULL DEFAULT 'text', + source_path VARCHAR(512), + original_content TEXT, + extracted_text TEXT, + content_hash VARCHAR(64), + file_size BIGINT NOT NULL DEFAULT 0, + processing_status VARCHAR(32) NOT NULL DEFAULT 'pending', + last_processed_at TIMESTAMP, + error_message VARCHAR(512), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wiki_raw_kb ON mate_wiki_raw_material (kb_id); +CREATE INDEX IF NOT EXISTS idx_wiki_raw_status ON mate_wiki_raw_material (kb_id, processing_status); + +CREATE TABLE IF NOT EXISTS mate_wiki_page ( + id BIGINT NOT NULL PRIMARY KEY, + kb_id BIGINT NOT NULL, + slug VARCHAR(256) NOT NULL, + title VARCHAR(256) NOT NULL, + content TEXT, + summary VARCHAR(1024), + outgoing_links TEXT, + source_raw_ids TEXT, + version INT NOT NULL DEFAULT 1, + last_updated_by VARCHAR(32) NOT NULL DEFAULT 'ai', + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_page_kb_slug ON mate_wiki_page (kb_id, slug); +CREATE INDEX IF NOT EXISTS idx_wiki_page_kb ON mate_wiki_page (kb_id); + +-- ============================================= +-- 工作区表(Phase 2) +-- ============================================= + +-- 工作区 +CREATE TABLE IF NOT EXISTS mate_workspace ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + slug VARCHAR(64) NOT NULL, + description VARCHAR(256), + owner_id BIGINT, + settings_json TEXT, + base_path VARCHAR(512), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_slug ON mate_workspace (slug); + +-- 工作区成员 +CREATE TABLE IF NOT EXISTS mate_workspace_member ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + role VARCHAR(32) NOT NULL DEFAULT 'member', + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member (workspace_id); +CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member (user_id); + +-- ============================================= +-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2) +-- ============================================= + +CREATE TABLE IF NOT EXISTS mate_agent_skill ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + skill_id BIGINT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + config_json TEXT, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_skill ON mate_agent_skill (agent_id, skill_id); + +CREATE TABLE IF NOT EXISTS mate_agent_tool ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + tool_name VARCHAR(128) NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_tool ON mate_agent_tool (agent_id, tool_name); + +-- ============================================= +-- CronJob 执行历史(Phase 3 Sprint 3) +-- ============================================= +CREATE TABLE IF NOT EXISTS mate_cron_job_run ( + id BIGINT NOT NULL PRIMARY KEY, + cron_job_id BIGINT NOT NULL, + conversation_id VARCHAR(64), + status VARCHAR(32) NOT NULL, + trigger_type VARCHAR(32) NOT NULL DEFAULT 'scheduled', + started_at TIMESTAMP NOT NULL, + finished_at TIMESTAMP, + error_message TEXT, + token_usage INT DEFAULT 0, + create_time TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_cron_run_job ON mate_cron_job_run (cron_job_id, started_at); + +CREATE TABLE IF NOT EXISTS mate_usage_daily ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + agent_id BIGINT, + stat_date DATE NOT NULL, + conversation_count INT DEFAULT 0, + message_count INT DEFAULT 0, + total_tokens BIGINT DEFAULT 0, + prompt_tokens BIGINT DEFAULT 0, + completion_tokens BIGINT DEFAULT 0, + tool_call_count INT DEFAULT 0, + error_count INT DEFAULT 0, + create_time TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_usage_daily ON mate_usage_daily (workspace_id, agent_id, stat_date); + +-- ============================================= +-- 操作审计事件表(Phase 3 Sprint 1) +-- ============================================= +CREATE TABLE IF NOT EXISTS mate_audit_event ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT, + user_id BIGINT NOT NULL, + username VARCHAR(64) NOT NULL, + action VARCHAR(64) NOT NULL, + resource_type VARCHAR(64) NOT NULL, + resource_id VARCHAR(128), + resource_name VARCHAR(256), + detail_json TEXT, + ip_address VARCHAR(64), + user_agent VARCHAR(256), + create_time TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_audit_ws_time ON mate_audit_event (workspace_id, create_time); +CREATE INDEX IF NOT EXISTS idx_audit_user ON mate_audit_event (user_id); +CREATE INDEX IF NOT EXISTS idx_audit_resource ON mate_audit_event (resource_type, resource_id); + +-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用) +DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V20__purge_soft_deleted_rows.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V20__purge_soft_deleted_rows.sql new file mode 100644 index 000000000..095d57745 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V20__purge_soft_deleted_rows.sql @@ -0,0 +1,34 @@ +-- V20: Purge soft-deleted rows from all tables and retire soft-delete semantics. +-- @TableLogic has been removed from all entities — the project no longer +-- supports soft-delete. Clear residual deleted=1 rows so queries that still +-- reference the deleted column (or raw SQL in service layer) continue to +-- behave consistently. The deleted column itself is retained with its +-- NOT NULL DEFAULT 0 constraint for schema compatibility. +DELETE FROM mate_agent WHERE deleted = 1; +DELETE FROM mate_agent_skill WHERE deleted = 1; +DELETE FROM mate_agent_tool WHERE deleted = 1; +DELETE FROM mate_channel WHERE deleted = 1; +DELETE FROM mate_channel_session WHERE deleted = 1; +DELETE FROM mate_conversation WHERE deleted = 1; +DELETE FROM mate_cron_job WHERE deleted = 1; +DELETE FROM mate_datasource WHERE deleted = 1; +DELETE FROM mate_mcp_server WHERE deleted = 1; +DELETE FROM mate_memory_recall WHERE deleted = 1; +DELETE FROM mate_message WHERE deleted = 1; +DELETE FROM mate_model_config WHERE deleted = 1; +DELETE FROM mate_plan WHERE deleted = 1; +DELETE FROM mate_plugin WHERE deleted = 1; +DELETE FROM mate_skill WHERE deleted = 1; +DELETE FROM mate_sub_plan WHERE deleted = 1; +DELETE FROM mate_tool WHERE deleted = 1; +DELETE FROM mate_tool_approval WHERE deleted = 1; +DELETE FROM mate_tool_guard_audit_log WHERE deleted = 1; +DELETE FROM mate_tool_guard_rule WHERE deleted = 1; +DELETE FROM mate_user WHERE deleted = 1; +DELETE FROM mate_wiki_chunk WHERE deleted = 1; +DELETE FROM mate_wiki_knowledge_base WHERE deleted = 1; +DELETE FROM mate_wiki_page WHERE deleted = 1; +DELETE FROM mate_wiki_raw_material WHERE deleted = 1; +DELETE FROM mate_workspace WHERE deleted = 1; +DELETE FROM mate_workspace_file WHERE deleted = 1; +DELETE FROM mate_workspace_member WHERE deleted = 1; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V21__provider_fallback_priority.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V21__provider_fallback_priority.sql new file mode 100644 index 000000000..deffe62d9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V21__provider_fallback_priority.sql @@ -0,0 +1,26 @@ +-- RFC-009 Phase 1: ordered multi-provider fallback chain +-- +-- fallback_priority defines the order in which a provider is tried after the +-- primary model exhausts retries: +-- 0 : not in the fallback chain (default — matches pre-RFC behavior) +-- 1, 2, … : try in ascending order +-- +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use the INFORMATION_SCHEMA guard so +-- this migration is idempotent across redeploys. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'fallback_priority' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN fallback_priority INT DEFAULT 0; + END IF; +END $$; + +-- Seed: keep DashScope as priority 1 so existing deployments preserve the +-- single-fallback-to-DashScope behavior the hardcoded path used to provide. +UPDATE mate_model_provider + SET fallback_priority = 1 + WHERE provider_id = 'dashscope' + AND (fallback_priority IS NULL OR fallback_priority = 0); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V23__wiki_relation_model.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V23__wiki_relation_model.sql new file mode 100644 index 000000000..e3dc2e3b3 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V23__wiki_relation_model.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS mate_wiki_page_citation ( + id BIGINT PRIMARY KEY, + page_id BIGINT NOT NULL, + chunk_id BIGINT NOT NULL, + paragraph_idx INT NOT NULL DEFAULT 0, + anchor_text VARCHAR(512), + confidence DECIMAL(4,3) NOT NULL DEFAULT 1.000, + created_by VARCHAR(32) NOT NULL DEFAULT 'system', + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wpc_page ON mate_wiki_page_citation (page_id); +CREATE INDEX IF NOT EXISTS idx_wpc_chunk ON mate_wiki_page_citation (chunk_id); + +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS page_type VARCHAR(32) NOT NULL DEFAULT 'concept'; +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS purpose_hint TEXT; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql new file mode 100644 index 000000000..8c49747ab --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql @@ -0,0 +1,25 @@ +CREATE TABLE IF NOT EXISTS mate_wiki_processing_job ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + raw_id BIGINT NOT NULL, + job_type VARCHAR(32) NOT NULL DEFAULT 'heavy_ingest', + stage VARCHAR(64) NOT NULL DEFAULT 'queued', + status VARCHAR(32) NOT NULL DEFAULT 'queued', + primary_model_id BIGINT, + current_model_id BIGINT, + fallback_chain_json TEXT, + retry_count INT NOT NULL DEFAULT 0, + max_retries INT NOT NULL DEFAULT 3, + error_code VARCHAR(64), + error_message TEXT, + resume_from_stage VARCHAR(64), + meta_json TEXT, + started_at TIMESTAMP(3), + finished_at TIMESTAMP(3), + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_wpj_raw ON mate_wiki_processing_job (raw_id); +CREATE INDEX IF NOT EXISTS idx_wpj_status ON mate_wiki_processing_job (status); +CREATE INDEX IF NOT EXISTS idx_wpj_kb ON mate_wiki_processing_job (kb_id, status); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V25__agent_provider_preference.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V25__agent_provider_preference.sql new file mode 100644 index 000000000..aa434990a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V25__agent_provider_preference.sql @@ -0,0 +1,24 @@ +-- RFC-009 Phase 4 PR-3: per-agent provider preferences +-- +-- Lets each agent declare an ordered list of preferred provider ids. Empty +-- table for an agent (no rows) means "use the global fallback chain order" +-- — fully backwards compatible with pre-PR-3 behavior. When rows exist, +-- listed providers are tried in ascending sort_order before any non-listed +-- provider is considered. +-- +-- This is purely a routing hint. The runtime walker still gates each entry +-- through AvailableProviderPool / ProviderHealthTracker — a preferred +-- provider that is HARD-removed or in cooldown is still skipped. + +CREATE TABLE IF NOT EXISTS mate_agent_provider_preference ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + provider_id VARCHAR(128) NOT NULL, + sort_order INT NOT NULL DEFAULT 0, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_provider ON mate_agent_provider_preference (agent_id, provider_id); +CREATE INDEX IF NOT EXISTS idx_agent_provider_order ON mate_agent_provider_preference (agent_id, sort_order); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V26__dream_report.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V26__dream_report.sql new file mode 100644 index 000000000..f9d9b003e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V26__dream_report.sql @@ -0,0 +1,24 @@ +-- Dream v2: structured dream report (rfc-035 §4.4) +CREATE TABLE IF NOT EXISTS mate_dream_report ( + id BIGINT PRIMARY KEY, + agent_id BIGINT NOT NULL, + mode VARCHAR(32) NOT NULL, + topic VARCHAR(256), + trigger_source VARCHAR(32) NOT NULL, + triggered_by VARCHAR(64), + started_at TIMESTAMP NOT NULL, + finished_at TIMESTAMP NOT NULL, + candidate_count INT NOT NULL, + promoted_count INT NOT NULL, + rejected_count INT NOT NULL, + memory_diff TEXT, + llm_reason TEXT, + status VARCHAR(16) NOT NULL, + error_message TEXT, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted SMALLINT DEFAULT 0 +); + +CREATE INDEX idx_dream_agent_time ON mate_dream_report(agent_id, started_at DESC); +CREATE INDEX idx_dream_agent_mode ON mate_dream_report(agent_id, mode, started_at DESC); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V27__memory_recall_review_fields.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V27__memory_recall_review_fields.sql new file mode 100644 index 000000000..a561894a5 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V27__memory_recall_review_fields.sql @@ -0,0 +1,6 @@ +-- Dream v2: candidate state machine fields (rfc-035 4.1.4) +-- Phase 1 writes values only; filtering enabled in Phase 2. +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_memory_recall ADD COLUMN IF NOT EXISTS review_count INT DEFAULT 0; +ALTER TABLE mate_memory_recall ADD COLUMN IF NOT EXISTS last_reviewed_at TIMESTAMP; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V28__morning_card_seen.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V28__morning_card_seen.sql new file mode 100644 index 000000000..7a709a7d4 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V28__morning_card_seen.sql @@ -0,0 +1,12 @@ +-- Dream v2 Phase 2b: Morning Card seen state per (user, agent) +-- Ref: rfc-034 F5 — DO NOT add to mate_user; use separate table +CREATE TABLE IF NOT EXISTS mate_morning_card_seen ( + id BIGINT PRIMARY KEY , + user_id BIGINT NOT NULL, + agent_id BIGINT NOT NULL, + last_seen_at TIMESTAMP NOT NULL, + last_report_id BIGINT, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_user_agent ON mate_morning_card_seen (user_id, agent_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V29__memory_fact_projection.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V29__memory_fact_projection.sql new file mode 100644 index 000000000..af833ad29 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V29__memory_fact_projection.sql @@ -0,0 +1,49 @@ +-- Dream v2 Phase 3: Fact projection tables (read-only derived from canonical) +-- Ref: rfc-038 §3.3 + +CREATE TABLE IF NOT EXISTS mate_fact ( + id BIGINT PRIMARY KEY , + agent_id BIGINT NOT NULL, + source_ref VARCHAR(512) NOT NULL, + category VARCHAR(64), + subject VARCHAR(256), + predicate VARCHAR(256), + object_value TEXT, + confidence DOUBLE PRECISION DEFAULT 1.0, + trust DOUBLE PRECISION DEFAULT 0.5, + last_used_at TIMESTAMP, + use_count INT DEFAULT 0, + extracted_by VARCHAR(32) DEFAULT 'pattern', + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted SMALLINT DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_fact_agent_source ON mate_fact (agent_id, source_ref); +CREATE INDEX IF NOT EXISTS idx_fact_agent_subject ON mate_fact (agent_id, subject); +CREATE INDEX IF NOT EXISTS idx_fact_agent ON mate_fact (agent_id, deleted); + +CREATE TABLE IF NOT EXISTS mate_fact_entity_ref ( + id BIGINT PRIMARY KEY , + fact_id BIGINT NOT NULL, + entity_name VARCHAR(256) NOT NULL, + entity_type VARCHAR(64), + role VARCHAR(32) NOT NULL, + create_time TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_fact_ref_entity ON mate_fact_entity_ref (entity_name, entity_type); +CREATE INDEX IF NOT EXISTS idx_fact_ref_fact ON mate_fact_entity_ref (fact_id); + +CREATE TABLE IF NOT EXISTS mate_fact_contradiction ( + id BIGINT PRIMARY KEY , + agent_id BIGINT NOT NULL, + fact_a_id BIGINT NOT NULL, + fact_b_id BIGINT NOT NULL, + description TEXT, + resolution VARCHAR(32), + resolved_at TIMESTAMP, + resolved_by VARCHAR(64), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted SMALLINT DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_contradiction_agent ON mate_fact_contradiction (agent_id, resolution); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql new file mode 100644 index 000000000..785c104e9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql @@ -0,0 +1,227 @@ +-- V2: Upgrade schema for databases created before Flyway was introduced. +-- MySQL does NOT support ALTER TABLE ... ADD COLUMN IF NOT EXISTS (MariaDB-only). +-- We use INFORMATION_SCHEMA + dynamic SQL as an idempotent replacement so this migration +-- is safe on BOTH: (a) fresh MySQL installs whose V1 baseline already contains the columns, +-- and (b) legacy installs bootstrapped from the old schema.sql that predates those columns. + +CREATE TABLE IF NOT EXISTS mate_workspace ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + slug VARCHAR(64) NOT NULL, + description VARCHAR(256), + owner_id BIGINT, + settings_json TEXT, + base_path VARCHAR(512), + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_slug ON mate_workspace (slug); + +-- mate_workspace.base_path (legacy upgrade path) +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_workspace' AND column_name = 'base_path' + ) THEN + ALTER TABLE mate_workspace ADD COLUMN base_path VARCHAR(512); + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS mate_workspace_member ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + role VARCHAR(32) NOT NULL DEFAULT 'member', + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member (workspace_id); +CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member (user_id); + +-- workspace_id on pre-existing domain tables +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_agent' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_agent ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_channel' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_channel ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_conversation' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_conversation ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_wiki_knowledge_base ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_tool' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_tool ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_skill ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS mate_workspace_file ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + filename VARCHAR(256) NOT NULL, + content TEXT, + file_size BIGINT NOT NULL DEFAULT 0, + enabled BOOLEAN NOT NULL DEFAULT FALSE, + sort_order INT NOT NULL DEFAULT 0, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_workspace_file_agent ON mate_workspace_file (agent_id); + +CREATE TABLE IF NOT EXISTS mate_usage_daily ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + agent_id BIGINT NOT NULL, + stat_date DATE NOT NULL, + conversation_count INT NOT NULL DEFAULT 0, + message_count INT NOT NULL DEFAULT 0, + tool_call_count INT NOT NULL DEFAULT 0, + prompt_tokens BIGINT NOT NULL DEFAULT 0, + completion_tokens BIGINT NOT NULL DEFAULT 0, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_usage_daily ON mate_usage_daily (workspace_id, agent_id, stat_date); + +CREATE TABLE IF NOT EXISTS mate_audit_event ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT, + user_id BIGINT, + username VARCHAR(64), + action VARCHAR(64) NOT NULL, + resource_type VARCHAR(64), + resource_id VARCHAR(128), + detail TEXT, + ip_address VARCHAR(64), + create_time TIMESTAMP NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_audit_ws_time ON mate_audit_event (workspace_id, create_time); + +-- model provider OAuth columns +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'auth_type' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key'; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_access_token' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN oauth_access_token TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_refresh_token' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN oauth_refresh_token TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_expires_at' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN oauth_expires_at BIGINT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_account_id' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN oauth_account_id VARCHAR(128); + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS mate_agent_skill ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + skill_id BIGINT NOT NULL, + create_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE TABLE IF NOT EXISTS mate_agent_tool ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + tool_name VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS mate_memory_recall ( + id BIGINT NOT NULL PRIMARY KEY, + agent_id BIGINT NOT NULL, + filename VARCHAR(256) NOT NULL, + content TEXT, + tags VARCHAR(512), + score DOUBLE PRECISION NOT NULL DEFAULT 0.0, + last_recalled_at TIMESTAMP, + promoted BOOLEAN NOT NULL DEFAULT FALSE, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_memory_recall_agent ON mate_memory_recall (agent_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V30__register_collab_skills.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V30__register_collab_skills.sql new file mode 100644 index 000000000..8433442a9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V30__register_collab_skills.sql @@ -0,0 +1,60 @@ +-- Register 4 collaboration skills introduced in RFC-044. +-- These were previously only in seed data files; this migration ensures they exist +-- in all environments (including existing installs that have already run seed data). +-- Ref: rfc-044-skill-md-completion-2026-04-23 + +INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted) +VALUES (1000000016, 'make_plan', '当任务需要多步拆解或不确定执行路径时,向更强 Agent 请求一份分步可落地的执行计划,由当前 Agent 自己执行。', 'builtin', '🗺️', '1.3.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'plan,delegate,agent,collaboration', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description, + skill_type = EXCLUDED.skill_type, + icon = EXCLUDED.icon, + version = EXCLUDED.version, + author = EXCLUDED.author, + config_json = EXCLUDED.config_json, + enabled = EXCLUDED.enabled, + builtin = EXCLUDED.builtin, + tags = EXCLUDED.tags, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; + +INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted) +VALUES (1000000017, 'chat_with_agent', '当需要咨询其他 Agent、寻求帮助或用户明确要求某个 Agent 参与时,使用本技能进行单次或并行委托。', 'builtin', '💬', '1.2.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'agent,chat,collaborate,delegate', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description, + skill_type = EXCLUDED.skill_type, + icon = EXCLUDED.icon, + version = EXCLUDED.version, + author = EXCLUDED.author, + config_json = EXCLUDED.config_json, + enabled = EXCLUDED.enabled, + builtin = EXCLUDED.builtin, + tags = EXCLUDED.tags, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; + +INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted) +VALUES (1000000018, 'channel_message', '当需要主动向用户、会话或渠道单向推送消息时使用。任务完成通知、定时提醒、异步结果回推等场景。', 'builtin', '📤', '1.3.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'channel,message,push,notify,dingtalk,feishu', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description, + skill_type = EXCLUDED.skill_type, + icon = EXCLUDED.icon, + version = EXCLUDED.version, + author = EXCLUDED.author, + config_json = EXCLUDED.config_json, + enabled = EXCLUDED.enabled, + builtin = EXCLUDED.builtin, + tags = EXCLUDED.tags, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; + +INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted) +VALUES (1000000019, 'multi_agent_collaboration', '当任务需要多个 Agent 的专业能力协同完成时,编排多 Agent 并行或串行协作,整合各方结果。', 'builtin', '🤝', '1.4.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description, + skill_type = EXCLUDED.skill_type, + icon = EXCLUDED.icon, + version = EXCLUDED.version, + author = EXCLUDED.author, + config_json = EXCLUDED.config_json, + enabled = EXCLUDED.enabled, + builtin = EXCLUDED.builtin, + tags = EXCLUDED.tags, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V31__register_docx_render_tool.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V31__register_docx_render_tool.sql new file mode 100644 index 000000000..34cf800bd --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V31__register_docx_render_tool.sql @@ -0,0 +1,5 @@ +-- V31: Register DocxRenderTool as built-in tool (RFC-045) +-- Idempotent: ON DUPLICATE KEY UPDATE keeps the row in sync if it already exists. +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000019, 'DocxRenderTool', 'DOCX Render', 'Render Markdown directly into a .docx and return a one-time download link. In-process Apache POI implementation, no Node.js subprocess; supports headings, bold, lists, tables. Preferred tool for creating new documents.', 'builtin', 'docxRenderTool', '📝', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V32__bailian_team_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V32__bailian_team_provider.sql new file mode 100644 index 000000000..a693903be --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V32__bailian_team_provider.sql @@ -0,0 +1,24 @@ +-- V32: Register Aliyun Bailian Token Plan provider and models +-- OpenAI-compatible endpoint for team subscription users. +-- freeze_url = TRUE: the endpoint is plan-specific and must not be overridden. + +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('bailian-team', '百炼 Token Plan', 'sk-', 'OpenAIChatModel', '', 'https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1', '{}', FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, generate_kwargs=EXCLUDED.generate_kwargs, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time; + +-- Chat models +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000400, 'Qwen 3.6 Plus', 'bailian-team', 'qwen3.6-plus', '百炼团队套餐 — 千问旗舰推理模型,支持视觉理解与文本生成', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000401, 'DeepSeek V3.2', 'bailian-team', 'deepseek-v3.2', '百炼团队套餐 — DeepSeek 最新推理模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000402, 'GLM-5', 'bailian-team', 'glm-5', '百炼团队套餐 — 智谱 GLM-5 文本生成模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; + +-- Image generation models (temperature/max_tokens/top_p not applicable) +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000403, 'Qwen Image 2.0', 'bailian-team', 'qwen-image-2.0', '百炼团队套餐 — 千问图片生成模型', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'image', NOW(), NOW(), 0), +(1000000404, 'Qwen Image 2.0 Pro', 'bailian-team', 'qwen-image-2.0-pro', '百炼团队套餐 — 千问图片生成旗舰模型', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'image', NOW(), NOW(), 0), +(1000000405, 'Wan 2.7 Image', 'bailian-team', 'wan2.7-image', '百炼团队套餐 — 万相图片生成模型', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'image', NOW(), NOW(), 0), +(1000000406, 'Wan 2.7 Image Pro', 'bailian-team', 'wan2.7-image-pro', '百炼团队套餐 — 万相图片生成旗舰模型', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'image', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V33__expand_api_key_column.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V33__expand_api_key_column.sql new file mode 100644 index 000000000..13cca3085 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V33__expand_api_key_column.sql @@ -0,0 +1,3 @@ +-- V33: Expand mate_model_provider.api_key from VARCHAR(256) to VARCHAR(512) +-- Bailian Token Plan keys exceed 256 chars (observed: 298 chars). +ALTER TABLE mate_model_provider ALTER COLUMN api_key TYPE VARCHAR(512); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V34__siliconflow_opencode_providers.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V34__siliconflow_opencode_providers.sql new file mode 100644 index 000000000..4845cc2df --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V34__siliconflow_opencode_providers.sql @@ -0,0 +1,47 @@ +-- V34: Add SiliconFlow (CN + INTL) and OpenCode providers with preset models +-- SiliconFlow supports model discovery; preset models cover the most popular ones. +-- OpenCode is a free-tier provider with two fixed models. + +-- ── Providers ────────────────────────────────────────────────────────────── +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('siliconflow-cn', '硅基流动 (China)', 'sk-', 'OpenAIChatModel', '', 'https://api.siliconflow.cn/v1', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, support_model_discovery=EXCLUDED.support_model_discovery, support_connection_check=EXCLUDED.support_connection_check, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time; + +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('siliconflow-intl', '硅基流动 (International)', 'sk-', 'OpenAIChatModel', '', 'https://api.siliconflow.com/v1', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, support_model_discovery=EXCLUDED.support_model_discovery, support_connection_check=EXCLUDED.support_connection_check, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time; + +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('opencode', 'OpenCode', '', 'OpenAIChatModel', '', 'https://opencode.ai/zen/v1', '{}', FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, update_time=EXCLUDED.update_time; + +-- ── SiliconFlow CN — preset popular models ───────────────────────────────── +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000500, 'DeepSeek V3', 'siliconflow-cn', 'deepseek-ai/DeepSeek-V3', '硅基流动 — DeepSeek V3,综合能力强,有免费额度', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000501, 'DeepSeek R1', 'siliconflow-cn', 'deepseek-ai/DeepSeek-R1', '硅基流动 — DeepSeek R1 推理模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000502, 'Qwen3 235B A22B', 'siliconflow-cn', 'Qwen/Qwen3-235B-A22B', '硅基流动 — 千问3旗舰 MoE 模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000503, 'Qwen3 30B A3B', 'siliconflow-cn', 'Qwen/Qwen3-30B-A3B', '硅基流动 — 千问3高性价比 MoE 模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000504, 'GLM-4 9B Chat', 'siliconflow-cn', 'THUDM/glm-4-9b-chat', '硅基流动 — 智谱 GLM-4 9B,免费可用', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000505, 'DeepSeek V3 Pro', 'siliconflow-cn', 'Pro/deepseek-ai/DeepSeek-V3', '硅基流动 Pro — DeepSeek V3 Pro 优先调度版', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000506, 'DeepSeek R1 Pro', 'siliconflow-cn', 'Pro/deepseek-ai/DeepSeek-R1', '硅基流动 Pro — DeepSeek R1 推理 Pro 优先调度版', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; + +-- ── SiliconFlow INTL — same preset models via international endpoint ──────── +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000510, 'DeepSeek V3', 'siliconflow-intl', 'deepseek-ai/DeepSeek-V3', 'SiliconFlow INTL — DeepSeek V3, strong general capability', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000511, 'DeepSeek R1', 'siliconflow-intl', 'deepseek-ai/DeepSeek-R1', 'SiliconFlow INTL — DeepSeek R1 reasoning model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000512, 'Qwen3 235B A22B', 'siliconflow-intl', 'Qwen/Qwen3-235B-A22B', 'SiliconFlow INTL — Qwen3 flagship MoE model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000513, 'Qwen3 30B A3B', 'siliconflow-intl', 'Qwen/Qwen3-30B-A3B', 'SiliconFlow INTL — Qwen3 efficient MoE model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000514, 'GLM-4 9B Chat', 'siliconflow-intl', 'THUDM/glm-4-9b-chat', 'SiliconFlow INTL — Zhipu GLM-4 9B, free tier', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000515, 'DeepSeek V3 Pro', 'siliconflow-intl', 'Pro/deepseek-ai/DeepSeek-V3', 'SiliconFlow INTL Pro — DeepSeek V3 priority tier', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000516, 'DeepSeek R1 Pro', 'siliconflow-intl', 'Pro/deepseek-ai/DeepSeek-R1', 'SiliconFlow INTL Pro — DeepSeek R1 priority tier', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; + +-- ── OpenCode — free public models ────────────────────────────────────────── +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000520, 'Big Pickle', 'opencode', 'big-pickle', 'OpenCode 免费模型 — Big Pickle', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000521, 'Nemotron 3 Super Free', 'opencode', 'nemotron-3-super-free', 'OpenCode 免费模型 — Nemotron 3 Super', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V35__skill_security_scan_result.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V35__skill_security_scan_result.sql new file mode 100644 index 000000000..1d7a79391 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V35__skill_security_scan_result.sql @@ -0,0 +1,28 @@ +-- V35: RFC-042 §2.3 — persist skill security scan result and timestamp. +-- Until now findings lived only in SkillRuntimeStatus memory; after a restart +-- the admin page couldn't explain why a skill was blocked. These two columns +-- keep the last scan's findings (JSONB) and time so the UI can render them +-- and offer a rescan control. +-- +-- MySQL has no ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guards so +-- the migration is idempotent across redeploys. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'security_scan_result' + ) THEN + ALTER TABLE mate_skill ADD COLUMN security_scan_result TEXT DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'security_scan_time' + ) THEN + ALTER TABLE mate_skill ADD COLUMN security_scan_time TIMESTAMP DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V36__skill_i18n_name.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V36__skill_i18n_name.sql new file mode 100644 index 000000000..91b2e0b64 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V36__skill_i18n_name.sql @@ -0,0 +1,51 @@ +-- V36: RFC-042 §2.2 — bilingual display names for skills. +-- name stays the immutable slug / unique identifier; name_zh and +-- name_en are optional locale-specific display labels. The UI falls +-- back to name when the locale-matching column is null. +-- +-- MySQL has no ADD COLUMN IF NOT EXISTS; INFORMATION_SCHEMA guards +-- make the migration idempotent across redeploys. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'name_zh' + ) THEN + ALTER TABLE mate_skill ADD COLUMN name_zh VARCHAR(128) DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'name_en' + ) THEN + ALTER TABLE mate_skill ADD COLUMN name_en VARCHAR(128) DEFAULT NULL; + END IF; +END $$; + +-- Backfill bilingual names for the 19 builtin skills that already exist on +-- upgraded deployments. UPDATE is idempotent — running it again is a no-op +-- since the values match. Fresh installs handle this in data-*.sql instead +-- (those rows don't exist yet when this migration runs). +UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron'; +UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader'; +UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect'; +UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya'; +UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news'; +UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf'; +UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx'; +UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx'; +UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx'; +UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible'; +UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp'; +UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance'; +UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index'; +UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query'; +UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective'; +UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan'; +UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent'; +UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message'; +UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V37__wiki_page_source_entries.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V37__wiki_page_source_entries.sql new file mode 100644 index 000000000..11edcf83a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V37__wiki_page_source_entries.sql @@ -0,0 +1,6 @@ +-- RFC-047 P2: Add source_entries column to mate_wiki_page for paired (rawId, rawTitle) lineage. +-- Paired entries guarantee title-rawId alignment even when raw titles change. +-- Dual-written alongside the existing source_raw_ids for backwards compatibility. +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS source_entries TEXT NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V38__wiki_chunk_content_mediumtext.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V38__wiki_chunk_content_mediumtext.sql new file mode 100644 index 000000000..605abd6fa --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V38__wiki_chunk_content_mediumtext.sql @@ -0,0 +1,3 @@ +-- V38: Expand mate_wiki_chunk.content from TEXT (64KB) to TEXT (16MB) +-- In KingbaseES/PostgreSQL, TEXT is already unlimited (up to 1GB), +-- so this migration is a no-op. Keep for Flyway version compatibility. diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V39__rfc051_chunk_metadata.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V39__rfc051_chunk_metadata.sql new file mode 100644 index 000000000..a3519b746 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V39__rfc051_chunk_metadata.sql @@ -0,0 +1,41 @@ +-- V39: RFC-051 PR-1a — chunk structural metadata. +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'page_number' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN page_number INT DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'token_count' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN token_count INT DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'header_breadcrumb' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN header_breadcrumb VARCHAR(1024) DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_chunk' AND column_name = 'source_section' + ) THEN + ALTER TABLE mate_wiki_chunk ADD COLUMN source_section VARCHAR(512) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V3__register_cron_job_tool.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V3__register_cron_job_tool.sql new file mode 100644 index 000000000..92e64ecbf --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V3__register_cron_job_tool.sql @@ -0,0 +1,4 @@ +-- V3: Register CronJobTool as built-in tool (RFC-003) +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000018, 'CronJobTool', 'Scheduled Tasks', 'Create, list, enable/disable, and delete scheduled tasks (cron jobs) through chat.', 'builtin', 'cronJobTool', '⏰', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V40__rfc051_page_locked.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V40__rfc051_page_locked.sql new file mode 100644 index 000000000..23aa877d9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V40__rfc051_page_locked.sql @@ -0,0 +1,11 @@ +-- V40: RFC-051 PR-2 — page protection flag. +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'locked' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN locked SMALLINT NOT NULL DEFAULT 0; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V41__rfc051_page_archived.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V41__rfc051_page_archived.sql new file mode 100644 index 000000000..ba7db5ec0 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V41__rfc051_page_archived.sql @@ -0,0 +1,10 @@ +-- V41: RFC-051 PR-7 — soft-archive flag. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_page' AND column_name = 'archived' + ) THEN + ALTER TABLE mate_wiki_page ADD COLUMN archived SMALLINT NOT NULL DEFAULT 0; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V42__claude_47_gpt_55_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V42__claude_47_gpt_55_models.sql new file mode 100644 index 000000000..be910d0cd --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V42__claude_47_gpt_55_models.sql @@ -0,0 +1,37 @@ +-- Add Claude 4.7 + GPT-5.5 model entries to mate_model_config for existing +-- deployments. New installs pick these up via DatabaseBootstrapRunner from +-- data-mysql-{en,zh}.sql; this migration covers operators who already have +-- earlier Flyway versions applied. +-- +-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert. +-- Same V number is used in h2/ for cross-dialect parity. + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES +-- GPT-5.5 series (OpenAI / Azure / OpenRouter) +(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +-- Claude 4.7 series. NOTE: Claude 4.7 forbids temperature/top_p/top_k — +-- handled in AgentAnthropicChatModelBuilder. NULL temperature/top_p in seed +-- is the documented signal. +(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + provider = EXCLUDED.provider, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + temperature = EXCLUDED.temperature, + max_tokens = EXCLUDED.max_tokens, + top_p = EXCLUDED.top_p, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V43__rfc062_claude_code_oauth_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V43__rfc062_claude_code_oauth_provider.sql new file mode 100644 index 000000000..f41d63f04 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V43__rfc062_claude_code_oauth_provider.sql @@ -0,0 +1,32 @@ +-- RFC-062: Seed the Anthropic Claude Code OAuth provider + its Claude 4.7 +-- model bindings on existing deployments. New installs already get these +-- rows from data-mysql-{en,zh}.sql via DatabaseBootstrapRunner; this +-- migration is for operators upgrading from <= V42. +-- +-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert. +-- Same V number is used in h2/ for cross-dialect parity. + +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, auth_type, create_time, update_time) +VALUES ('anthropic-claude-code', 'Anthropic Claude Code (OAuth)', '', 'ClaudeCodeChatModel', '', 'https://api.anthropic.com', '{}', FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, 'oauth', NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + auth_type = EXCLUDED.auth_type, + update_time = EXCLUDED.update_time; + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES +(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', 'Claude Opus 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', 'Claude Sonnet 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + provider = EXCLUDED.provider, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + temperature = EXCLUDED.temperature, + max_tokens = EXCLUDED.max_tokens, + top_p = EXCLUDED.top_p, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V44__fix_claude_sonnet_47_does_not_exist.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V44__fix_claude_sonnet_47_does_not_exist.sql new file mode 100644 index 000000000..f6eb6d39a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V44__fix_claude_sonnet_47_does_not_exist.sql @@ -0,0 +1,31 @@ +-- Repair migration for V42/V43: Anthropic only released Opus 4.7 — there +-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body +-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}. +-- +-- Anthropic's current model line includes claude-opus-4-7 but no +-- claude-sonnet-4-7. The latest released Sonnet remains claude-sonnet-4-6 +-- (released alongside Opus 4.6). +-- +-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281 +-- so user-customised settings (default flag, enabled flag) survive. + +UPDATE mate_model_config +SET name = 'Claude Sonnet 4.6', + model_name = 'claude-sonnet-4-6', + description = 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)', + update_time = NOW() +WHERE id = 1000000271 AND model_name = 'claude-sonnet-4-7'; + +UPDATE mate_model_config +SET name = 'Claude Sonnet 4.6', + model_name = 'anthropic/claude-sonnet-4-6', + description = 'Claude Sonnet 4.6 via OpenRouter', + update_time = NOW() +WHERE id = 1000000273 AND model_name = 'anthropic/claude-sonnet-4-7'; + +UPDATE mate_model_config +SET name = 'Claude Sonnet 4.6', + model_name = 'claude-sonnet-4-6', + description = 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription', + update_time = NOW() +WHERE id = 1000000281 AND model_name = 'claude-sonnet-4-7'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V45__deepseek_v4_models.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V45__deepseek_v4_models.sql new file mode 100644 index 000000000..24223faba --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V45__deepseek_v4_models.sql @@ -0,0 +1,19 @@ +-- Add DeepSeek V4 (flash + pro) model entries for MySQL deployments. +-- Cross-dialect parity with h2/V45 — see that file's header for context. + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES +(1000000282, 'DeepSeek V4 Flash', 'deepseek', 'deepseek-v4-flash', 'DeepSeek V4 Flash (1M context, reasoning via thinking-enabled mode)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0), +(1000000283, 'DeepSeek V4 Pro', 'deepseek', 'deepseek-v4-pro', 'DeepSeek V4 Pro (1M context, reasoning via thinking-enabled mode)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + provider = EXCLUDED.provider, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + temperature = EXCLUDED.temperature, + max_tokens = EXCLUDED.max_tokens, + top_p = EXCLUDED.top_p, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + update_time = EXCLUDED.update_time, + deleted = EXCLUDED.deleted; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V46__stt_default_enabled.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V46__stt_default_enabled.sql new file mode 100644 index 000000000..2ed0518fe --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V46__stt_default_enabled.sql @@ -0,0 +1,21 @@ +-- Default-enable STT on existing deployments. See the h2/ counterpart for +-- the "why enabled by default" rationale and the bug history that drove +-- the skip-if-exists idiom. Same V number is used in h2/ for cross-dialect +-- parity. +-- +-- MySQL doesn't allow INSERT ... SELECT ... WHERE NOT EXISTS without a +-- FROM clause, so we synthesise one with FROM DUAL. The end result is +-- the same: insert when the setting_key is absent, no-op when it's already +-- there. + +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +SELECT 1000000020, 'sttEnabled', 'true', 'Enable speech-to-text (TalkMode mic input)', NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttEnabled'); + +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +SELECT 1000000021, 'sttProvider', 'auto', 'STT provider: auto / openai / dashscope', NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttProvider'); + +INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time) +SELECT 1000000022, 'sttFallbackEnabled', 'true', 'Try alternate STT provider when the primary fails', NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttFallbackEnabled'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V47__agent_max_iterations_100.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V47__agent_max_iterations_100.sql new file mode 100644 index 000000000..7c3f8b4da --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V47__agent_max_iterations_100.sql @@ -0,0 +1,16 @@ +-- V47: Bump default agents' max_iterations to 100 (hard ceiling). +-- +-- The previous defaults (25 for ReAct, 20 for plan-execute) ran the LimitExceededNode +-- too eagerly on substantive multi-tool tasks (e.g. document generation with image +-- conversion). New default is 100, the hard upper bound enforced at runtime. +-- AgentGraphBuilder still clamps any per-agent override to MAX_ITERATIONS_HARD_CEILING +-- at runtime, so a user-configured 200 will be silently capped to 100. +-- +-- Idempotent: only updates rows that still hold the old defaults, so user-customized +-- agents are not touched. + +UPDATE mate_agent SET max_iterations = 100 +WHERE id IN (1000000001, 1000000003) AND max_iterations = 25; + +UPDATE mate_agent SET max_iterations = 100 +WHERE id = 1000000002 AND max_iterations = 20; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V48__agent_max_iterations_and_agents_md_tools.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V48__agent_max_iterations_and_agents_md_tools.sql new file mode 100644 index 000000000..221644c5e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V48__agent_max_iterations_and_agents_md_tools.sql @@ -0,0 +1,20 @@ +-- V48 (Kingbase): see h2/V48 for full rationale. +-- Use || instead of CONCAT() because Kingbase Oracle-compat CONCAT() only takes 2 args. + +UPDATE mate_agent SET max_iterations = 100 +WHERE id IN (1000000001, 1000000002, 1000000003) + AND (max_iterations IS NULL OR max_iterations < 100); + +UPDATE mate_workspace_file +SET content = REPLACE( + content, + '需要执行文件操作或命令时,直接调用对应的工具(如 execute_shell_command、read_file 等),不要用文本描述你要做什么。', + '需要执行文件操作或命令时,直接调用对应的工具:' || CHR(10) || + '- 读文件 → read_file' || CHR(10) || + '- 写新文件或覆盖整个文件 → write_file(一次写完整内容,不要用 printf / heredoc / echo 拼)' || CHR(10) || + '- 修改已有文件局部内容 → edit_file' || CHR(10) || + '- 执行 shell 命令 → execute_shell_command' || CHR(10) || + '不要用文本描述你要做什么。' + ) +WHERE filename = 'AGENTS.md' + AND content LIKE '%execute_shell_command、read_file%'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V49__reenable_write_edit_file_tools.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V49__reenable_write_edit_file_tools.sql new file mode 100644 index 000000000..638f4b0ac --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V49__reenable_write_edit_file_tools.sql @@ -0,0 +1,5 @@ +-- V49 (MySQL): see h2/V49 for full rationale. + +UPDATE mate_tool SET enabled = TRUE +WHERE bean_name IN ('writeFileTool', 'editFileTool') + AND enabled = FALSE; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V4__agent_thinking_level.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V4__agent_thinking_level.sql new file mode 100644 index 000000000..0f5226ca3 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V4__agent_thinking_level.sql @@ -0,0 +1,12 @@ +-- V4: Add default_thinking_level to mate_agent +-- Supports: off / low / medium / high / max (null = follow model default) +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_agent' AND column_name = 'default_thinking_level' + ) THEN + ALTER TABLE mate_agent ADD COLUMN default_thinking_level VARCHAR(32) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V50__fix_agents_md_tool_guidance_concat.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V50__fix_agents_md_tool_guidance_concat.sql new file mode 100644 index 000000000..30cc456fd --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V50__fix_agents_md_tool_guidance_concat.sql @@ -0,0 +1,50 @@ +-- V50 (Kingbase): mirror of h2/V50. +-- Use || instead of CONCAT() because Kingbase Oracle-compat CONCAT() only takes 2 args. + +UPDATE mate_workspace_file +SET content = + '## 记忆' || CHR(10) || + CHR(10) || + '你的记忆由数据库工作区文件提供连续性:' || CHR(10) || + CHR(10) || + '- PROFILE.md:稳定用户画像与协作偏好' || CHR(10) || + '- MEMORY.md:长期事实、经验教训、工具设置、反复出现的模式' || CHR(10) || + '- memory/YYYY-MM-DD.md:当日事件、观察、一次性上下文' || CHR(10) || + CHR(10) || + '### 记忆策略' || CHR(10) || + CHR(10) || + '- 稳定信息进入 PROFILE.md 或 MEMORY.md' || CHR(10) || + '- 临时事件进入 memory/YYYY-MM-DD.md' || CHR(10) || + '- 修改前先读取原文,优先做增量编辑而不是整篇重写' || CHR(10) || + '- 避免记录敏感信息,除非用户明确要求' || CHR(10) || + CHR(10) || + '### 主动召回' || CHR(10) || + CHR(10) || + '- 遇到历史偏好、旧决策、持续任务、用户习惯时,优先查看工作区记忆' || CHR(10) || + '- 不确定具体发生日期时,检查相关 memory/YYYY-MM-DD.md' || CHR(10) || + CHR(10) || + '## 安全' || CHR(10) || + CHR(10) || + '- 绝不泄露私密数据。' || CHR(10) || + '- 拿不准的事情,先确认。' || CHR(10) || + CHR(10) || + '## 边界' || CHR(10) || + CHR(10) || + '- 私密的保持私密。' || CHR(10) || + '- 需要执行文件操作或命令时,**必须**调用对应的工具:' || CHR(10) || + ' - 读文件 → read_file' || CHR(10) || + ' - 写新文件或覆盖整个文件 → write_file(一次写完整内容,不要用 printf / heredoc / echo / cat << EOF 拼字符串)' || CHR(10) || + ' - 修改已有文件局部内容 → edit_file' || CHR(10) || + ' - 执行 shell 命令 → execute_shell_command' || CHR(10) || + ' 禁止用 shell 命令绕过 write_file 写文件。系统会自动对危险操作弹出审批确认。' || CHR(10) || + '- 拿不准就先问。' || CHR(10) || + CHR(10) || + '## 风格' || CHR(10) || + CHR(10) || + '该简洁就简洁,重要时详细。' || CHR(10) || + CHR(10) || + '## 连续性' || CHR(10) || + CHR(10) || + '每次会话都全新醒来。工作区文件就是你的记忆。读它们。更新它们。' || CHR(10) +WHERE filename = 'AGENTS.md' + AND agent_id IN (1000000001, 1000000002, 1000000003); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V51__remove_write_edit_file_from_toolguard.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V51__remove_write_edit_file_from_toolguard.sql new file mode 100644 index 000000000..60f6b1849 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V51__remove_write_edit_file_from_toolguard.sql @@ -0,0 +1,5 @@ +-- V51 (MySQL): see h2/V51 for full rationale. + +UPDATE mate_tool_guard_config +SET guarded_tools_json = '["execute_shell_command"]' +WHERE id = 1000000001; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V52__feishu_default_connection_mode.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V52__feishu_default_connection_mode.sql new file mode 100644 index 000000000..ee8a2b3f0 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V52__feishu_default_connection_mode.sql @@ -0,0 +1,4 @@ +-- V52: Intentional no-op (deprecated). See h2/V52 for the full rationale. +-- The actual migration was moved to V53. + +SELECT 1; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql new file mode 100644 index 000000000..81c675800 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql @@ -0,0 +1,24 @@ +-- V53: Recover from V52's silent no-op. +-- See h2/V53 for the full rationale. Same surgery, Kingbase-flavored. +-- Idempotent: rows already at "websocket" are skipped by the WHERE clause. + +UPDATE mate_channel +SET config_json = CASE + WHEN config_json IS NULL OR TRIM(config_json) = '' OR TRIM(config_json) = '{}' THEN + '{"connection_mode":"websocket"}' + WHEN POSITION('"connection_mode"' IN config_json) = 0 THEN + CONCAT('{"connection_mode":"websocket",', SUBSTRING(config_json, 2)) + ELSE + REPLACE( + REPLACE(config_json, + '"connection_mode": "webhook"', '"connection_mode": "websocket"'), + '"connection_mode":"webhook"', '"connection_mode":"websocket"' + ) + END +WHERE channel_type = 'feishu' + AND deleted = 0 + AND ( + config_json IS NULL + OR (POSITION('"connection_mode": "websocket"' IN config_json) = 0 + AND POSITION('"connection_mode":"websocket"' IN config_json) = 0) + ); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V54__channel_names_to_chinese_for_zh_locale.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V54__channel_names_to_chinese_for_zh_locale.sql new file mode 100644 index 000000000..1241fa391 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V54__channel_names_to_chinese_for_zh_locale.sql @@ -0,0 +1,45 @@ +-- V54: Localize seeded channel names to Chinese for zh-CN installs. +-- See h2/V54 for the full rationale. Same logic, MySQL-flavored. +-- Idempotent: re-running finds no matching rows after the first apply. + +UPDATE mate_channel SET name = 'Web 控制台' + WHERE id = 1000000001 AND name = 'Web Console' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = '钉钉机器人' + WHERE id = 1000000002 AND name = 'DingTalk Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = '飞书机器人' + WHERE id = 1000000003 AND name = 'Feishu Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = 'Telegram 机器人' + WHERE id = 1000000004 AND name = 'Telegram Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = 'Discord 机器人' + WHERE id = 1000000005 AND name = 'Discord Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = '企业微信机器人' + WHERE id = 1000000006 AND name = 'WeCom Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +UPDATE mate_channel SET name = 'QQ 机器人' + WHERE id = 1000000007 AND name = 'QQ Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); + +-- id 1000000008 ("微信") already in Chinese; intentionally skipped. + +UPDATE mate_channel SET name = 'Slack 机器人' + WHERE id = 1000000009 AND name = 'Slack Bot' + AND EXISTS (SELECT 1 FROM mate_system_setting + WHERE setting_key = 'language' AND setting_value = 'zh-CN'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V55__provider_enabled.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V55__provider_enabled.sql new file mode 100644 index 000000000..0aebcdd45 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V55__provider_enabled.sql @@ -0,0 +1,45 @@ +-- V55 (RFC-074): explicit user-enabled flag on providers. See H2 sibling for +-- the full rationale; this file only differs in dialect-specific syntax. +-- +-- MySQL lacks ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS — +-- guard via INFORMATION_SCHEMA + dynamic SQL so re-runs are no-ops. + +-- ── Add enabled column ─────────────────────────────────────────────────── +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_provider' AND column_name = 'enabled' + ) THEN + ALTER TABLE mate_model_provider ADD COLUMN enabled BOOLEAN DEFAULT FALSE; + END IF; +END $$; + +-- ── Index supporting Rule 3's 30-day usage lookup ────────────────────────── +CREATE INDEX IF NOT EXISTS idx_message_runtime_provider_time ON mate_message (runtime_provider, create_time); + +-- ── Rule 1: real (non-masked, non-empty) API key → user is using it ──────── +UPDATE mate_model_provider + SET enabled = TRUE + WHERE api_key IS NOT NULL AND api_key <> '' AND POSITION('*' IN api_key) = 0; + +-- ── Rule 2: OAuth provider with token → user is using it ─────────────────── +UPDATE mate_model_provider + SET enabled = TRUE + WHERE oauth_access_token IS NOT NULL AND oauth_access_token <> ''; + +-- ── Rule 3: local provider with messages in last 30 days → user is using it ─ +UPDATE mate_model_provider + SET enabled = TRUE + WHERE is_local = TRUE + AND provider_id IN ( + SELECT DISTINCT runtime_provider + FROM mate_message + WHERE runtime_provider IS NOT NULL + AND create_time >= CURRENT_TIMESTAMP - INTERVAL '30 days' + ); + +-- ── Rule 4: provider whose model is the current default → user is using it ─ +UPDATE mate_model_provider + SET enabled = TRUE + WHERE provider_id IN (SELECT provider FROM mate_model_config WHERE is_default = TRUE); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V56__volcengine_plan_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V56__volcengine_plan_provider.sql new file mode 100644 index 000000000..853bc8a38 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V56__volcengine_plan_provider.sql @@ -0,0 +1,29 @@ +-- V56: register Volcano Ark Coding Plan as a separate provider with its own +-- pre-seeded model catalog. See the H2 copy for full background. + +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('volcengine-plan', 'Volcano Engine Coding Plan (火山方舟代码计划)', '', 'OpenAIChatModel', '', 'https://ark.cn-beijing.volces.com/api/coding/v3', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000000320, 'Ark Coding Plan', 'volcengine-plan', 'ark-code-latest', '方舟代码计划旗舰模型,256K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000321, 'Doubao Seed Code', 'volcengine-plan', 'doubao-seed-code', '豆包代码模型,256K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000322, 'Doubao Seed Code Preview', 'volcengine-plan', 'doubao-seed-code-preview-251028', '豆包代码预览模型,256K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000323, 'GLM 4.7 Coding', 'volcengine-plan', 'glm-4.7', 'GLM 4.7 编码版(火山方舟托管),200K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000324, 'Kimi K2 Thinking', 'volcengine-plan', 'kimi-k2-thinking', 'Kimi K2 推理版(火山方舟托管),256K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000325, 'Kimi K2.5 Coding', 'volcengine-plan', 'kimi-k2.5', 'Kimi K2.5 编码版(火山方舟托管),256K 上下文', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V57__cron_run_delivery_status.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V57__cron_run_delivery_status.sql new file mode 100644 index 000000000..3a6acbdce --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V57__cron_run_delivery_status.sql @@ -0,0 +1,38 @@ +-- RFC-063r §2.9: cron run delivery state machine (MySQL dialect). +-- See V57 H2 file for state-machine + design reasoning. +-- +-- MySQL has no ADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS — +-- guard each statement via INFORMATION_SCHEMA + dynamic SQL (project pattern +-- previously used in V4/V19/V44). + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job_run' AND column_name = 'delivery_status' + ) THEN + ALTER TABLE mate_cron_job_run ADD COLUMN delivery_status VARCHAR(16) NOT NULL DEFAULT 'NONE'; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job_run' AND column_name = 'delivery_target' + ) THEN + ALTER TABLE mate_cron_job_run ADD COLUMN delivery_target VARCHAR(512); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job_run' AND column_name = 'delivery_error' + ) THEN + ALTER TABLE mate_cron_job_run ADD COLUMN delivery_error VARCHAR(500); + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_cron_run_pending_started ON mate_cron_job_run (delivery_status, started_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql new file mode 100644 index 000000000..5380c9b4f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql @@ -0,0 +1,23 @@ +-- RFC-063r §2.9: bind a cron job to its originating channel (MySQL dialect). + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job' AND column_name = 'channel_id' + ) THEN + ALTER TABLE mate_cron_job ADD COLUMN channel_id BIGINT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job' AND column_name = 'delivery_config' + ) THEN + ALTER TABLE mate_cron_job ADD COLUMN delivery_config TEXT; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_cron_channel ON mate_cron_job (channel_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V59__approval_chat_origin_snapshot.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V59__approval_chat_origin_snapshot.sql new file mode 100644 index 000000000..1da5a73ff --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V59__approval_chat_origin_snapshot.sql @@ -0,0 +1,11 @@ +-- RFC-063r §2.12: persist ChatOrigin Memento snapshot on approval (MySQL dialect). + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_tool_approval' AND column_name = 'chat_origin' + ) THEN + ALTER TABLE mate_tool_approval ADD COLUMN chat_origin TEXT; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V5__conversation_parent.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V5__conversation_parent.sql new file mode 100644 index 000000000..69fce6974 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V5__conversation_parent.sql @@ -0,0 +1,13 @@ +-- V5: Add parent_conversation_id to mate_conversation for multi-agent delegation tracking +-- MySQL lacks ADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS; use INFORMATION_SCHEMA guards. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_conversation' AND column_name = 'parent_conversation_id' + ) THEN + ALTER TABLE mate_conversation ADD COLUMN parent_conversation_id VARCHAR(64) DEFAULT NULL; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_conversation_parent ON mate_conversation (parent_conversation_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V60__fix_invalid_workspace_member_roles.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V60__fix_invalid_workspace_member_roles.sql new file mode 100644 index 000000000..c569cdb14 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V60__fix_invalid_workspace_member_roles.sql @@ -0,0 +1,26 @@ +-- RFC-076: clean up invalid workspace_member rows produced by the legacy +-- WorkspaceSchemaMigration.ensureDefaultWorkspaceMembership() insert +-- (issue: https://github.com/matevip/mateclaw/issues/29). + +-- 1) For users who already have a valid membership in another workspace, +-- drop their illegal default-workspace membership. +DELETE FROM mate_workspace_member +WHERE workspace_id = 1 + AND deleted = 0 + AND role NOT IN ('owner', 'admin', 'member', 'viewer') + AND user_id IN ( + SELECT user_id FROM ( + SELECT user_id FROM mate_workspace_member + WHERE workspace_id <> 1 + AND deleted = 0 + AND role IN ('owner', 'admin', 'member', 'viewer') + ) t + ); + +-- 2) For orphans whose only membership is the illegal default one, +-- normalize the role to 'member' so they don't get locked out entirely. +UPDATE mate_workspace_member +SET role = 'member', update_time = NOW() +WHERE workspace_id = 1 + AND deleted = 0 + AND role NOT IN ('owner', 'admin', 'member', 'viewer'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V61__agent_creator_user_id.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V61__agent_creator_user_id.sql new file mode 100644 index 000000000..6318259e6 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V61__agent_creator_user_id.sql @@ -0,0 +1,14 @@ +-- RFC-077 §4.1: track which user created an Agent, so members can delete +-- their own Agents without needing workspace admin role (issue #26 Bug B). + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_agent' AND column_name = 'creator_user_id' + ) THEN + ALTER TABLE mate_agent ADD COLUMN creator_user_id BIGINT; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_agent_creator_user ON mate_agent (creator_user_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V62__cron_job_workspace_id.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V62__cron_job_workspace_id.sql new file mode 100644 index 000000000..d6d375309 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V62__cron_job_workspace_id.sql @@ -0,0 +1,14 @@ +-- RFC-083: workspace-isolate cron jobs +-- (issue: https://github.com/matevip/mateclaw/issues/37). + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_cron_job' AND column_name = 'workspace_id' + ) THEN + ALTER TABLE mate_cron_job ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1; + END IF; +END $$; + +CREATE INDEX IF NOT EXISTS idx_cron_job_workspace ON mate_cron_job (workspace_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql new file mode 100644 index 000000000..4e9b5274d --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql @@ -0,0 +1,14 @@ +-- RFC-084 follow-up: persist the identity returned by ChannelVerifier so +-- the channel list can show "Connected as @MyBot" instead of generic +-- type-level descriptions. Populated on wizard create from VerificationResult; +-- refreshed by adapters on first successful connect. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_channel' AND column_name = 'identity_json' + ) THEN + ALTER TABLE mate_channel ADD COLUMN identity_json TEXT; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V64__cleanup_unused_channel_seeds.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V64__cleanup_unused_channel_seeds.sql new file mode 100644 index 000000000..36a6a076b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V64__cleanup_unused_channel_seeds.sql @@ -0,0 +1,10 @@ +-- RFC-084 follow-up: see h2/V64 for the rationale. MySQL accepts the +-- same DELETE directly — DELETE on no matching rows is a no-op, not an +-- error, so no INFORMATION_SCHEMA guard is needed. + +DELETE FROM mate_channel +WHERE id IN (1000000002, 1000000003, 1000000004, 1000000005, + 1000000006, 1000000007, 1000000008, 1000000009) + AND channel_type IN ('dingtalk', 'feishu', 'telegram', 'discord', + 'wecom', 'qq', 'weixin', 'slack') + AND enabled = FALSE; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V65__seed_tasks_conversation_per_workspace.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V65__seed_tasks_conversation_per_workspace.sql new file mode 100644 index 000000000..ce5b9c87b --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V65__seed_tasks_conversation_per_workspace.sql @@ -0,0 +1,28 @@ +-- See h2/V65 for rationale. MySQL syntax differs only in the string +-- concatenation operator (CONCAT vs ||). + +INSERT INTO mate_conversation + (id, conversation_id, title, agent_id, username, message_count, + last_message, last_active_time, stream_status, workspace_id, + parent_conversation_id, create_time, update_time, deleted) +SELECT + 1000200000 + ws.id, + CONCAT('tasks_', ws.id), + '📋 定时任务', + NULL, + 'system', + 0, + NULL, + NOW(), + 'idle', + ws.id, + NULL, + NOW(), + NOW(), + 0 +FROM mate_workspace ws +WHERE ws.deleted = 0 + AND NOT EXISTS ( + SELECT 1 FROM mate_conversation c + WHERE c.conversation_id = CONCAT('tasks_', ws.id) AND c.deleted = 0 + ); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V66__add_model_capabilities.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V66__add_model_capabilities.sql new file mode 100644 index 000000000..4a38a5128 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V66__add_model_capabilities.sql @@ -0,0 +1,11 @@ +-- V66: Per-model capability declaration (issue #44) +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_config' AND column_name = 'modalities' + ) THEN + ALTER TABLE mate_model_config ADD COLUMN modalities VARCHAR(512) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql new file mode 100644 index 000000000..315bef7c2 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql @@ -0,0 +1,14 @@ +-- V67: Skill manifest_json column (RFC-090 Phase 2) +-- Stores the full parsed SKILL.md frontmatter as JSON. This becomes the +-- source of truth (RFC-090 §14.6); existing columns (skill_type, icon, +-- version, author) are kept as index projections, written by +-- SkillPackageResolver.projectManifestToColumns after each resolve. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_skill' AND column_name = 'manifest_json' + ) THEN + ALTER TABLE mate_skill ADD COLUMN manifest_json TEXT; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql new file mode 100644 index 000000000..adde830ff --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql @@ -0,0 +1,43 @@ +-- V68: ACP (Agent Communication Protocol) endpoint registry (RFC-090 Phase 7) +-- See h2/V68 for column rationale; MySQL needs INSERT ... ON DUPLICATE KEY +-- and a unique index on name for the seed merge to be idempotent. +CREATE TABLE IF NOT EXISTS mate_acp_endpoint ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + display_name VARCHAR(128), + description TEXT, + command VARCHAR(256) NOT NULL, + args_json TEXT, + env_json TEXT, + tool_parse_mode VARCHAR(32) NOT NULL DEFAULT 'call_title', + builtin BOOLEAN NOT NULL DEFAULT FALSE, + trusted BOOLEAN NOT NULL DEFAULT TRUE, + enabled BOOLEAN NOT NULL DEFAULT FALSE, + stdio_buffer_limit_bytes BIGINT NOT NULL DEFAULT 52428800, + last_status VARCHAR(32), + last_tested_at TIMESTAMP, + last_error TEXT, + workspace_id BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_acp_endpoint_name ON mate_acp_endpoint (name); + +INSERT INTO mate_acp_endpoint + (id, name, display_name, description, command, args_json, env_json, + tool_parse_mode, builtin, trusted, enabled, + stdio_buffer_limit_bytes, workspace_id, create_time, update_time, deleted) +VALUES + (9100001, 'codex', 'OpenAI Codex CLI', 'Delegate to the Codex ACP agent via npx', 'npx', '["-y","@zed-industries/codex-acp"]', '{}', 'call_detail', TRUE, TRUE, FALSE, 52428800, 1, NOW(), NOW(), 0), + (9100002, 'claude-code', 'Claude Code', 'Delegate to Anthropic''s Claude Code agent via npx', 'npx', '["-y","@zed-industries/claude-agent-acp"]', '{}', 'update_detail', TRUE, TRUE, FALSE, 52428800, 1, NOW(), NOW(), 0), + (9100003, 'opencode', 'OpenCode', 'Delegate to OpenCode ACP agent (binary on PATH)', 'opencode', '["acp"]', '{}', 'update_detail', TRUE, TRUE, FALSE, 52428800, 1, NOW(), NOW(), 0), + (9100004, 'qwen-code', 'Qwen Code', 'Delegate to Qwen Code ACP agent (binary on PATH)', 'qwen', '["--acp"]', '{}', 'call_detail', TRUE, TRUE, FALSE, 52428800, 1, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET display_name = EXCLUDED.display_name, + description = EXCLUDED.description, + command = EXCLUDED.command, + args_json = EXCLUDED.args_json, + tool_parse_mode = EXCLUDED.tool_parse_mode, + builtin = EXCLUDED.builtin, + trusted = EXCLUDED.trusted, + update_time = NOW(); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V69__cron_job_dedup_unique.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V69__cron_job_dedup_unique.sql new file mode 100644 index 000000000..38cf96fe2 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V69__cron_job_dedup_unique.sql @@ -0,0 +1,16 @@ +-- Issue #50: deduplicate accumulated cron jobs and prevent future duplicates +-- at the DB level. PostgreSQL allows DELETE with subquery on the same table. +-- +-- Step 1 — purge duplicate active rows, keeping the earliest id per +-- (workspace_id, agent_id, name). Hard delete because this entity has no +-- @TableLogic; deleteById() already performs physical deletes. +DELETE FROM mate_cron_job +WHERE id NOT IN ( + SELECT MIN(id) + FROM mate_cron_job + GROUP BY workspace_id, agent_id, name +); + +-- Step 2 — add the unique index, idempotent via INFORMATION_SCHEMA guard +-- (MySQL < 8.0.29 has no CREATE INDEX IF NOT EXISTS). +CREATE UNIQUE INDEX IF NOT EXISTS uk_cron_job_workspace_agent_name ON mate_cron_job (workspace_id, agent_id, name); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql new file mode 100644 index 000000000..7e1d3068a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql @@ -0,0 +1,20 @@ +-- Plugin SDK: mate_plugin table +CREATE TABLE IF NOT EXISTS mate_plugin ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + version VARCHAR(32) NOT NULL, + plugin_type VARCHAR(32) NOT NULL, + display_name VARCHAR(128), + description TEXT, + author VARCHAR(128), + entrypoint VARCHAR(256) NOT NULL, + jar_path VARCHAR(512), + config_json TEXT NOT NULL DEFAULT ('{}'), + enabled BOOLEAN NOT NULL DEFAULT TRUE, + status VARCHAR(32) NOT NULL DEFAULT 'LOADED', + error_message TEXT, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_plugin_name ON mate_plugin (name); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V70__cron_job_dedup_safety.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V70__cron_job_dedup_safety.sql new file mode 100644 index 000000000..ee3cc2b94 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V70__cron_job_dedup_safety.sql @@ -0,0 +1,16 @@ +-- Issue #50 follow-up: see h2/V70 for rationale. MySQL doesn't allow +-- DELETE with a subquery scanning the same table directly, so use +-- the LEFT JOIN + IS NULL pattern. + +-- Step 1: physically purge any deleted=1 rows. +DELETE FROM mate_cron_job WHERE deleted = 1; + +-- Step 2: idempotent re-dedup against active rows only. +DELETE FROM mate_cron_job +WHERE deleted = 0 + AND id NOT IN ( + SELECT MIN(id) + FROM mate_cron_job + WHERE deleted = 0 + GROUP BY workspace_id, agent_id, name + ); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V71__hunyuan_3d_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V71__hunyuan_3d_provider.sql new file mode 100644 index 000000000..29d4d660e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V71__hunyuan_3d_provider.sql @@ -0,0 +1,23 @@ +-- V71: Register Tencent Hunyuan 3D provider for ai3d service. +-- See h2/V71 for full rationale. + +INSERT INTO mate_model_provider ( + provider_id, name, api_key_prefix, chat_model, api_key, base_url, + generate_kwargs, is_custom, is_local, support_model_discovery, + support_connection_check, freeze_url, require_api_key, auth_type, + create_time, update_time +) VALUES ('hunyuan-3d', '腾讯混元 3D', 'AKID', 'NotApplicable', '', 'https://ai3d.tencentcloudapi.com', '{"service":"ai3d","version":"2025-05-13","region":"ap-guangzhou"}', FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, 'tc3_hmac_sha256', NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET + name = EXCLUDED.name, + api_key_prefix = EXCLUDED.api_key_prefix, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + is_custom = EXCLUDED.is_custom, + is_local = EXCLUDED.is_local, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + auth_type = EXCLUDED.auth_type, + update_time = NOW(); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V72__hunyuan_3d_model_config.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V72__hunyuan_3d_model_config.sql new file mode 100644 index 000000000..735706a48 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V72__hunyuan_3d_model_config.sql @@ -0,0 +1,19 @@ +-- V72: Register Tencent Hunyuan 3D model variants. See h2/V72 for full rationale. + +INSERT INTO mate_model_config ( + id, name, provider, model_name, description, + temperature, max_tokens, top_p, builtin, enabled, is_default, + model_type, create_time, update_time, deleted +) VALUES + (1000000500, 'HY-3D-3.1', 'hunyuan-3d', 'HY-3D-3.1', '腾讯混元 3D 3.1 — 最高精度,支持 PBR / 多视角 / Geometry 白模等专业参数', NULL, NULL, NULL, TRUE, TRUE, TRUE, 'model3d', NOW(), NOW(), 0), + (1000000501, 'HY-3D-3.0', 'hunyuan-3d', 'HY-3D-3.0', '腾讯混元 3D 3.0 — 老一代 Pro 模型,与 3.1 共享 SubmitHunyuanTo3DProJob 调用', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'model3d', NOW(), NOW(), 0), + (1000000502, 'HY-3D-Express', 'hunyuan-3d', 'HY-3D-Express', '腾讯混元 3D 极速版 — 走 SubmitHunyuanTo3DRapidJob 接口,速度最快但仅支持 Prompt / ImageUrl', NULL, NULL, NULL, TRUE, TRUE, FALSE, 'model3d', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + is_default = EXCLUDED.is_default, + model_type = EXCLUDED.model_type, + update_time = NOW(); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V73__add_skill_secret.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V73__add_skill_secret.sql new file mode 100644 index 000000000..0340b3de1 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V73__add_skill_secret.sql @@ -0,0 +1,23 @@ +-- V73: Per-skill encrypted secret store (RFC-091 settings bridge). +-- Holds AES-encrypted values for skill manifest fields with type=secret — +-- e.g. AIRTABLE_API_KEY for the airtable-base wizard template. The +-- runtime layer (SkillScriptExecutionService) decrypts and injects these +-- as environment variables when spawning skill subprocesses, so SKILL.md +-- bodies can reference them as plain $AIRTABLE_API_KEY without baking +-- the secret into the manifest. +-- +-- MySQL doesn't support ADD COLUMN IF NOT EXISTS; we get idempotency +-- via CREATE TABLE IF NOT EXISTS plus an INFORMATION_SCHEMA guard for +-- index creation. + +CREATE TABLE IF NOT EXISTS mate_skill_secret ( + id BIGINT NOT NULL PRIMARY KEY, + skill_id BIGINT NOT NULL, + secret_key VARCHAR(128) NOT NULL, + encrypted_value TEXT NOT NULL, + create_time TIMESTAMP NOT NULL, + update_time TIMESTAMP NOT NULL, + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_skill_secret_key ON mate_skill_secret (skill_id, secret_key); +CREATE INDEX IF NOT EXISTS idx_skill_secret_skill ON mate_skill_secret (skill_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V74__shedlock_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V74__shedlock_table.sql new file mode 100644 index 000000000..bc262e84e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V74__shedlock_table.sql @@ -0,0 +1,16 @@ +-- V74: ShedLock distributed-lock table (RFC-03 Lane G2). +-- Backs the LockProvider configured in vip.mate.cron.config.ShedLockConfig +-- so a multi-instance deployment fires each cron job exactly once per tick. +-- Single-node setups are unaffected (acquiring the lock from the only node +-- always succeeds trivially). +-- +-- Schema is the canonical ShedLock layout from +-- https://github.com/lukas-krecan/ShedLock#configure-lockprovider. + +CREATE TABLE IF NOT EXISTS shedlock ( + name VARCHAR(64) NOT NULL, + lock_until TIMESTAMP(3) NOT NULL, + locked_at TIMESTAMP(3) NOT NULL, + locked_by VARCHAR(255) NOT NULL, + PRIMARY KEY (name) +); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V75__model_config_request_timeout.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V75__model_config_request_timeout.sql new file mode 100644 index 000000000..6a460660c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V75__model_config_request_timeout.sql @@ -0,0 +1,16 @@ +-- V75: Per-model HTTP read timeout (RFC-03 Lane B1). +-- Lets thinking models (o1-pro, claude opus extended-thinking, qwen3-max +-- with deep reasoning) override the default 180s read timeout when their +-- p99 legitimately exceeds it. Null / zero keeps the existing global +-- default — no behavior change for existing rows. +-- +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_model_config' AND column_name = 'request_timeout_seconds' + ) THEN + ALTER TABLE mate_model_config ADD COLUMN request_timeout_seconds INT DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V76__personal_access_token.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V76__personal_access_token.sql new file mode 100644 index 000000000..f71a7adbc --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V76__personal_access_token.sql @@ -0,0 +1,24 @@ +-- V76: Personal Access Token (RFC-03 Lane I1). +-- Lets headless / CI / SDK callers authenticate without going through +-- the interactive JWT login flow. Tokens are stored as SHA-256 hashes — +-- a DB compromise reveals which user owns which token but never the +-- plaintext value the user sees once at creation time. +-- +-- token_hash is the lookup key (UNIQUE) so the auth filter can do a +-- single indexed query on every authenticated request. + +CREATE TABLE IF NOT EXISTS mate_personal_access_token ( + id BIGINT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + name VARCHAR(64), + token_hash CHAR(64) NOT NULL, + scopes VARCHAR(255), + last_used_at TIMESTAMP NULL, + expires_at TIMESTAMP NULL, + enabled BOOLEAN DEFAULT TRUE, + create_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP , + deleted INT DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_pat_token_hash ON mate_personal_access_token (token_hash); +CREATE INDEX IF NOT EXISTS idx_pat_user_id ON mate_personal_access_token (user_id); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql new file mode 100644 index 000000000..710309480 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql @@ -0,0 +1,41 @@ +-- mate_wiki_relation: persistent cache of page-to-page multi-signal relations. +-- +-- Distinct from mate_wiki_page_citation, which models page-to-chunk citations. +-- A row here represents one directed (or undirected, see notes below) edge in +-- the wiki page graph for a given knowledge base, materializing: +-- * the aggregate relevance score across registered signal strategies +-- (direct link, shared chunk, shared raw, semantic similarity, ...) +-- * a per-signal breakdown for explainability +-- * an optional taxonomy tag (mention / cite / supports / contradicts / +-- extends) populated by the planning stage of the compile pipeline +-- * confidence + evidence snippets sourced from the same compile output +-- * cache invalidation metadata so readers can decide whether to recompute + +CREATE TABLE IF NOT EXISTS mate_wiki_relation ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + page_a_id BIGINT NOT NULL, + page_b_id BIGINT NOT NULL, + + total_score DECIMAL(8, 4), + signals_json TEXT, + + type VARCHAR(32), + + confidence VARCHAR(16), + evidence TEXT, + evidence_raw_id BIGINT, + + source VARCHAR(32), + + computed_at TIMESTAMP(3), + computed_hash VARCHAR(64), + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wr_pair ON mate_wiki_relation (kb_id, page_a_id, page_b_id, deleted); +CREATE INDEX IF NOT EXISTS idx_wr_page_a ON mate_wiki_relation (kb_id, page_a_id, total_score DESC); +CREATE INDEX IF NOT EXISTS idx_wr_kb_score ON mate_wiki_relation (kb_id, total_score DESC); +CREATE INDEX IF NOT EXISTS idx_wr_computed_at ON mate_wiki_relation (computed_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V78__feature_flag.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V78__feature_flag.sql new file mode 100644 index 000000000..d980d495f --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V78__feature_flag.sql @@ -0,0 +1,36 @@ +-- mate_feature_flag: runtime-toggleable feature flag store. +-- +-- Each row defines one named flag with optional KB / user whitelists and +-- a percentage rollout. Reads go through an in-memory cache that refreshes +-- on a 30-second timer (and immediately on admin write); the cache is +-- per-instance so multi-instance deployments converge within one tick. + +CREATE TABLE IF NOT EXISTS mate_feature_flag ( + id BIGSERIAL PRIMARY KEY, + flag_key VARCHAR(128) NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT FALSE, + description VARCHAR(512), + whitelist_kb_ids TEXT, + whitelist_user_ids TEXT, + rollout_percent INT DEFAULT 0, + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_mff_key ON mate_feature_flag (flag_key); +CREATE INDEX IF NOT EXISTS idx_mff_key_flag ON mate_feature_flag (flag_key, enabled, deleted); + +-- Seed wiki feature flags with safe defaults. ON DUPLICATE KEY UPDATE +-- preserves existing operator overrides on re-run. +INSERT INTO mate_feature_flag (flag_key, enabled, description) VALUES + ('wiki.ocr.enabled', FALSE, 'Image OCR / vision-in pipeline for wiki uploads'), + ('wiki.compile.4stage.enabled', FALSE, 'Four-stage knowledge base compilation pipeline'), + ('wiki.compile.cache.enabled', FALSE, 'Prompt cache layer for the wiki compile pipeline'), + ('wiki.confidence.enabled', FALSE, 'Confidence taxonomy on wiki relations and pages'), + ('wiki.hot_cache.enabled', FALSE, 'KB-level recent-activity snapshot injected into agent system prompt'), + ('wiki.graph.insights.enabled', FALSE, 'Wiki graph insights panel (surprising connections, gaps, bridges)'), + ('wiki.graph.adamic_adar.enabled', FALSE, 'Adamic-Adar graph signal (additive to existing four signals)'), + ('wiki.graph.boundary.enabled', FALSE, 'Boundary score for surfacing dangling pages'), + ('wiki.relation.cache.enabled', TRUE, 'Persistent cache for wiki page-to-page relation computation') +ON CONFLICT (flag_key) DO UPDATE SET description = EXCLUDED.description; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V79__wiki_image_caption_cache.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V79__wiki_image_caption_cache.sql new file mode 100644 index 000000000..a6ba1c9a9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V79__wiki_image_caption_cache.sql @@ -0,0 +1,32 @@ +-- mate_wiki_image_caption_cache: SHA-256 keyed image caption store. +-- +-- Cache is shared across all knowledge bases — the same image bytes +-- uploaded twice (in different KBs, by different users, or to the same +-- KB at different times) cost exactly one vision-LLM call total. +-- +-- The cache is content-addressed by raw image bytes; perceptual variations +-- (re-encoded JPEG, slightly cropped) are intentionally treated as misses. +-- A second-tier perceptual hash can be added later if the miss rate +-- becomes a cost concern. + +CREATE TABLE IF NOT EXISTS mate_wiki_image_caption_cache ( + id BIGINT PRIMARY KEY, + image_sha256 CHAR(64) NOT NULL, + + caption TEXT NOT NULL, + visible_text TEXT, + mime_type VARCHAR(64), + + capture_model VARCHAR(128) NOT NULL, + provider_id VARCHAR(64) NOT NULL, + + duration_ms BIGINT, + hit_count BIGINT NOT NULL DEFAULT 0, + + captured_at TIMESTAMP(3) NOT NULL, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_wicc_sha ON mate_wiki_image_caption_cache (image_sha256); +CREATE INDEX IF NOT EXISTS idx_wicc_captured ON mate_wiki_image_caption_cache (captured_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V7__wiki_last_processed_hash.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V7__wiki_last_processed_hash.sql new file mode 100644 index 000000000..ec7050bba --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V7__wiki_last_processed_hash.sql @@ -0,0 +1,12 @@ +-- V7: Add last_processed_hash to mate_wiki_raw_material for skip-if-unchanged optimization +-- RFC-012 Change 5: when reprocessing, skip LLM pipeline if content_hash == last_processed_hash +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_raw_material' AND column_name = 'last_processed_hash' + ) THEN + ALTER TABLE mate_wiki_raw_material ADD COLUMN last_processed_hash VARCHAR(64) DEFAULT NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V80__wiki_raw_material_mime_type.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V80__wiki_raw_material_mime_type.sql new file mode 100644 index 000000000..14948ab21 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V80__wiki_raw_material_mime_type.sql @@ -0,0 +1,8 @@ +-- Adds the MIME type column to mate_wiki_raw_material so the upload pipeline +-- can route uploads to the right downstream extractor. Image source types +-- in particular need the original Content-Type to pick a vision provider +-- and to render previews correctly without re-sniffing the file. +-- +-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_wiki_raw_material ADD COLUMN IF NOT EXISTS mime_type VARCHAR(64); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V81__refresh_bailian_qwen_catalog.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V81__refresh_bailian_qwen_catalog.sql new file mode 100644 index 000000000..4abddefcc --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V81__refresh_bailian_qwen_catalog.sql @@ -0,0 +1,49 @@ +-- Refresh the Bailian (Aliyun Model Studio) Qwen catalog to match the 2026 Q2 +-- model lineup, and remove a non-existent model id that triggered 400 +-- InvalidParameter on the native text-generation endpoint. +-- +-- Why now: +-- * Reported user error: "Bad request, please check input (type=MODEL_NOT_FOUND)" +-- when chatting with the seeded "Qwen3 Plus" entry. +-- * Root cause: model id qwen3-plus does not exist on Bailian. The real +-- balanced Qwen3 series uses dotted minor versions (qwen3.5-plus, +-- qwen3.6-plus); plain qwen3-plus was never published. DashScope's +-- native endpoint rejects it with [InvalidParameter], which our +-- failover classifier maps to MODEL_NOT_FOUND and evicts the entire +-- dashscope provider from the pool. +-- +-- Two-part fix: +-- 1. Soft-delete the bogus qwen3-plus row (id 1000000172). +-- 2. Seed three latest-snapshot trackers on the dashscope native provider +-- (qwen-plus-latest / qwen-max-latest / qwen-turbo-latest) and six +-- newer Qwen3 series models on the bailian-team OpenAI-compat provider +-- where they are documented to work (vision + flash + coder + 3.6 snapshot). + +-- 1. Soft-delete the bogus model id (idempotent). +UPDATE mate_model_config + SET deleted = 1, enabled = FALSE, update_time = NOW() + WHERE id = 1000000172 + AND model_name = 'qwen3-plus'; + +-- 2a. Latest-snapshot trackers on dashscope native (text-generation endpoint). +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000174, 'Qwen Plus (latest)', 'dashscope', 'qwen-plus-latest', 'Latest stable snapshot of Qwen Plus — auto-updates as Bailian rolls new releases.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000175, 'Qwen Max (latest)', 'dashscope', 'qwen-max-latest', 'Latest stable snapshot of Qwen Max — strongest reasoning capability.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000176, 'Qwen Turbo (latest)', 'dashscope', 'qwen-turbo-latest', 'Latest stable snapshot of Qwen Turbo — low latency, high frequency.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; + +-- 2b. Newer Qwen3 series on the bailian-team OpenAI-compat token plan endpoint. +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted) +VALUES +(1000000407, 'Qwen 3.5 Plus', 'bailian-team', 'qwen3.5-plus', 'Bailian Token Plan — Qwen3.5 balanced flagship, hybrid thinking, 128K context.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000408, 'Qwen 3.5 Flash', 'bailian-team', 'qwen3.5-flash', 'Bailian Token Plan — Qwen3.5 fast variant, lower latency for high-frequency calls.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000409, 'Qwen3 VL Plus', 'bailian-team', 'qwen3-vl-plus', 'Bailian Token Plan — Qwen3 vision-language flagship, image + video understanding.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000410, 'Qwen3 VL Flash', 'bailian-team', 'qwen3-vl-flash', 'Bailian Token Plan — Qwen3 vision-language fast variant for high-throughput vision.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000411, 'Qwen3 Coder Plus', 'bailian-team', 'qwen3-coder-plus', 'Bailian Token Plan — Qwen3 coding flagship, agentic code editing and tool use.', 0.2, 8192, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000412, 'Qwen 3.6 Plus 2026-04-02', 'bailian-team', 'qwen3.6-plus-2026-04-02', 'Bailian Token Plan — pinned snapshot of Qwen 3.6 Plus released 2026-04-02.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000413, 'Qwen 3.6 Max (preview)', 'bailian-team', 'qwen3.6-max-preview', 'Bailian Token Plan — Qwen3.6 Max preview, strongest reasoning in the 3.6 lineup.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000414, 'Qwen 3.6 Flash', 'bailian-team', 'qwen3.6-flash', 'Bailian Token Plan — Qwen3.6 fast variant, hybrid thinking mode default-on.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000415, 'Qwen 3.6 Flash 2026-04-16', 'bailian-team', 'qwen3.6-flash-2026-04-16', 'Bailian Token Plan — pinned snapshot of Qwen 3.6 Flash released 2026-04-16.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0), +(1000000416, 'Qwen 3.5 Omni Plus', 'bailian-team', 'qwen3.5-omni-plus', 'Bailian Token Plan — Qwen3.5 omni-modal plus, text + vision + audio in/out.', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, 'chat', NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V82__wiki_hot_cache.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V82__wiki_hot_cache.sql new file mode 100644 index 000000000..ed692871e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V82__wiki_hot_cache.sql @@ -0,0 +1,30 @@ +-- mate_wiki_hot_cache: KB-level rolling snapshot of "what happened recently" +-- in this knowledge base, injected into the agent system prompt so the model +-- doesn't have to wiki_search the obvious every turn. +-- +-- One row per KB (uk_whc_kb). Body is markdown rendered from a four-section +-- structure (Last Updated / Key Recent Facts / Recent Changes / Active Threads), +-- regenerated by an LLM call after compile/page/conversation events; this +-- migration only provisions storage. Consumers and updater land in later PRs. + +CREATE TABLE IF NOT EXISTS mate_wiki_hot_cache ( + id BIGINT PRIMARY KEY, + kb_id BIGINT NOT NULL, + + content TEXT, + content_hash CHAR(64), + + last_updated TIMESTAMP(3), + update_reason VARCHAR(32), + + rebuild_count BIGINT NOT NULL DEFAULT 0, + last_rebuild_started_at TIMESTAMP(3), + last_rebuild_duration_ms BIGINT, + last_rebuild_error VARCHAR(512), + + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_whc_kb ON mate_wiki_hot_cache (kb_id, deleted); +CREATE INDEX IF NOT EXISTS idx_whc_kb ON mate_wiki_hot_cache (kb_id, deleted); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V84__widen_raw_material_mime_type.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V84__widen_raw_material_mime_type.sql new file mode 100644 index 000000000..f2a5ae8c8 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V84__widen_raw_material_mime_type.sql @@ -0,0 +1,7 @@ +-- V80 created mate_wiki_raw_material.mime_type as VARCHAR(64). Office Open +-- XML Content-Types blow that on the very first upload — docx is 71 chars, +-- pptx is 73, xlsx is 65 — so any user uploading a Word / Excel / PowerPoint +-- file hits "Data truncation: Data too long for column 'mime_type'". +-- Widen to 255 (covers any registered RFC 6838 type with parameters). + +ALTER TABLE mate_wiki_raw_material ALTER COLUMN mime_type TYPE VARCHAR(255); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V85__ckjia_mcp_seed.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V85__ckjia_mcp_seed.sql new file mode 100644 index 000000000..d160ac5a9 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V85__ckjia_mcp_seed.sql @@ -0,0 +1,28 @@ +-- Seed ckjia-shopping MCP server config (disabled by default). +-- The localhost URL below is a dev/test placeholder only. Production admins +-- must replace it in Settings > MCP Connections with the official CKJIA SaaS +-- domain or their private CKJIA deployment URL before enabling the server, +-- then configure CKJIA_MCP_KEY for authorization. +-- +-- Column name is url (not endpoint) per McpServerEntity. +-- headers_json uses ${CKJIA_MCP_KEY} placeholder so the plaintext API key +-- never lands in the database (parseHeaders expands env vars at request time). + +-- mate_mcp_server.id is BIGINT NOT NULL PRIMARY KEY without DB-side auto-increment; +-- production uses MyBatis Plus Snowflake at insert time, but Flyway bypasses that. +-- Following existing seed convention (1000000901=filesystem, 1000000902=github), +-- ckjia-shopping takes 1000000903. +INSERT INTO mate_mcp_server ( + id, name, transport, url, headers_json, enabled, description, + connect_timeout_seconds, read_timeout_seconds, builtin, create_time, update_time, deleted +) +SELECT 1000000903, + 'ckjia-shopping', + 'sse', + 'http://localhost:8085/sse', + '{"Authorization": "Bearer ${CKJIA_MCP_KEY}"}', + FALSE, + 'CKJIA price comparison MCP server. Disabled by default; replace the dev/test localhost URL with the production CKJIA domain before enabling.', + 30, 30, TRUE, + NOW(), NOW(), 0 +WHERE NOT EXISTS (SELECT 1 FROM mate_mcp_server WHERE name = 'ckjia-shopping'); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V87__skill_usage_stat.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V87__skill_usage_stat.sql new file mode 100644 index 000000000..c19941b3a --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V87__skill_usage_stat.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS mate_skill_usage_stat ( + id BIGINT PRIMARY KEY, + skill_name VARCHAR(128) NOT NULL, + skill_id BIGINT, + agent_id BIGINT NOT NULL DEFAULT 0, + conversation_id VARCHAR(128) NOT NULL DEFAULT '', + load_count BIGINT NOT NULL DEFAULT 0, + last_loaded_at TIMESTAMP(3), + last_file_path VARCHAR(512), + last_token_estimate INT, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_skill_usage_scope ON mate_skill_usage_stat (skill_name, agent_id, conversation_id); +CREATE INDEX IF NOT EXISTS idx_skill_usage_agent_recent ON mate_skill_usage_stat (agent_id, last_loaded_at); +CREATE INDEX IF NOT EXISTS idx_skill_usage_name_recent ON mate_skill_usage_stat (skill_name, last_loaded_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V88__convert_default_agent_icons_to_pixelart.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V88__convert_default_agent_icons_to_pixelart.sql new file mode 100644 index 000000000..490a0d4ff --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V88__convert_default_agent_icons_to_pixelart.sql @@ -0,0 +1,8 @@ +-- Convert the three default seed agents from emoji to pixelarticons icons. +-- The card UI now treats pi: as an inline pixel-art SVG, so the +-- defaults need to match. Guarded with the original emoji so a user who +-- already customised the icon keeps their choice; non-seed (user-created) +-- agents are intentionally untouched. +UPDATE mate_agent SET icon = 'pi:robot-face-happy' WHERE id = 1000000001 AND icon = '🤖'; +UPDATE mate_agent SET icon = 'pi:clipboard-note' WHERE id = 1000000002 AND icon = '📋'; +UPDATE mate_agent SET icon = 'pi:cpu' WHERE id = 1000000003 AND icon = '🔄'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V89__chatgpt_oauth_model_discovery.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V89__chatgpt_oauth_model_discovery.sql new file mode 100644 index 000000000..587afcbea --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V89__chatgpt_oauth_model_discovery.sql @@ -0,0 +1,16 @@ +-- Enable model discovery on the ChatGPT OAuth provider so the catalog can be +-- pulled live from chatgpt.com/backend-api/codex/models, and seed the GPT-5.5 +-- flagship row alongside the existing GPT-5.4 / GPT-5.4 Mini entries. The +-- ON DUPLICATE KEY UPDATE clause keeps the migration idempotent. + +UPDATE mate_model_provider + SET support_model_discovery = TRUE, + update_time = CURRENT_TIMESTAMP + WHERE provider_id = 'openai-chatgpt' + AND support_model_discovery <> TRUE; + +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES (1000000252, 'GPT-5.5', 'openai-chatgpt', 'gpt-5.5', 'ChatGPT Plus/Pro flagship model', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + description = EXCLUDED.description, + update_time = NOW(); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V8__wiki_raw_progress.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V8__wiki_raw_progress.sql new file mode 100644 index 000000000..5c68c8246 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V8__wiki_raw_progress.sql @@ -0,0 +1,33 @@ +-- V8: wiki raw material two-phase digest progress fields, for UI progress bar +-- RFC-012 M2 v2 UI follow-up: expose per-raw progress (current phase + pages done / total planned) +-- so the frontend can render a determinate progress bar instead of an opaque "处理中" badge. +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_raw_material' AND column_name = 'progress_phase' + ) THEN + ALTER TABLE mate_wiki_raw_material ADD COLUMN progress_phase VARCHAR(32) DEFAULT NULL; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_raw_material' AND column_name = 'progress_total' + ) THEN + ALTER TABLE mate_wiki_raw_material ADD COLUMN progress_total INT DEFAULT 0; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_raw_material' AND column_name = 'progress_done' + ) THEN + ALTER TABLE mate_wiki_raw_material ADD COLUMN progress_done INT DEFAULT 0; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V90__zhipu_coding_plan_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V90__zhipu_coding_plan_provider.sql new file mode 100644 index 000000000..acdfb8715 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V90__zhipu_coding_plan_provider.sql @@ -0,0 +1,85 @@ +-- V90: register coding-plan subscription endpoints as separate providers +-- with their own pre-seeded model catalogs. See the H2 copy for full +-- background. Covers: zhipu-cn-codingplan, zhipu-intl-codingplan, and +-- aliyun-codingplan-intl. + +-- -- Zhipu Coding Plan (China) ---------------------------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('zhipu-cn-codingplan', 'Zhipu Coding Plan (BigModel)', '', 'OpenAIChatModel', '', 'https://open.bigmodel.cn/api/coding/paas/v4', '{"completionsPath":"/chat/completions"}', FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Zhipu Coding Plan (International) --------------------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('zhipu-intl-codingplan', 'Zhipu Coding Plan (Z.AI)', '', 'OpenAIChatModel', '', 'https://api.z.ai/api/coding/paas/v4', '{"completionsPath":"/chat/completions"}', FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Aliyun DashScope Coding Plan (International) --------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('aliyun-codingplan-intl', 'Aliyun Coding Plan (International)', 'sk-sp', 'OpenAIChatModel', '', 'https://coding-intl.dashscope.aliyuncs.com/v1', '{}', FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + api_key_prefix = EXCLUDED.api_key_prefix, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Zhipu Coding Plan model catalog ---------------------------------------- +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000000230, 'GLM-5 Coding', 'zhipu-cn-codingplan', 'glm-5', '智谱编码套餐 — GLM-5 旗舰', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000231, 'GLM-5.1 Coding', 'zhipu-cn-codingplan', 'glm-5.1', '智谱编码套餐 — GLM-5.1 最新旗舰', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000232, 'GLM-5-Turbo Coding', 'zhipu-cn-codingplan', 'glm-5-turbo', '智谱编码套餐 — GLM-5 高速版', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000233, 'GLM-4.7 Coding', 'zhipu-cn-codingplan', 'glm-4.7', '智谱编码套餐 — GLM-4.7', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000234, 'GLM-5 Coding', 'zhipu-intl-codingplan', 'glm-5', 'Zhipu Coding Plan — GLM-5 flagship', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000235, 'GLM-5.1 Coding', 'zhipu-intl-codingplan', 'glm-5.1', 'Zhipu Coding Plan — GLM-5.1 latest flagship', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000236, 'GLM-5-Turbo Coding', 'zhipu-intl-codingplan', 'glm-5-turbo', 'Zhipu Coding Plan — GLM-5 fast variant', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000237, 'GLM-4.7 Coding', 'zhipu-intl-codingplan', 'glm-4.7', 'Zhipu Coding Plan — GLM-4.7', 0.2, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; + +-- -- Aliyun Coding Plan model catalog (cn backfill + intl mirror) ---------- +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + -- Backfill: qwen3.6-plus on aliyun-codingplan (cn). + (1000000162, 'Qwen3.6 Plus', 'aliyun-codingplan', 'qwen3.6-plus', '阿里云编码套餐 — Qwen3.6 Plus 旗舰', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + -- Aliyun Coding Plan (International) catalog. + (1000000241, 'Qwen3.6 Plus', 'aliyun-codingplan-intl', 'qwen3.6-plus', 'Aliyun Coding Plan (Intl) — Qwen3.6 Plus flagship', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000242, 'Qwen3.5 Plus', 'aliyun-codingplan-intl', 'qwen3.5-plus', 'Aliyun Coding Plan (Intl) — Qwen3.5 balanced', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000243, 'GLM-5', 'aliyun-codingplan-intl', 'glm-5', 'Aliyun Coding Plan (Intl) — GLM-5 hosted on DashScope', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000244, 'GLM-4.7', 'aliyun-codingplan-intl', 'glm-4.7', 'Aliyun Coding Plan (Intl) — GLM-4.7 hosted on DashScope', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000245, 'MiniMax M2.5', 'aliyun-codingplan-intl', 'MiniMax-M2.5', 'Aliyun Coding Plan (Intl) — MiniMax M2.5 hosted on DashScope', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000246, 'Kimi K2.5', 'aliyun-codingplan-intl', 'kimi-k2.5', 'Aliyun Coding Plan (Intl) — Kimi K2.5 hosted on DashScope', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000247, 'Qwen3 Max 2026-01-23', 'aliyun-codingplan-intl', 'qwen3-max-2026-01-23', 'Aliyun Coding Plan (Intl) — Qwen3 Max pinned snapshot', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000248, 'Qwen3 Coder Next', 'aliyun-codingplan-intl', 'qwen3-coder-next', 'Aliyun Coding Plan (Intl) — Qwen3 Coder Next, agentic coding', 0.2, 8192, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000249, 'Qwen3 Coder Plus', 'aliyun-codingplan-intl', 'qwen3-coder-plus', 'Aliyun Coding Plan (Intl) — Qwen3 Coder Plus, agentic coding', 0.2, 8192, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V91__widen_message_and_skill_content.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V91__widen_message_and_skill_content.sql new file mode 100644 index 000000000..2a3bf7f93 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V91__widen_message_and_skill_content.sql @@ -0,0 +1,5 @@ +-- V91: Widen mate_message.content / content_parts and mate_skill.skill_content +-- from TEXT (64KB) to TEXT (16MB). +-- +-- In KingbaseES/PostgreSQL, TEXT is already unlimited (up to 1GB), +-- so this migration is a no-op. Keep for Flyway version compatibility. diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql new file mode 100644 index 000000000..98867cc51 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql @@ -0,0 +1,31 @@ +-- V92: Persist each MCP server's discovered tool list as a per-row JSON +-- snapshot so the agent edit picker can render the tools even when the +-- upstream server is briefly disconnected, and so the per-tool atomic +-- binding flow has a stable place to resolve raw tool names from the +-- prefixed callback name. +-- +-- MySQL doesn't support ADD COLUMN IF NOT EXISTS natively (5.7 and most +-- 8.0 deployments), so guard each ALTER with an INFORMATION_SCHEMA lookup +-- + PREPARE/EXECUTE so re-runs become no-ops instead of failing the +-- migration. Flyway's repair-on-startup compensates for any partial +-- failure. + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_mcp_server' AND column_name = 'tools_cache_json' + ) THEN + ALTER TABLE mate_mcp_server ADD COLUMN tools_cache_json TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_mcp_server' AND column_name = 'tools_cache_updated_at' + ) THEN + ALTER TABLE mate_mcp_server ADD COLUMN tools_cache_updated_at TIMESTAMP NULL; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V93__xiaomi_mimo_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V93__xiaomi_mimo_provider.sql new file mode 100644 index 000000000..db12b6ad1 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V93__xiaomi_mimo_provider.sql @@ -0,0 +1,31 @@ +-- V93: register Xiaomi MiMo as an OpenAI-compatible provider with a +-- pre-seeded model catalog. See the H2 copy for full background. + +-- -- Provider -------------------------------------------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('xiaomi-mimo', 'Xiaomi MiMo', '', 'OpenAIChatModel', '', 'https://api.xiaomimimo.com/v1', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + api_key_prefix = EXCLUDED.api_key_prefix, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Model catalog --------------------------------------------------------- +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000001200, 'MiMo V2.5 Pro', 'xiaomi-mimo', 'mimo-v2.5-pro', 'Xiaomi MiMo V2.5 Pro — latest flagship reasoning + coding model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000001201, 'MiMo V2.5', 'xiaomi-mimo', 'mimo-v2.5', 'Xiaomi MiMo V2.5 — balanced model in the V2.5 family', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000001202, 'MiMo V2 Pro', 'xiaomi-mimo', 'mimo-v2-pro', 'Xiaomi MiMo V2 Pro — 1M token context window flagship', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000001203, 'MiMo V2 Omni', 'xiaomi-mimo', 'mimo-v2-omni', 'Xiaomi MiMo V2 Omni — multimodal variant supporting text, vision, audio', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000001204, 'MiMo V2 Flash', 'xiaomi-mimo', 'mimo-v2-flash', 'Xiaomi MiMo V2 Flash — fast, low-latency variant with 262K context', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V94__register_office_render_tools.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V94__register_office_render_tools.sql new file mode 100644 index 000000000..938c47793 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V94__register_office_render_tools.sql @@ -0,0 +1,16 @@ +-- V94: Register XlsxRenderTool / PptxRenderTool / PdfRenderTool as built-in tools. +-- These mirror DocxRenderTool (V31) so agents can bind them through the tool picker +-- and so the AvailableToolService surfaces them in the UI. +-- Idempotent: ON DUPLICATE KEY UPDATE keeps rows in sync if they already exist. + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000020, 'XlsxRenderTool', 'XLSX Render', 'Render Markdown directly into a .xlsx workbook and return a one-time download link. In-process Apache POI; each # heading becomes a sheet, pipe tables become rows, numeric cells auto-detected.', 'builtin', 'xlsxRenderTool', '📊', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, update_time=EXCLUDED.update_time; + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000021, 'PptxRenderTool', 'PPTX Render', 'Render Marp-style Markdown directly into a .pptx deck and return a one-time download link. In-process Apache POI; --- separates slides, # / ## titles, - bullets, .', 'builtin', 'pptxRenderTool', '🎞️', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, update_time=EXCLUDED.update_time; + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000022, 'PdfRenderTool', 'PDF Render', 'Render Markdown into a final-form .pdf and return a one-time download link. Two backends (LibreOffice subprocess preferred, OpenPDF + Flying Saucer fallback); supports YAML frontmatter for cover / page header / page footer.', 'builtin', 'pdfRenderTool', '📄', TRUE, TRUE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, update_time=EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V95__wiki_raw_material_cancel.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V95__wiki_raw_material_cancel.sql new file mode 100644 index 000000000..4e2a1854c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V95__wiki_raw_material_cancel.sql @@ -0,0 +1,15 @@ +-- V95: cancellation flag for in-progress wiki raw material processing. +-- Lets the user request a stop on a long-running PDF analysis (e.g. when +-- the embedding model has run out of credits) without having to delete +-- the raw material. The processing pipeline checks the flag at its +-- existing abort checkpoints and bails out with a 'cancelled' status. +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_wiki_raw_material' AND column_name = 'cancel_requested' + ) THEN + ALTER TABLE mate_wiki_raw_material ADD COLUMN cancel_requested BOOLEAN NOT NULL DEFAULT FALSE; + END IF; +END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql new file mode 100644 index 000000000..2df7e7ebc --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql @@ -0,0 +1,154 @@ +-- V96: Foundational schema for the workflow runtime. +-- Eight tables establish workflow identity (workflow + immutable revisions), +-- run state (run + per-step rows + durable pause rows for await_approval), +-- payload URI storage with inline / filesystem fallback, and trigger +-- definitions paired with a dedup-window table for envelope-based event +-- governance. CREATE TABLE IF NOT EXISTS is itself idempotent on MySQL. + +-- 1. Stable workflow identity + draft (1:1 with workflow row). +CREATE TABLE IF NOT EXISTS mate_workflow ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + name VARCHAR(128) NOT NULL, + description VARCHAR(1024), + enabled BOOLEAN NOT NULL DEFAULT TRUE, + draft_json TEXT, + draft_schema_version VARCHAR(8), + draft_updated_by BIGINT, + draft_updated_at TIMESTAMP(3), + latest_revision_id BIGINT, + created_by BIGINT, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted INT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_workspace_name ON mate_workflow (workspace_id, name, deleted); + +-- 2. Immutable published revisions; integer revision is monotonic per workflow. +CREATE TABLE IF NOT EXISTS mate_workflow_revision ( + id BIGINT NOT NULL PRIMARY KEY, + workflow_id BIGINT NOT NULL, + revision INT NOT NULL, + graph_json TEXT NOT NULL, + schema_version VARCHAR(8) NOT NULL, + published_note VARCHAR(512), + published_by BIGINT, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_revision ON mate_workflow_revision (workflow_id, revision); + +-- 3. Workflow run instance; payload bodies live behind URIs in mate_workflow_payload. +CREATE TABLE IF NOT EXISTS mate_workflow_run ( + id BIGINT NOT NULL PRIMARY KEY, + workflow_id BIGINT NOT NULL, + revision_id BIGINT NOT NULL, + workspace_id BIGINT NOT NULL, + state VARCHAR(16) NOT NULL, + triggered_by VARCHAR(32), + triggered_meta TEXT, + initial_input_ref VARCHAR(256), + final_output_ref VARCHAR(256), + error_message VARCHAR(2048), + started_at TIMESTAMP(3), + completed_at TIMESTAMP(3), + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_workflow_run_started ON mate_workflow_run (workflow_id, started_at); + +-- 4. Per-step run row; iteration_index reserved for fan_out (and future loop). +CREATE TABLE IF NOT EXISTS mate_workflow_run_step ( + id BIGINT NOT NULL PRIMARY KEY, + run_id BIGINT NOT NULL, + step_index INT NOT NULL, + iteration_index INT, + step_name VARCHAR(128), + agent_id BIGINT, + state VARCHAR(16), + input_ref VARCHAR(256), + output_ref VARCHAR(256), + output_summary VARCHAR(512), + output_content_type VARCHAR(64), + error_message VARCHAR(2048), + duration_ms BIGINT, + token_input INT, + token_output INT, + started_at TIMESTAMP(3), + completed_at TIMESTAMP(3) +); +CREATE INDEX IF NOT EXISTS idx_workflow_run_step ON mate_workflow_run_step (run_id, step_index, iteration_index); + +-- 5. Durable pause rows so await_approval can resume across restarts. +CREATE TABLE IF NOT EXISTS mate_workflow_run_pause ( + id BIGINT NOT NULL PRIMARY KEY, + run_id BIGINT NOT NULL, + step_id BIGINT NOT NULL, + pause_kind VARCHAR(32) NOT NULL, + pause_token VARCHAR(128) NOT NULL, + external_approval_id BIGINT, + paused_at TIMESTAMP(3) NOT NULL, + resume_deadline TIMESTAMP(3), + resume_payload_ref VARCHAR(256), + resumed_at TIMESTAMP(3), + resume_outcome VARCHAR(32) +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_pause_run_step ON mate_workflow_run_pause (run_id, step_id); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_pause_token ON mate_workflow_run_pause (pause_token); +CREATE INDEX IF NOT EXISTS idx_workflow_pause_external_approval ON mate_workflow_run_pause (external_approval_id); +CREATE INDEX IF NOT EXISTS idx_workflow_pause_open_deadline ON mate_workflow_run_pause (resumed_at, resume_deadline); + +-- 6. Payload URI storage. Inline BYTEA for < 256KB; storage_kind=fs/s3/oss +-- carries the external object key in storage_ref. +CREATE TABLE IF NOT EXISTS mate_workflow_payload ( + id BIGINT NOT NULL PRIMARY KEY, + payload_uri VARCHAR(256) NOT NULL, + workspace_id BIGINT NOT NULL, + content_bytes BYTEA, + storage_kind VARCHAR(16) NOT NULL, + storage_ref VARCHAR(512), + content_type VARCHAR(64), + sha256 CHAR(64), + size_bytes BIGINT, + created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_payload_uri ON mate_workflow_payload (payload_uri); +CREATE INDEX IF NOT EXISTS idx_workflow_payload_workspace_created ON mate_workflow_payload (workspace_id, created_at); + +-- 7. Trigger definitions. pattern_version is a lamport counter that fire +-- callbacks compare against on every fire to detect that another instance +-- has updated the cron expression and self-cancel the local schedule. +CREATE TABLE IF NOT EXISTS mate_trigger ( + id BIGINT NOT NULL PRIMARY KEY, + workspace_id BIGINT NOT NULL, + name VARCHAR(128), + pattern_type VARCHAR(32) NOT NULL, + pattern_json TEXT NOT NULL, + target_type VARCHAR(16) NOT NULL, + target_id BIGINT NOT NULL, + payload_template TEXT, + rate_limit_per_min INT NOT NULL DEFAULT 60, + dedup_window_secs INT NOT NULL DEFAULT 60, + bot_self_filter BOOLEAN NOT NULL DEFAULT TRUE, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + fire_count BIGINT NOT NULL DEFAULT 0, + max_fires BIGINT NOT NULL DEFAULT 0, + last_fired_at TIMESTAMP(3), + pattern_version BIGINT NOT NULL DEFAULT 1, + create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP , + deleted INT NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_trigger_workspace_enabled ON mate_trigger (workspace_id, enabled, deleted); +CREATE INDEX IF NOT EXISTS idx_trigger_target ON mate_trigger (target_type, target_id); + +-- 8. Event dedup window. dedup_key is envelope.eventId, falling back to +-- sourceHash when the upstream channel did not provide a stable id. +CREATE TABLE IF NOT EXISTS mate_trigger_event ( + id BIGINT NOT NULL PRIMARY KEY, + trigger_id BIGINT NOT NULL, + dedup_key VARCHAR(128) NOT NULL, + received_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP(3) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS uk_trigger_dedup ON mate_trigger_event (trigger_id, dedup_key); +CREATE INDEX IF NOT EXISTS idx_trigger_event_expires ON mate_trigger_event (expires_at); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V97__workflow_purge_tombstones.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V97__workflow_purge_tombstones.sql new file mode 100644 index 000000000..8b3a9125e --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V97__workflow_purge_tombstones.sql @@ -0,0 +1,9 @@ +-- See the matching H2 file for context. The workflow / trigger entities +-- moved off @TableLogic to align with the project's hard-delete convention; +-- this migration drops any tombstones the old soft-delete path persisted so +-- list endpoints don't expose them after the annotation-driven filter is +-- removed. + +DELETE FROM mate_workflow WHERE deleted <> 0; +DELETE FROM mate_workflow_run WHERE deleted <> 0; +DELETE FROM mate_trigger WHERE deleted <> 0; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V98__trigger_last_error.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V98__trigger_last_error.sql new file mode 100644 index 000000000..dd98b00d2 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V98__trigger_last_error.sql @@ -0,0 +1,5 @@ +-- See the H2 file for context. KingbaseES (PostgreSQL) supports +-- ADD COLUMN IF NOT EXISTS natively. + +ALTER TABLE mate_trigger ADD COLUMN IF NOT EXISTS last_error VARCHAR(2048); +ALTER TABLE mate_trigger ADD COLUMN IF NOT EXISTS last_dispatched_at TIMESTAMP NULL; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V99__dashscope_compat_provider.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V99__dashscope_compat_provider.sql new file mode 100644 index 000000000..be99a04e2 --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V99__dashscope_compat_provider.sql @@ -0,0 +1,39 @@ +-- V99: register a DashScope OpenAI-compatible provider entry alongside the +-- existing native dashscope provider, plus the dot-versioned Qwen families +-- (qwen3.5-*, qwen3.6-*) that only ship on compatible-mode/v1. +-- +-- See the H2 copy for full background. The MySQL copy uses INSERT ... ON +-- DUPLICATE KEY UPDATE; the api_key column is intentionally omitted from the +-- update list so existing deployments that have already configured a key keep +-- it (this only matters if a future migration re-applies a similar block; +-- Flyway runs each version once today). + +-- -- Provider -------------------------------------------------------------- +INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time) +VALUES ('dashscope-compat', 'DashScope (兼容模式)', 'sk-', 'OpenAIChatModel', '', 'https://dashscope.aliyuncs.com/compatible-mode/v1', '{}', FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, NOW(), NOW()) +ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name, + api_key_prefix = EXCLUDED.api_key_prefix, + chat_model = EXCLUDED.chat_model, + base_url = EXCLUDED.base_url, + generate_kwargs = EXCLUDED.generate_kwargs, + support_model_discovery = EXCLUDED.support_model_discovery, + support_connection_check = EXCLUDED.support_connection_check, + freeze_url = EXCLUDED.freeze_url, + require_api_key = EXCLUDED.require_api_key, + update_time = EXCLUDED.update_time; + +-- -- Model catalog --------------------------------------------------------- +-- Only seed the variants that are publicly callable on compatible-mode. The +-- -max / -vl-max variants exist in the marketplace but return 404 for general +-- accounts; users with whitelist access can add them via Settings → Models. +INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted) +VALUES + (1000000601, 'Qwen3.6 Plus', 'dashscope-compat', 'qwen3.6-plus', '通义千问 3.6 Plus 旗舰,平衡推理与速度(兼容模式专属)', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000603, 'Qwen3.5 Plus', 'dashscope-compat', 'qwen3.5-plus', '通义千问 3.5 Plus(兼容模式专属)', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0), + (1000000605, 'Qwen3 VL Plus', 'dashscope-compat', 'qwen3-vl-plus', '通义千问 3 视觉理解 Plus,支持图像、视频输入(兼容模式专属)', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0) +ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, + model_name = EXCLUDED.model_name, + description = EXCLUDED.description, + builtin = EXCLUDED.builtin, + enabled = EXCLUDED.enabled, + update_time = EXCLUDED.update_time; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V9__usage_cache_tokens.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V9__usage_cache_tokens.sql new file mode 100644 index 000000000..ae616828c --- /dev/null +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V9__usage_cache_tokens.sql @@ -0,0 +1,24 @@ +-- V9: Track Anthropic prompt cache token usage +-- RFC-014 Change 4: per-call cache_creation_input_tokens / cache_read_input_tokens +-- accumulated daily so the dashboard can show cache hit rate and cost savings. +-- (was originally numbered V8 but collided with V8__wiki_raw_progress.sql; renumbered to V9.) +-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead. +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_usage_daily' AND column_name = 'cache_read_tokens' + ) THEN + ALTER TABLE mate_usage_daily ADD COLUMN cache_read_tokens BIGINT DEFAULT 0; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'mate_usage_daily' AND column_name = 'cache_write_tokens' + ) THEN + ALTER TABLE mate_usage_daily ADD COLUMN cache_write_tokens BIGINT DEFAULT 0; + END IF; +END $$; From a3a85e7f6f2d6d6f1795b66a197bee765c6386e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Fri, 26 Jun 2026 15:03:08 +0800 Subject: [PATCH 2/7] feat(db): upgrade high-frequency JSON columns from TEXT to JSONB on PostgreSQL The forked postgresql migration tree stored all JSON payloads as TEXT (config_json / headers_json / settings_json / delivery_config / ... ~40 columns), inherited from the kingbase/h2/mysql dialect. On PostgreSQL these are better modelled as JSONB: writes are validated as well-formed JSON at the database boundary, and the door is open to GIN indexing / JSON queries later. Changes (postgresql tree only; kingbase/h2/mysql untouched): - Convert 46 columns across 18 migrations from TEXT to JSONB. Columns were whitelisted by name + verified against their entities and seed inserts; free text columns (description, source_code, encrypted_value, ...) keep TEXT. - Six NOT NULL columns get a JSONB default ('{}'::jsonb, or '[]'::jsonb for the array-typed steps_json) so a missing write can't break the NOT NULL contract. - Three columns stay TEXT on purpose: mate_tool.params_schema and mate_wiki_transformation.output_schema (arbitrary JSON-Schema text) and mate_message.metadata (frequently truncated half-structured blob). - Rewrite V53's connection_mode recovery to JSONB-native ops. The TEXT version used TRIM/POSITION/SUBSTRING/CONCAT/REPLACE on config_json, which are invalid on jsonb; the JSONB merge operator `||` does the same idempotent key-set in one step while preserving the other keys. - Add stringtype=unspecified to the datasource and flyway JDBC URLs so the driver sends String-bound JSON values as `unknown`, letting PostgreSQL coerce them into jsonb (covers both the JacksonTypeHandler path and plain String columns). Without it, setString -> jsonb fails at runtime. Verified against a real PostgreSQL 16 container: all 153 migrations apply cleanly, converted columns report data_type=jsonb (params_schema / metadata stay text), invalid JSON is rejected by a jsonb column, and the V53 merge sets connection_mode=websocket while preserving sibling keys. --- .../main/resources/application-postgres.yml | 14 ++++++-- .../migration/postgresql/V10__hook_system.sql | 2 +- .../migration/postgresql/V120__agent_goal.sql | 2 +- .../V134__wiki_page_type_profile.sql | 6 ++-- .../V135__wiki_layered_knowledge.sql | 4 +-- .../V136__wiki_pipeline_runtime.sql | 14 ++++---- .../postgresql/V148__wiki_entity.sql | 2 +- .../postgresql/V1__baseline_schema.sql | 34 +++++++++---------- .../postgresql/V24__wiki_processing_job.sql | 4 +-- .../postgresql/V2__workspace_base_path.sql | 2 +- .../V53__feishu_connection_mode_recover.sql | 30 ++++++---------- .../postgresql/V58__cron_channel_binding.sql | 2 +- .../postgresql/V63__channel_identity_json.sql | 2 +- .../postgresql/V67__add_skill_manifest.sql | 2 +- .../postgresql/V68__add_acp_endpoints.sql | 4 +-- .../migration/postgresql/V6__plugin_table.sql | 2 +- .../postgresql/V77__wiki_relation_table.sql | 2 +- .../V92__mcp_server_tools_cache.sql | 2 +- .../postgresql/V96__workflow_foundations.sql | 6 ++-- 19 files changed, 68 insertions(+), 68 deletions(-) diff --git a/mateclaw-server/src/main/resources/application-postgres.yml b/mateclaw-server/src/main/resources/application-postgres.yml index c69a33467..1f2c78f2e 100644 --- a/mateclaw-server/src/main/resources/application-postgres.yml +++ b/mateclaw-server/src/main/resources/application-postgres.yml @@ -9,7 +9,17 @@ spring: # connectTimeout=10 — TCP connect timeout (s), avoids OS-level stalls # socketTimeout=30 — socket read timeout (s), prevents dead connections hanging forever # loginTimeout=10 — database login timeout (s) - url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10 + # + # stringtype=unspecified — REQUIRED for the JSONB columns in the postgresql + # migration tree. MyBatis / the JDBC driver bind JSON column values as + # java.lang.String via setString, which PostgreSQL types as `varchar`. Writing + # a varchar into a `jsonb` column otherwise fails with "column is of type jsonb + # but expression is of type character varying". With stringtype=unspecified the + # driver sends String params as `unknown`, letting PostgreSQL coerce them to + # jsonb (and validate the JSON) at write time. This covers both the + # JacksonTypeHandler path (CronJobEntity.deliveryConfig) and the plain String + # JSON columns (config_json / settings_json / headers_json / ...). + url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10&stringtype=unspecified driver-class-name: org.postgresql.Driver username: ${DB_USERNAME:postgres} password: ${DB_PASSWORD:postgres} @@ -36,7 +46,7 @@ spring: # two trees are byte-identical, so switching locations is transparent for # existing PostgreSQL deployments: Flyway tracks version + checksum, not # the classpath location, and identical scripts keep identical checksums. - url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10 + url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10&stringtype=unspecified user: ${DB_USERNAME:postgres} password: ${DB_PASSWORD:postgres} locations: diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql index 1b2780e47..4fdd3fb99 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V10__hook_system.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS mate_hook ( event_type VARCHAR(64) NOT NULL, match_expression TEXT, action_kind VARCHAR(32) NOT NULL, - action_config TEXT NOT NULL, + action_config JSONB NOT NULL DEFAULT '{}'::jsonb, rate_limit_per_min INT DEFAULT 60, timeout_ms INT DEFAULT 3000, source VARCHAR(16) DEFAULT 'db', diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql index 8d855685b..dc6c75852 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V120__agent_goal.sql @@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS mate_agent_goal_event ( goal_id BIGINT NOT NULL, event_type VARCHAR(32) NOT NULL, message_id BIGINT NULL, - detail_json TEXT NULL, + detail_json JSONB NULL, create_time TIMESTAMP(3) NOT NULL, PRIMARY KEY (id) ); diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql index 845f9510a..da34db9a6 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V134__wiki_page_type_profile.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS mate_wiki_page_type_profile ( kb_id BIGINT NOT NULL, name VARCHAR(128) NOT NULL, version INT NOT NULL DEFAULT 1, - config_json TEXT NOT NULL, + config_json JSONB NOT NULL DEFAULT '{}'::jsonb, -- SMALLINT (not BOOLEAN): WikiPageTypeProfileEntity.enabled is Integer (1/0). -- Vanilla PostgreSQL cannot map a BOOLEAN into a JDBC int. The generated -- column below compares enabled = 1 accordingly. Do not switch to BOOLEAN. @@ -36,7 +36,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_json' ) THEN - ALTER TABLE mate_wiki_page ADD COLUMN metadata_json TEXT; + ALTER TABLE mate_wiki_page ADD COLUMN metadata_json JSONB; END IF; END $$; @@ -56,7 +56,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_validation_json' ) THEN - ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_json TEXT; + ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_json JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql index fe6bdd70d..05f60850d 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V135__wiki_layered_knowledge.sql @@ -18,7 +18,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_wiki_page' AND column_name = 'depends_on_json' ) THEN - ALTER TABLE mate_wiki_page ADD COLUMN depends_on_json TEXT; + ALTER TABLE mate_wiki_page ADD COLUMN depends_on_json JSONB; END IF; END $$; @@ -40,7 +40,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_wiki_page' AND column_name = 'stale_reason_json' ) THEN - ALTER TABLE mate_wiki_page ADD COLUMN stale_reason_json TEXT; + ALTER TABLE mate_wiki_page ADD COLUMN stale_reason_json JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql index d022e0948..718d62856 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V136__wiki_pipeline_runtime.sql @@ -7,8 +7,8 @@ CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_definition ( name VARCHAR(128) NOT NULL, owner_agent_id BIGINT NOT NULL, trigger_type VARCHAR(32) NOT NULL, - trigger_config_json TEXT, - steps_json TEXT NOT NULL, + trigger_config_json JSONB, + steps_json JSONB NOT NULL DEFAULT '[]'::jsonb, dedup_window_seconds INT NOT NULL DEFAULT 0, -- SMALLINT (not BOOLEAN): WikiPipelineDefinitionEntity.enabled is Integer (1/0). -- Vanilla PostgreSQL cannot map a BOOLEAN into a JDBC int. Do not switch to BOOLEAN. @@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_run ( trigger_type VARCHAR(32) NOT NULL, trigger_subject VARCHAR(128) NOT NULL, trigger_bucket VARCHAR(64) NOT NULL, - trigger_payload_json TEXT, - input_json TEXT, - output_json TEXT, + trigger_payload_json JSONB, + input_json JSONB, + output_json JSONB, error_message VARCHAR(2048), started_at TIMESTAMP(3), finished_at TIMESTAMP(3), @@ -46,8 +46,8 @@ CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_step_run ( step_id VARCHAR(128) NOT NULL, executor VARCHAR(32) NOT NULL, status VARCHAR(16) NOT NULL, - input_json TEXT, - output_json TEXT, + input_json JSONB, + output_json JSONB, error_message VARCHAR(2048), started_at TIMESTAMP(3), finished_at TIMESTAMP(3), diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql index 2f88fff7b..20ab5350f 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V148__wiki_entity.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS mate_wiki_entity ( normalized_key VARCHAR(256) NOT NULL, type VARCHAR(32) NOT NULL, - aliases_json TEXT, + aliases_json JSONB, description TEXT, salience DECIMAL(5, 4), mention_count INT NOT NULL DEFAULT 0, diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql index 910fea030..f9b22e4c4 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V1__baseline_schema.sql @@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS mate_model_provider ( chat_model VARCHAR(64), api_key VARCHAR(512), base_url VARCHAR(512), - generate_kwargs TEXT, + generate_kwargs JSONB, is_custom BOOLEAN NOT NULL DEFAULT FALSE, is_local BOOLEAN NOT NULL DEFAULT FALSE, support_model_discovery BOOLEAN NOT NULL DEFAULT FALSE, @@ -98,7 +98,7 @@ CREATE TABLE IF NOT EXISTS mate_skill ( icon VARCHAR(256), version VARCHAR(32), author VARCHAR(64), - config_json TEXT, + config_json JSONB, source_code TEXT, skill_content TEXT, enabled BOOLEAN NOT NULL DEFAULT TRUE, @@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS mate_channel ( channel_type VARCHAR(32) NOT NULL, agent_id BIGINT, bot_prefix VARCHAR(64), - config_json TEXT, + config_json JSONB, enabled BOOLEAN NOT NULL DEFAULT FALSE, description VARCHAR(256), workspace_id BIGINT NOT NULL DEFAULT 1, @@ -277,10 +277,10 @@ CREATE TABLE IF NOT EXISTS mate_mcp_server ( description TEXT, transport VARCHAR(32) NOT NULL DEFAULT 'stdio', url VARCHAR(512), - headers_json TEXT, + headers_json JSONB, command VARCHAR(512), - args_json TEXT, - env_json TEXT, + args_json JSONB, + env_json JSONB, cwd VARCHAR(512), enabled BOOLEAN NOT NULL DEFAULT TRUE, connect_timeout_seconds INT NOT NULL DEFAULT 30, @@ -314,7 +314,7 @@ CREATE TABLE IF NOT EXISTS mate_tool_approval ( tool_call_hash VARCHAR(64), sibling_tool_calls TEXT, summary TEXT, - findings_json TEXT, + findings_json JSONB, max_severity VARCHAR(16), status VARCHAR(32) NOT NULL DEFAULT 'PENDING', resolved_by VARCHAR(64), @@ -356,10 +356,10 @@ CREATE TABLE IF NOT EXISTS mate_tool_guard_config ( id BIGINT NOT NULL PRIMARY KEY, enabled BOOLEAN NOT NULL DEFAULT TRUE, guard_scope VARCHAR(32) NOT NULL DEFAULT 'all', - guarded_tools_json TEXT, - denied_tools_json TEXT, + guarded_tools_json JSONB, + denied_tools_json JSONB, file_guard_enabled BOOLEAN NOT NULL DEFAULT TRUE, - sensitive_paths_json TEXT, + sensitive_paths_json JSONB, audit_enabled BOOLEAN NOT NULL DEFAULT TRUE, audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO', audit_retention_days INT NOT NULL DEFAULT 90, @@ -375,10 +375,10 @@ CREATE TABLE IF NOT EXISTS mate_tool_guard_audit_log ( user_id VARCHAR(64), channel_type VARCHAR(32), tool_name VARCHAR(128) NOT NULL, - tool_params_json TEXT, + tool_params_json JSONB, decision VARCHAR(16) NOT NULL, max_severity VARCHAR(16), - findings_json TEXT, + findings_json JSONB, pending_id VARCHAR(32), replay_payload_hash VARCHAR(64), create_time TIMESTAMP NOT NULL, @@ -421,8 +421,8 @@ CREATE TABLE IF NOT EXISTS mate_async_task ( message_id BIGINT, provider_name VARCHAR(64), provider_task_id VARCHAR(128), - request_json TEXT, - result_json TEXT, + request_json JSONB, + result_json JSONB, error_message VARCHAR(512), progress INT DEFAULT 0, created_by VARCHAR(64), @@ -524,7 +524,7 @@ CREATE TABLE IF NOT EXISTS mate_workspace ( slug VARCHAR(64) NOT NULL, description VARCHAR(256), owner_id BIGINT, - settings_json TEXT, + settings_json JSONB, base_path VARCHAR(512), create_time TIMESTAMP NOT NULL, update_time TIMESTAMP NOT NULL, @@ -554,7 +554,7 @@ CREATE TABLE IF NOT EXISTS mate_agent_skill ( agent_id BIGINT NOT NULL, skill_id BIGINT NOT NULL, enabled BOOLEAN NOT NULL DEFAULT TRUE, - config_json TEXT, + config_json JSONB, create_time TIMESTAMP NOT NULL, update_time TIMESTAMP NOT NULL, deleted INT NOT NULL DEFAULT 0 @@ -617,7 +617,7 @@ CREATE TABLE IF NOT EXISTS mate_audit_event ( resource_type VARCHAR(64) NOT NULL, resource_id VARCHAR(128), resource_name VARCHAR(256), - detail_json TEXT, + detail_json JSONB, ip_address VARCHAR(64), user_agent VARCHAR(256), create_time TIMESTAMP NOT NULL diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql index 8c49747ab..e460882ec 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V24__wiki_processing_job.sql @@ -7,13 +7,13 @@ CREATE TABLE IF NOT EXISTS mate_wiki_processing_job ( status VARCHAR(32) NOT NULL DEFAULT 'queued', primary_model_id BIGINT, current_model_id BIGINT, - fallback_chain_json TEXT, + fallback_chain_json JSONB, retry_count INT NOT NULL DEFAULT 0, max_retries INT NOT NULL DEFAULT 3, error_code VARCHAR(64), error_message TEXT, resume_from_stage VARCHAR(64), - meta_json TEXT, + meta_json JSONB, started_at TIMESTAMP(3), finished_at TIMESTAMP(3), create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql index 785c104e9..be4be2c66 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V2__workspace_base_path.sql @@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS mate_workspace ( slug VARCHAR(64) NOT NULL, description VARCHAR(256), owner_id BIGINT, - settings_json TEXT, + settings_json JSONB, base_path VARCHAR(512), create_time TIMESTAMP NOT NULL, update_time TIMESTAMP NOT NULL, diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql index 81c675800..4a28f1676 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V53__feishu_connection_mode_recover.sql @@ -1,24 +1,14 @@ -- V53: Recover from V52's silent no-op. --- See h2/V53 for the full rationale. Same surgery, Kingbase-flavored. --- Idempotent: rows already at "websocket" are skipped by the WHERE clause. - +-- See h2/V53 for the full rationale. Same intent, PostgreSQL JSONB flavour. +-- +-- The kingbase/h2/mysql trees treat config_json as TEXT and do string surgery +-- (TRIM / POSITION / SUBSTRING / CONCAT / REPLACE) to inject or rewrite the +-- connection_mode key. On PostgreSQL config_json is JSONB (this tree only), so +-- those text functions don't apply. The JSONB merge operator `||` sets the key +-- in one step (right side wins), COALESCE handles NULL, and the WHERE clause is +-- idempotent: rows already at "websocket" are skipped. UPDATE mate_channel -SET config_json = CASE - WHEN config_json IS NULL OR TRIM(config_json) = '' OR TRIM(config_json) = '{}' THEN - '{"connection_mode":"websocket"}' - WHEN POSITION('"connection_mode"' IN config_json) = 0 THEN - CONCAT('{"connection_mode":"websocket",', SUBSTRING(config_json, 2)) - ELSE - REPLACE( - REPLACE(config_json, - '"connection_mode": "webhook"', '"connection_mode": "websocket"'), - '"connection_mode":"webhook"', '"connection_mode":"websocket"' - ) - END +SET config_json = COALESCE(config_json, '{}'::jsonb) || '{"connection_mode":"websocket"}'::jsonb WHERE channel_type = 'feishu' AND deleted = 0 - AND ( - config_json IS NULL - OR (POSITION('"connection_mode": "websocket"' IN config_json) = 0 - AND POSITION('"connection_mode":"websocket"' IN config_json) = 0) - ); + AND config_json->>'connection_mode' IS DISTINCT FROM 'websocket'; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql index 5380c9b4f..a23a0e519 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V58__cron_channel_binding.sql @@ -16,7 +16,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_cron_job' AND column_name = 'delivery_config' ) THEN - ALTER TABLE mate_cron_job ADD COLUMN delivery_config TEXT; + ALTER TABLE mate_cron_job ADD COLUMN delivery_config JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql index 4e9b5274d..fa079d131 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V63__channel_identity_json.sql @@ -9,6 +9,6 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_channel' AND column_name = 'identity_json' ) THEN - ALTER TABLE mate_channel ADD COLUMN identity_json TEXT; + ALTER TABLE mate_channel ADD COLUMN identity_json JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql index 315bef7c2..176457050 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V67__add_skill_manifest.sql @@ -9,6 +9,6 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_skill' AND column_name = 'manifest_json' ) THEN - ALTER TABLE mate_skill ADD COLUMN manifest_json TEXT; + ALTER TABLE mate_skill ADD COLUMN manifest_json JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql index adde830ff..643073787 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V68__add_acp_endpoints.sql @@ -7,8 +7,8 @@ CREATE TABLE IF NOT EXISTS mate_acp_endpoint ( display_name VARCHAR(128), description TEXT, command VARCHAR(256) NOT NULL, - args_json TEXT, - env_json TEXT, + args_json JSONB, + env_json JSONB, tool_parse_mode VARCHAR(32) NOT NULL DEFAULT 'call_title', builtin BOOLEAN NOT NULL DEFAULT FALSE, trusted BOOLEAN NOT NULL DEFAULT TRUE, diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql index 7e1d3068a..6152ed7ca 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V6__plugin_table.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS mate_plugin ( author VARCHAR(128), entrypoint VARCHAR(256) NOT NULL, jar_path VARCHAR(512), - config_json TEXT NOT NULL DEFAULT ('{}'), + config_json JSONB NOT NULL DEFAULT '{}'::jsonb, enabled BOOLEAN NOT NULL DEFAULT TRUE, status VARCHAR(32) NOT NULL DEFAULT 'LOADED', error_message TEXT, diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql index 710309480..7589de61b 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V77__wiki_relation_table.sql @@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS mate_wiki_relation ( page_b_id BIGINT NOT NULL, total_score DECIMAL(8, 4), - signals_json TEXT, + signals_json JSONB, type VARCHAR(32), diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql index 98867cc51..851fdd810 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V92__mcp_server_tools_cache.sql @@ -16,7 +16,7 @@ BEGIN SELECT 1 FROM information_schema.columns WHERE table_name = 'mate_mcp_server' AND column_name = 'tools_cache_json' ) THEN - ALTER TABLE mate_mcp_server ADD COLUMN tools_cache_json TEXT; + ALTER TABLE mate_mcp_server ADD COLUMN tools_cache_json JSONB; END IF; END $$; diff --git a/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql b/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql index 2df7e7ebc..165c757a2 100644 --- a/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql +++ b/mateclaw-server/src/main/resources/db/migration/postgresql/V96__workflow_foundations.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS mate_workflow ( name VARCHAR(128) NOT NULL, description VARCHAR(1024), enabled BOOLEAN NOT NULL DEFAULT TRUE, - draft_json TEXT, + draft_json JSONB, draft_schema_version VARCHAR(8), draft_updated_by BIGINT, draft_updated_at TIMESTAMP(3), @@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS mate_workflow_revision ( id BIGINT NOT NULL PRIMARY KEY, workflow_id BIGINT NOT NULL, revision INT NOT NULL, - graph_json TEXT NOT NULL, + graph_json JSONB NOT NULL DEFAULT '{}'::jsonb, schema_version VARCHAR(8) NOT NULL, published_note VARCHAR(512), published_by BIGINT, @@ -122,7 +122,7 @@ CREATE TABLE IF NOT EXISTS mate_trigger ( workspace_id BIGINT NOT NULL, name VARCHAR(128), pattern_type VARCHAR(32) NOT NULL, - pattern_json TEXT NOT NULL, + pattern_json JSONB NOT NULL DEFAULT '{}'::jsonb, target_type VARCHAR(16) NOT NULL, target_id BIGINT NOT NULL, payload_template TEXT, From 0df0b4e7c9550be3443a800369964f1c4d19267f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Fri, 26 Jun 2026 15:13:14 +0800 Subject: [PATCH 3/7] test(db): add Testcontainers PostgreSQL integration tests for the postgresql tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The postgresql migration tree (JSONB columns, JSONB-native V53) can only be exercised on a real PostgreSQL server — H2/MySQL/Kingbase profiles never touch it, so nothing in CI proved it actually applies. Add two Testcontainers tests against postgres:16-alpine: - PostgresE2EBaseTest: shared base that points the datasource + Flyway at a throwaway PG container with the postgresql tree, postgre_sql dialect, the mateclaw schema (init script), and currentSchema/stringtype URL params (mirrors application-postgres.yml). @Testcontainers(disabledWithoutDocker) so a normal `mvn test` skips — not fails — where no Docker daemon exists. - PostgresMigrationSmokeTest: asserts all 150+ migrations apply with no failed flyway_schema_history rows, and that the upgraded columns are physically jsonb while the excluded ones (params_schema, message.metadata) stay text. - CronJobDeliveryConfigPgTest: drives the JacksonTypeHandler path end-to-end (CronJobEntity.deliveryConfig insert -> select round-trip), confirms the column is jsonb and queryable via ->> on the server, and that a jsonb column rejects malformed JSON. testcontainers junit-jupiter + postgresql deps added at test scope (versions managed by Spring Boot's testcontainers-bom). Verified: both classes green (4 tests) against postgres:16-alpine. This also exercises the DatabaseBootstrapRunner seed path (data-kingbase-zh.sql is the PostgreSQL-family seed), which the migration tree alone doesn't cover. --- mateclaw-server/pom.xml | 16 ++++ .../support/CronJobDeliveryConfigPgTest.java | 94 +++++++++++++++++++ .../vip/mate/support/PostgresE2EBaseTest.java | 94 +++++++++++++++++++ .../support/PostgresMigrationSmokeTest.java | 78 +++++++++++++++ .../db/test/init-mateclaw-schema.sql | 4 + 5 files changed, 286 insertions(+) create mode 100644 mateclaw-server/src/test/java/vip/mate/support/CronJobDeliveryConfigPgTest.java create mode 100644 mateclaw-server/src/test/java/vip/mate/support/PostgresE2EBaseTest.java create mode 100644 mateclaw-server/src/test/java/vip/mate/support/PostgresMigrationSmokeTest.java create mode 100644 mateclaw-server/src/test/resources/db/test/init-mateclaw-schema.sql diff --git a/mateclaw-server/pom.xml b/mateclaw-server/pom.xml index e04bb74fd..92965bd5a 100644 --- a/mateclaw-server/pom.xml +++ b/mateclaw-server/pom.xml @@ -382,6 +382,22 @@ test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + test + +