[general]
name=QGIS Agent
qgisMinimumVersion=3.22
qgisMaximumVersion=4.99
description=LLM-powered intelligent assistant for QGIS. Natural language control of QGIS tools, plus an optional local MCP server so external AI agents (Claude Desktop, Cursor, etc.) can drive QGIS.
version=2.4.11
author=bunkmr
email=bunkr@qq.com
about=QGIS Agent is an AI-powered assistant that runs inside QGIS desktop. It allows you to control QGIS through natural language conversations, including: viewing project info, adding/removing layers, executing Processing algorithms, running PyQGIS code, setting layer labels, rendering maps, and more.

    Features:
    - Natural language interface for QGIS operations
    - 20 built-in QGIS tools driven by natural language (PyQGIS / Processing)
    - Bundled reference catalog: 679 Processing algorithm/tool entries, one TOML per algorithm (tool_docs/)
    - RAG-enhanced API documentation retrieval
    - Cookbook self-evolution mechanism
    - Long-term memory support
    - Multiple LLM providers (DeepSeek, Mimo, GLM, Gemini, etc.)
    - Safety: PyQGIS AST static scan, confirmation for dangerous ops, untrusted data sanitization
    - Skills system: load/run user or bundled skill recipes (run_skill tool)
    - Workflow recording & replay: record a QGIS operation sequence and replay it later
    - Proactive clarification: agent asks when intent is ambiguous before acting
    - Task graph: decompose complex goals into a trackable plan
    - Optional MCP server: expose the same tools to external AI agents over the Model Context Protocol

    MCP server security model (disabled by default; you start it explicitly from the settings page):
    - listens on 127.0.0.1 only; binding to any other address is not supported
    - requires an access token; requests without a valid token are rejected
    - privileged tools (execute_pyqgis, execute_processing, remove_layer, load_project,
      save_project, run_skill) are hidden from external agents unless you enable them,
      and each execution still requires an on-screen confirmation
    - port and token are recorded in ~/.qgis_agent/mcp_session.json (mode 0600) so the
      bundled stdio MCP server can discover them; the file is deleted when the service stops

    External dependencies (install via pip before use):
    langchain_core, langchain_openai, langchain_deepseek, httpx
    Optional (only for the "browser-compatible TLS" option in settings; not required by default):
    curl_cffi  # sends API requests with a browser TLS stack, for gateways that interrupt non-browser clients
    The bundled MCP server (mcp_server/) requires no third-party packages.

tracker=https://github.com/bunkmr/qgis-agent/issues
repository=https://github.com/bunkmr/qgis-agent
tags=ai,agent,llm,qgis,assistant,automation,deepseek,openai,人工智能,自然语言处理,中文,空间分析,GIS
homepage=https://github.com/bunkmr/qgis-agent
category=Analysis
icon=icon.png
experimental=False
deprecated=False
hasProcessingProvider=no
server=no
changelog=
    v2.4.11 更新内容：
    【改进】把「逐要素批量出图」补进助手的系统提示能力清单。上一版新增了该工具，但引导只写在工具说明里；助手建立「我有哪些能力」的认知主要看系统提示的能力清单，因此面对「对图层逐个要素出图」这类需求时，第一反应仍可能是自己手写 PyQGIS 打印布局代码，并因此踩到已被移除的旧 API。现在能力清单直接列出该工具（含标准图纸尺寸说明），并在重要规则里写明：这类需求直接用该工具，不要手写打印布局代码。另新增 3 例回归测试守住这两处引导文案。

    v2.4.10 更新内容：
    【重要修复】向插件提「对某个图层逐个要素出图，要指北针和比例尺」这类需求时，助手会回答「工具调用轮次已达上限，无法完成」。两个层面的原因都已修掉。其一，工具调用轮次上限原为 10 轮，对多步制图任务（探查图层 → 建布局 → 逐要素设定范围 → 导出）明显不够，助手常在几步探查加一次失败重试后就被强制收尾；现已放宽到 30 轮（单轮内仍可并行多个工具调用，不会失控）。其二，工具集里原本没有出图专用工具，助手只能手写 PyQGIS 打印布局代码，而它习惯写的 QgsLayoutItemNorthArrow 在当前 QGIS 版本里早已被移除，必然报错、反复重写，把轮次全部烧光。
    【新功能】新增「逐要素批量出图」工具：指定图层与输出目录，一次调用即把该图层每个要素单独出一张图，可自动配置指北针与比例尺，支持标准图纸尺寸（A0 至 A4、B0 至 B4，可选纵向或横向）。出图时会自动按要素范围缩放到合适视野并留出边距、按页面纵横比校正、置白底；比例尺按每个要素的实际地图比例尺逐个重算；图片默认以要素属性值命名（如「支局01.png」）。输出目录不存在会自动创建；若目标文件已存在，会先请求确认再覆盖，不会静默覆盖。单次默认最多导出 100 个要素（可调整），避免一次导出上千张卡住界面。
    【改进】受限执行环境里预置了打印布局相关类，减少因缺少导入而失败。
    【修复】逐要素出图时比例尺曾显示为「0」，真因是比例尺长度依赖地图当前比例尺，而原先在设定要素范围之前就固定了；现在每个要素设定范围后重新计算。
    【测试】新增 24 例回归守卫：文件名净化、跨平台定位指北针 SVG、源码中不得再出现已被移除的 API、工具注册与参数校验、轮次上限不得退回个位数；并做了反向验证（故意引入这两个问题，守卫确实会失败）。

    v2.4.9 更新内容：
    【重要修复】searchpyqgisapi 工具连续报「API 文档检索失败: no such module: fts5」。真因是 QGIS 自带的 SQLite 没有编译 FTS5 全文检索模块（实测部分平台的 QGIS 就是这样），而文档库初始化时会无条件创建 FTS5 虚拟表，缺模块就直接让检索器崩溃 —— 这是环境缺组件，与调用参数无关，所以自动重试多少次都会以同样方式失败。现在初始化时先探测 FTS5 是否可用：可用则照常使用；不可用则跳过建索引、记一条警告日志，检索自动降级为 LIKE 模糊匹配，功能保持可用（相关性排序与速度略差）。API 文档、Cookbook、Processing 算法目录（679 条）三条检索路径全部完成对应降级，任何一处都不会再因缺 FTS5 抛错。
    【修复】连带修了一个既有 bug：API 文档主表与 FTS 索引是「外部内容表」关系，单条写入从不重建索引，导致全文检索一直查不到新写入的文档。现在单条写入后同步重建索引。
    【测试】新增 6 例降级守卫：模拟无 FTS5 环境下初始化不崩、写入后模糊检索可查回、Cookbook 与算法目录检索可用、清空索引不抛错；有 FTS5 的环境做对照验证全文检索命中。真机双版本（Qt5 + Qt6）验收：安装副本 50/50，降级链路（679 条算法目录入库、精确检索、写入后查回）双版本通过。

    v2.4.8 更新内容：
    【重要修复】插件仓库的安全扫描把上一版判为 Critical 并**阻断了版本发布**。Bandit 共报出 33 项，其中 5 项属于**不可跳过的 Critical**：execute_pyqgis 的 exec（B102 —— 此前的 nosec 注释写在**上一行**，而 bandit 只认**同一行**的注释，等于从未生效）、三处「变量名里含 token 字样」触发的 B105 误报（ENV_TOKEN 常量的值是环境变量**名字**本身；self._token 是空占位）、以及一处 B107（__init__ 的 token 默认参数）。现在 exec 的快照注释移到同一行并写明理由，三处 B105 逐条标注为误报，token 默认参数改为 None（与内部「token 或空串」的既有写法完全等价）。本地复扫：33 项降到 **0 项，Critical 为 0**。
    【重要修复】文件权限：包内 45 个 .py 带可执行位（工作区在同步卷上是 0755，打包时会原样写进 ZIP），触发插件仓库的 File Permissions 检查。打包脚本现在**显式设置 ZIP 内的权限位**（普通文件 0644），不再继承工作区的权限。
    【改进】21 处 try / except / pass 改写为 contextlib.suppress（等价的惯用写法），3 处 try / except / continue 补上调试日志（避免静默吞异常）；两处 subprocess 调用补上说明注释，明确是列表传参、不经 shell。
    【修复】代码质量：删掉两处函数内重复的 import os（模块顶部已有），把单字符变量名 l 改为 line。插件仓库报出的 3 项 Flake8 问题已全部清零。
    【测试】新增安全扫描回归守卫：exec 的 nosec 必须与 exec 同行、包内 .py 的权限位必须是 0644、被改写的异常处理不得回归。

    v2.4.7 更新内容：
    【重要修复】Agent 明明调用了工具、却给出「待计算」「暂无法显示」这类空回答。真因是 execute_pyqgis 的受限执行环境里**缺少 __import__**：AST 安全检查层明确放行 import qgis，但执行层用了一份不含 __import__ 的内建白名单，结果**任何带 import 的代码都会失败**，只报一行 ImportError: __import__ not found。而系统提示词又写着「已预导入，无需 import」，模型照着写代码照样死。现在注入一个**受控 __import__**，与 AST 层**共用同一份模块白名单**：from qgis.core import ...、import math / json 等都能跑，而 import os / subprocess / shutil 在 AST 层和执行层各被拦一次，安全边界不降级。同时清理了那份从未被引用、且把 __import__ 列为「应移除」的死代码黑名单（正是它的错误意图来源）。
    【重要修复】execute_processing 用 OUTPUT='memory:' 时结果图层不进工程，且只返回一句无用的对象 repr（<QgsVectorLayer: 'output' (memory)>），模型既拿不到图层名、也没法继续处理，只能反复重跑或放弃。现在结果图层会自动加入当前工程，并回报结构化信息（名称 / id / 是否入工程 / 要素数）与可操作的提示，模型可直接用该名称继续调用后续工具。
    【修复】Agent 短暂失败后的重试计数在成功后不归零，导致后续偶发失败就**过早放弃**。现在每次成功都重置。
    【修复】工具调用轮次跑满时的强制总结没有任何「不得虚报」的约束，模型会把「我准备怎么做」写成「已完成」。现在明确要求如实汇报：执行成功的结果与数值直接给出，失败或未执行的部分必须说明，严禁把计划写成已完成的成果，且不得用「待计算」「暂无法显示」等占位词搪塞。
    【改进】系统提示词的导入规范由「已预导入，无需 import」改为**明确的导入白名单说明**，并给出统计类需求的正解范式（遍历要素 + QgsDistanceArea 椭球面积，直接输出 max/min/median 数值）。同时新增两条硬约束：结果必须给出真实数值；统计口径与单位要讲清楚。
    【测试】新增 16 例纯 Python 契约守卫（execute_pyqgis 必须注入 __import__ 且与白名单同源、processing 的 memory 输出必须入工程、重试计数必须在循环内归零、危险模块仍被拒）；并新增真机行为守卫，用**真实的 call_tool → execute_pyqgis** 链路验证（不再用写死结果的工具替身 —— 正是它让这个 bug 一路绿着）。三处修复各做了一次反向验证：删掉任意一处，对应守卫立刻变红且无连带失败。另新增 3 例元数据守卫（metadata.txt 必须能被 configparser 解析 —— 正文里出现裸百分号会让整份元数据解析失败，手工检查发现不了）。单测 421 → 440。

    v2.4.6 更新内容：
    【重要修复】对话区只显示回答，看不到用户自己发出去的内容。真因是「写入约定」与「渲染约定」不一致，而且完全静默：本插件的一问一答**存在同一条 interaction 记录里**（typeMessage 恒为 return，requestText 与 responseText 同行），而重建对话历史的代码只在 typeMessage == "input" 时才渲染用户气泡 —— 那条分支永远不会成立（库里一条 input 记录都没有）。于是**每次重建历史都会把用户气泡丢掉**，而回答一到达就会重建一次，现象恰好就是「我发出去了，但只剩回答」。现在重建历史会像重建 LLM 上下文那样，把同一行的 requestText 一并渲染成用户气泡（processor 早就是这么做的，两处终于一致），并兼容「提问与回答各占一行」的旧形态数据，不会重复渲染。
    【修复】用户消息在发送瞬间与回答到达后的样式不一致（前者是右对齐纯文本、后者是气泡卡片），回答一到就肉眼可见地跳变。现在两处共用同一个气泡生成函数 user_bubble_html，从发送那一刻起就是最终样式。
    【测试】新增 6 例跨文件契约守卫（写进库的类型必须被渲染覆盖；用户气泡只能有一处生成函数）与 17 例真机行为守卫（含用真实数据库里那条报障会话做端到端渲染）。两套守卫都做了反向验证：删掉提问渲染、或把 return 写成 output，守卫会立刻变红并直接指出「写进库的 return 没有被渲染」。单测 415 → 421；真机双版本：历史渲染 17/17、真实会话端到端 4/4、对话 UI 138/138、消息持久化 45/45。

    v2.4.5 更新内容：
    【重要修复】把「复制客户端配置」给出的 JSON 粘进 Claude Desktop / Cursor 后连不上，甚至一启动就多出一个 QGIS 窗口。真因是配置里的 command 直接用了 sys.executable，而 macOS 上 QGIS 的 Python 嵌在 app 里，GUI 进程的 sys.executable 就是 QGIS 的 GUI 主程序（/Applications/QGIS.app/Contents/MacOS/QGIS）——它不是解释器，不读 stdin、不讲 JSON-RPC，被客户端当 stdio 服务拉起来只会再开一个 QGIS 界面然后握手超时。Windows 上 sys.executable 正好是 python.exe，所以这个坑只在 macOS 发作。现在配置生成会实际执行一次校验再落笔：把候选解释器真的跑一遍服务脚本的 --help，退出码为 0 才算通过（只看文件存在不够：macOS 上 QGIS.app/Contents/MacOS/python3.12 确实存在，但脱离 app 直接跑会报 Could not find platform independent libraries）。并优先选「在干净环境下就能启动」的解释器，因为客户端是在自己的环境里拉起 command 的；只在 QGIS 进程环境里能跑的解释器只作兜底且在界面上明确提示。候选一律先过「文件名像 python」这一关，绝不执行看起来不像解释器的可执行文件；一个都找不到时不回吐 sys.executable，而是退化成裸命令名 python3 / python。
    【修复】「测试连通性」按钮也会误启动一个 QGIS 窗口（同一根因：同样用 sys.executable 拉子进程跑 --check）。现在与客户端配置共用同一套解释器解析逻辑，并在自检结果里写明用的是哪个解释器。
    【修复】页签切换回调硬编码下标（模型页 = index 2），新增页签后即指向别的页。改为按控件定位。
    【新增】MCP 服务独立成「MCP」页签，不再挂在「模型」页底部。分组框标题压短为「MCP 服务」（窄面板下过长标题会被裁掉）。「复制客户端配置」的弹窗会说明解释器为何不是 QGIS 的路径；README 新增「外部 Agent 接入」章节。
    【文档】全量清理指向旧路径「模型配置 → MCP 服务」的提示与文档，并写明 macOS 上 QGIS.app/Contents/MacOS/python3.12 不能当 command 的原因。
    【测试】单测 376 → 415（新增 26 例解释器解析 + 13 例页签结构守卫，均含反向验证）；真机双版本验收：新页签与配置生成 63/63（Qt5+Qt6，含用生成的 command 做真实 stdio 握手与 tools/list）、MCP 安装回归 50/50、对话 UI 137/137、dock 尺寸 29/29 与 27/27、浏览器兼容 TLS 15/15、令牌显示 21/21。

    v2.4.4 更新内容：
    【重要修复】模型开始调用工具后，用户自己发出去的消息会从对话区消失。真因是聊天区有两套写法：一套是「整体重写」（思考流、工具状态、错误卡片都会走它），另一套是直接 append（用户消息、各类提示行）。而 append 的内容没有同步进内部缓冲，于是任何一次整体重写都会把它整段抹掉——所以现象恰好是「一开始能看到，模型一调工具就不见了」。现在所有写入路径统一同步缓冲，并顺带修掉两个同源问题：清空对话后内容会复活、反复重写导致缓冲膨胀。实测：用户消息、工具状态、思考块共存且互不冲掉。
    【重要修复】切换模型或温度后再对话，报 TypeError: 'method' object is not connected（conversation.py 的 response_ready.disconnect），且新对话的回复永远不显示。真因是切换模型时 processor 对象会被整体替换，而旧 worker 的信号连接仍留在旧对象上；旧 worker 收尾时触发旧对象的信号，回调却拿着新的 self.processor 去断开连接——既报错，又把新对象上刚建立的连接误断开。现在用信号发送者定位真正的对象，过期回调只清理自己、绝不渲染过期结果，也不碰新连接。
    【修复】「停止」按钮永远提示「没有正在进行的生成」，中间的「停止中…」状态形同虚设。真因是停止时会把「空闲」标志提前置真，而判断总在置真之后执行。
    【修复】会话收尾改为幂等：一轮请求只认第一次结果。工作线程先发错误再发完成时，两个事件都已排队，此前第二个到达就会抛错并让界面卡在发送中。
    【测试】新增会话信号与 processor 生命周期回归守卫 13 例（含切换模型重建 processor 的真实现场）；测试基础设施修正 sys.path 顺序（项目根目录里有同名的 qgis_agent.py，曾使 import qgis_agent 命中文件而非包）；单测 359 → 376 全绿。

    v2.4.3 更新内容：
    【重要修复】本地大模型每次对话都报「模型服务内部错误 HTTP 500」，而同一个模型在别的客户端完全正常。根因是插件自己发出的消息格式有问题：Query Tuning 的改写结果此前被作为第二条 SystemMessage 追加在系统提示词之后，而 Qwen3 系 chat template 只允许第一条是 system，遇到第二条直接报 Jinja Exception: System message must be at the beginning。现已改为并入同一条系统消息，维持「有且仅有一条系统消息、且位于首位」的不变式。
    【修复】新增 template 错误分类（排在 server 之前）。此前 Jinja / chat template 报错因原文里带 HTTP 500 而被 server 抢走，给出「模型未加载 / 显存不足」这种完全不对路的建议；现在明确指出模板只允许第一条是 system，并指引先更新插件、再查消息序列、最后确认 --jinja。
    【修复】诊断报告自相矛盾（「服务端没有这个模型」与「对话接口可用 HTTP 200」并存）。真因是单模型推理服务会忽略 model 字段。现在模型名比对先登记为信息级，只有对话实测也确认是模型问题才升级为失败。
    【修复】诊断的探测请求没带 system 消息，因此测不出上面那条 500。现在两次探测都以 system + user 的形态发出，与插件真实请求形态一致。
    【重要修复】切换页签后回到「对话」页，底部输入框与发送按钮消失。真因是 Qt5 侧工作流页的 QtWebKit QWebView 没有实现 sizeHint，Qt 回落到默认 800x600，把 QTabWidget 的 sizeHint 顶到 812x805，触发尺寸自适应后 dock 被撑到屏幕之外并被 QGIS 记入 profile。三处修复：① QWebView 设 Ignored 尺寸策略；② 新增 _DockTabWidget，sizeHint / minimumSizeHint 只按当前页算并夹在 [430,720]；③ 新增 QGISAgent._clamp_dock_to_screen()，首次显示后把底边超出屏幕的旧尺寸收回一次。实测 QTabWidget.sizeHint 由 812x805 降至 366x430，工作流页 sizeHint 由 773 降至 173。
    【改进】诊断建议定向化：模型名与服务端清单不一致且无相近候选时，直接点名「服务端实际提供的是「X」，模型名请照它填写」，不再说「改成上面列出的其中一个」；并说明单模型服务会忽略模型名、网关则必须填对。
    【测试】新增 system 消息不变式守卫（已反向验证，旧写法会以 ['SystemMessage','SystemMessage','HumanMessage'] 失败）、template 分类与规则顺序不变式、mock 模式 MODE_SYSTEM_POS 及 3 例诊断用例；单测 355 → 359 全绿；真机验收双版本既有 UI 各 137/137，新增 dock 尺寸验收 Qt5 28/28、Qt6 26/26。

    v2.4.2 更新内容：
    【修复】本地模型报「模型错误 / 错误原因无法自动识别（unknown）」——本次报障的主场景。补上四条此前落到 unknown 的真实措辞：model 'x' not found、model is not loaded、the request exceeds the available context size (NNNN tokens)、Failed to parse chat template: this model does not support tools；新增「地址路径不对」与「服务端 5xx（可重试）」两个分类。
    【修复】API Key 留空时报错无法归类。本地服务（llama.cpp / Ollama / LM Studio）通常「随便填」即可，但留空会让 OpenAI SDK 自己先抛 The api_key client option must be set；现在这条单独接住，明确说明填任意占位符（如 sk-local）即可但不能留空。
    【修复】报错原文此前只写进「报告」页签的执行日志，对话里看不到任何线索。现在把服务端原文直接摊在对话的错误卡片上（自动抽 JSON message、补 HTTP 状态码、脱敏、截断），并提供「复制报错详情」「诊断连接」两个可点击入口。
    【修复】tool 分类在 llama.cpp 场景下建议过于笼统，现直接给出 --jinja 启动参数与 GGUF chat template 检查项；rate_limit 补上 OpenAI 真实语序（You exceeded your current quota / billing hard limit）此前无法匹配。
    【新增】「测试连接与诊断」取代原来只发一条纯文本的连通性测试：① 地址缺 /v1 时自动生成候选并逐个试连；② 拉取 /v1/models 列出服务端实际模型名；③ 模型名比对并指出差异；④ 读 llama.cpp /props 的 n_ctx 并与插件固定开销（8193 字符 ≈ 2560 token）比对；⑤ 最后再发一次带 tools 的请求。诊断模块不依赖 Qt，任何一步失败只追加说明、绝不抛异常，结果可一键复制。
    【新增】明确「测试连接通过 ≠ 对话可用」：旧测试只发纯文本，真实对话走 bind_tools，因此模型不支持 function calling 时旧测试照样报「连接成功」而每次对话都失败——这正是「同一个模型在别处能用、在这里不行」的成因。新诊断显式补上了带 tools 的那一次请求。
    【改进】顶部功能页签：文案压缩为 2-3 字（对话 / 历史 / 模型 / 工作流 / 报告 / 帮助），改为下划线式选中态并配逐页 tooltip，不拉伸、窄宽度自动退化为滚动按钮；实测 dock 最小宽度 360px 下六页签完整可见。
    【改进】帮助页重做：抽成 help_content.py 纯函数，只用实测可用的富文本 CSS（表格宽度写成 width 属性、不用 var()/border-radius/flex），颜色按当前调色板注入；内容重写为「30 秒上手 / 页签都在做什么 / 能做什么 / 模型配置要点 / MCP 服务 / 常见问题 / 安全与隐私 / 内置工具 / 链接」，重点说明本地模型三条坑（Base URL 带 /v1、Key 填 sk-local、上下文开够 --ctx-size 8192）以及 --jinja。
    【改进】错误卡片告警色按主题自适应（浅色 #C0392B / 深色 #FF7A70），此前深色主题下几乎看不出是红的。
    【测试】新增 endpoint_diagnostics 单测 28 例（含 mock llama.cpp 端到端：正常 / 模型名不匹配 / 上下文过小 / 不支持 tools / 缺 /v1 / 不可达）与 help_content 单测 23 例（含富文本 CSS 禁区守卫）；单测 293 → 353 全绿；真机验收 QGIS3(Qt5) 与 QGIS4(Qt6) 各 137/137 通过。

    v2.4.1 更新内容：
    【改进】「对话」窗口视觉重做：消息改为气泡卡片（提问靠右、回复靠左，各带一条角色色条），回复中的代码块、表格与引用块在深色与浅色两种主题下都清晰可辨；思考过程折叠成一行「思考完成 · N 字」，可点击展开/收起、也可单独复制；空白状态下给出可点击的示例问题；输入区整理为「输入框 + 发送/停止按钮」，发送过程中只切换按钮，不再禁用整个输入区。
    【修复】对话历史区整块空白的严重问题。装配聊天区时用了 QLayout.replaceWidget()，它返回的布局项由 Python 侧持有，未接住就会被回收，而底层布局仍指向它——结果是新控件从未真正进入布局，表现就是"消息区什么都没有"。现改为「先摘出、再插入」的装配方式，并加入装配顺序自检（搜索条 < 聊天区 < 输入区 < 底部栏 < 状态条），顺序一旦错乱会在日志中明确报出。
    【修复】输入框高度自适应从未生效。此前依据 QTextDocument 的高度判断，而该高度在文档尚未参与绘制时返回的是"段落数"而非像素高度，导致输入框永远停在 44px，多打几行就冒出假滚动条。现改用文本排版引擎逐行累加真实行高，Qt5 与 Qt6 下表现一致：1 行 44px、2 行起逐行增高，超过上限（140px）才出现滚动条。
    【修复】Markdown 渲染的组件样式表整份失效。样式表使用了 CSS 自定义属性 var(--x)，而 QGIS 的富文本引擎不解析它——整条声明连备用值一起被丢弃，代码块底色、表格表头、链接颜色等全部落空，只是恰好被对话区自身的样式兜住才未暴露。现全部改为实际色值，并让转换函数自带完整样式，不再依赖调用方。
    【改进】对话窗口对窄幅停靠更友好：整块最小宽度由 604px 降到 360px，「报告」页由 588px 降到 259px，「模型配置」页由 377px 降到 130px。对话头部的元信息行改为可折行（此前禁止折行，一行摘要就把最小宽度顶到 395px），日期与计数内部使用不换行空格，折行只发生在分隔符处；「报告」页的 5 个按钮改为 3 行 2 列网格。
    v2.4.0 更新内容：
    【新增】MCP 服务出口：插件内置的 20 个工具可通过 Model Context Protocol 提供给外部 AI Agent（Claude Desktop / Cursor / Codex 等），实现"外部 Agent 驱动 QGIS"。由两部分组成——插件内的 mcp_bridge（本地 socket 服务）与随包附带的 stdio MCP Server（纯标准库实现，无需安装任何第三方包）。
    【新增】「模型配置 → MCP 服务」设置区：启用开关、监听端口、访问令牌（可重新生成与复制）、随插件自动启动、是否放行特权工具、实时连接状态、一键复制客户端配置、连通性自检。
    【安全】MCP 服务默认关闭且仅在 127.0.0.1 上监听，不支持绑定其它地址；强制校验访问令牌（常数时间比较），无令牌或令牌错误一律拒绝；特权工具（执行 PyQGIS 代码 / 处理算法 / 删除图层 / 打开与保存工程 / 运行技能）默认不出现在外部 Agent 的工具清单中，调用也会被拒绝，即便显式放行，每次执行仍会在 QGIS 界面上弹窗确认；端口与令牌写入 ~/.qgis_agent/mcp_session.json（权限 0600），服务停止后立即删除。
    【修复】「浏览器兼容 TLS」是可选项，此前一旦勾选而环境里没有 curl_cffi，插件会直接报错——不只是「测试连接」失败，对话功能也会整条不可用。现在改为降级处理：改用标准 TLS 栈继续工作，并在日志与测试结果里如实说明该选项未生效；设置页打开时会自动纠正「勾了但不生效」的陈旧取值，手工再勾选会立即给出说明。该选项仅在接口连接被网关重置时才需要，不安装不影响其它功能。
    【修复】依赖探测不再只捕获 ImportError（模块加载前的探测与启用插件时的依赖检查两处均已修复）：依赖装了一半时抛出的常是 SystemError（如 pydantic 与 pydantic-core 版本错配）或 OSError（动态库加载失败），异常逃逸会导致整个插件加载失败且界面没有任何提示。现统一兜住，并区分「确实没装」与「装了但加载不了」两种情况：前者可自动安装，后者只做诊断、绝不去重装（重装上层依赖只会把用户环境改得更乱），且会从报错中解析出版本号并直接给出修复命令。
    【修复】修复 QGIS 尚未注册 locale 时 QSettings 返回 None 导致插件初始化直接报错的问题。
    【改进】接口连接被网关在 TLS 握手阶段中断时，错误提示改为中性表述并给出可操作建议（更换接口，或开启浏览器兼容 TLS）。
    【改进】未安装 curl_cffi 时，「浏览器兼容 TLS」开关直接置灰，而不是「点了才弹窗、再自动取消勾选」——置灰的控件收不到鼠标事件，这条误导路径在界面上彻底不可达；灰字提示给出可照抄的 pip 安装命令与重启说明。
    【安全】「模型配置 → MCP 服务」的访问令牌默认以星号隐藏，并提供「显示/隐藏」开关。令牌是长期凭证，此前明文长期贴在设置页上，任何一次截图、录屏或远程协助都等于把凭证交出去；「复制令牌」与内部取值拿到的始终是完整明文，隐藏只影响屏幕呈现。
    【改进】移除 psutil 依赖。它唯一的用武之地是一个自首个版本起从未被调用的 get_system_info()，而该包在 macOS 的 QGIS 里因 hardened runtime 签名限制本就加载不了——少一个装不上又不生效的依赖。
    【兼容】Qt5/Qt6 双兼容，最低兼容 QGIS 3.22（覆盖 QGIS 3.22~4.x）。
    v2.3.2 更新内容：
    【新增】浏览器兼容 TLS（可选）：部分 API 网关会依据客户端 TLS 指纹判断请求来源，非浏览器客户端可能在握手阶段被中断。开启该选项后改用 curl_cffi 的浏览器 TLS 栈，可提升这类接口的连接成功率（实测可连通 ai.zhaosh.fun）。默认关闭，不引入额外依赖，不影响默认发布路径。
    【改进】错误分级新增「TLS 握手被网关中断」专属分类：连接失败时直接指出这一原因，并给出「更换官方接口 / 开启浏览器兼容 TLS」的可操作建议，不再只显示泛泛的"网络连接失败"。
    【兼容】Qt5/Qt6 双兼容（同 v2.3.1）。
    v2.3.1 更新内容：
    【兼容】Qt5/Qt6 双兼容修复（真机 QGIS 4.2.1 / Qt6 实例化验证通过）：
      - thinking_display.py 取色改用 qgis.PyQt（移除硬编码 PyQt5/PyQt6，Qt6 下会加载失败或与 QGIS 绑定冲突）；
      - 输入框自适应高度信号由 QTextDocument.sizeChanged（Qt6 已移除）改为 QTextEdit.textChanged（Qt5/Qt6 通用）。
    【发布】最低兼容 QGIS 3.22（覆盖 QGIS 3.22~4.x，Qt5 与 Qt6 全兼容）。
    v2.3.0 更新内容：
    【新增】Skills 系统：新增 run_skill 工具，可加载并运行内置/用户技能配方（工具数 19→20）。
    【新增】工作流录制与回放：可录制一段 QGIS 操作序列并事后回放。
    【新增】主动澄清：意图不明确时先向用户确认再执行（clarification 信号已接入对话链路）。
    【新增】任务图：复杂目标可拆解为可跟踪的计划步骤。
    【兼容】最低兼容 QGIS 3.22（Qt5/Py6 双兼容，覆盖 QGIS 3.22~4.x）；全仓 Qt 枚举改为作用域写法。
    【清理】删除废弃的 v1 dockwidget、agent_loop 实验模块等约 4900 行零引用死代码；前台交互优化（思考展示、停止中间态、首启引导、RAG 后台化、测试连接后台化）。
    v2.2.0 更新内容：
    【新增】4 个高频工具：查询算法真实参数(get_algorithm_parameters)、查看图层档案(get_layer_profile)、分级/分类设色(set_layer_renderer)、坐标转换(reproject_layer)。
    【修复】危险操作确认扩展到 5 类（删图层/打开工程/保存工程/覆盖渲染）；跳过确认不再永久生效（仅本次会话）；点"停止"后对话可恢复。
    【修复】SmartDebugger 自动诊断并改写重试，复杂任务成功率提升；出错改为中文分级提示，不再甩英文堆栈。
    【改进】思考过程改为累积展示、Enter 发送 / Shift+Enter 换行；属性表等不可信数据统一净化，阻断提示词注入。
    【发布】最低兼容 QGIS 3.22；打包剔除开发记忆文件。
    完整版本历史见 CHANGELOG.md。
