79 Commits

Author SHA1 Message Date
jxxghp
d9afaba8dd feat(plugin): expose documentation before installation (#7222) 2026-10-27 21:37:00 +08:00
Aqr-K
6610a8c008 fix: retain plugin lifecycle ownership and recover installation state (#7146)
* fix: retain plugin lifecycle ownership and recover installation state

* test: cover plugin rollback and uninstall lifecycle boundaries

---------

Co-authored-by: jxxghp <jxxghp@gmail.com>
2026-10-23 07:44:07 +08:00
jxxghp
6d4ce83837 fix(types): clear host mypy debt and prevent typing regressions
严格检查全部宿主源码,清零类型错误预算并移除按模块类型豁免。
按原因、所属方法、错误码及语法指纹锁定必要兼容例外和站点类型桩,
CI 阻断新增忽略、检查范围退化、本地桩替代真实接口及新的类型错误。

修复空值、异步和供应方类型边界,保持插件与 API 兼容合同,
并补充存储、订阅、通知、Agent 和工作流边界的回归覆盖。
2026-10-21 22:03:18 +08:00
DDSRem
e0642eafa7 chore(types): reduce v3 host mypy debt (#7057)
* chore(types): clear AniList endpoint mypy debt

* chore(types): clear Douban endpoint mypy debt

* chore(types): clear recommendation endpoint mypy debt

* chore(types): clear subscription notification mypy debt

* chore(types): clear SDK string compatibility mypy debt

* chore(types): ratchet v3 host mypy across remaining files

* chore(types): reduce v3 host mypy debt and retire ledger

* chore(types): reconcile host snapshot with upstream HTTP pool

* test(types): cover host paths exposed by mypy cleanup
2026-10-18 09:37:49 +08:00
DDSRem
2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00
jxxghp
68101746ae fix(plugin): recover invalid historical instance rows (#7039)
* fix(plugin): recover invalid historical instance rows

* fix(plugin): keep maintenance on existing startup path

* style(plugin): order maintenance test imports

* chore(architecture): record plugin instance validator edges
2026-10-17 06:34:25 +08:00
jxxghp
22283b19ee refactor(types): govern plugin monitor candidate contracts 2026-10-16 23:21:33 +08:00
jxxghp
2b5eb02701 refactor(types): govern plugin path candidate contracts 2026-10-16 23:20:47 +08:00
jxxghp
6f7731545f refactor(types): govern plugin sync candidate payloads 2026-10-16 23:19:31 +08:00
jxxghp
38dd03b9f7 refactor(types): declare plugin system market port 2026-10-16 23:18:09 +08:00
jxxghp
2b5e0ce473 refactor(types): declare plugin catalog facade projections 2026-10-16 18:55:41 +08:00
jxxghp
661e9d95ae refactor(types): declare plugin access comparison result 2026-10-16 18:49:36 +08:00
jxxghp
5e14615dce refactor(types): govern raw plugin metadata mappings 2026-10-16 15:49:36 +08:00
jxxghp
3ee36342bd refactor(types): prove acquired plugin mutation context 2026-10-16 15:15:17 +08:00
jxxghp
ca15f05038 refactor(types): govern plugin lifecycle configuration 2026-10-16 15:15:17 +08:00
jxxghp
2d7af4b0a7 refactor(types): govern stored plugin configuration values 2026-10-16 15:15:17 +08:00
jxxghp
967860e830 refactor(types): specify plugin clone configuration values 2026-10-16 13:25:40 +08:00
InfinityPacer
d9ac4cc429 feat(plugin): expose remote entry and version on agent pets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:08:15 +08:00
InfinityPacer
40e80f43f5 fix(plugin): advance runtime generation when a plugin is enabled or disabled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:08:15 +08:00
InfinityPacer
e97f64e777 feat(plugin): add avatar to agent pet declarations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:08:15 +08:00
InfinityPacer
25aaf3a32b feat(plugin): add agent pet extension contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:08:15 +08:00
jxxghp
b663825fe7 fix(plugin): recover dependencies without disabled constraints
Keep enabled host and clone sources in dependency recovery. Fall back to per-plugin requests while retaining ready and recovered manifest constraints, preserving original sources and cancellation ownership.

Refs #6943
2026-10-10 12:20:13 +08:00
jxxghp
d7db4cfc0b fix(plugin): serialize package swaps with imports (#6939) 2026-10-10 07:42:37 +08:00
freeman
f3ad81b39e style: apply safe ruff auto-fixes and exempt startup late imports
Run `ruff check app tests scripts --fix` with the configured rule set
(E4/E7/E9/F/I): import sorting (I001), f-strings without placeholders
(F541), lambda/variable/unused-import cleanups that ruff marks safe.
Nine files whose only ruff debt sits next to pre-existing pylint
import-resolution errors (lazy exports, sys.path script imports) or
whose "unused" imports are reached through attribute access by tests are
left untouched so the changed-file pylint gate and the benchmark tests
stay green; their diagnostics remain in the baseline.

app/startup/lifecycle/__init__.py and app/main.py import business
modules after cache composition, import-path and stdio setup on purpose
(the source comments document the contract), so declare E402
per-file-ignores for them instead of adding per-line noqa.

Ruff ratchet watermark drops from 498 to 100; sync the baseline fixture
and the optimization checklist figure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 13:29:09 +08:00
InfinityPacer
2a68dd6fea fix(plugin): release reloaded plugin code held by framework caches (#6807) 2026-09-28 13:32:19 +08:00
InfinityPacer
19e062c178 feat(plugin): attribute free-threaded GIL fallback to plugins (#6767)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 13:12:02 +08:00
InfinityPacer
3cef2149d0 fix(plugin): read the runtime compatibility gate from the declared snapshot (#6761)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 01:44:45 +08:00
InfinityPacer
0238351071 fix(plugin): v3t 下不再隐藏不兼容插件,改为提示不支持 (#6760)
* feat(plugin): surface v3t incompatibility instead of hiding plugins

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(architecture): refresh dependency baseline after plugin gate split

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(plugin): keep runtime-incompatible plugins out of startup sync

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(plugin): annotate runtime compatibility ports without bare dict

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 00:35:20 +08:00
moviepilot-codex[bot]
63c07c43ad fix(plugin): snapshot registry classes during reload (#6750) 2026-09-21 17:25:06 +08:00
moviepilot-codex[bot]
c0f7141e26 fix(plugin): invalidate federation static resource cache (#6748) 2026-09-21 16:24:38 +08:00
Aqr-K
dc7e06ce38 fix(plugin): 落库失败的清理留在占位锁内,不再误删他人的实例 (#6695) 2026-09-14 17:41:08 +08:00
Aqr-K
c5d2fd2ef4 fix(plugin): 修分身恢复的占用判定、空模板配置重建、自动分配探测上限与预留落库回滚 (#6692) 2026-09-14 16:06:02 +08:00
Aqr-K
dd4ae3e8d5 refactor(plugin): 移除已无调用者的物理分身源码改写器 (#6683)
物理分身是早期实现:复制整个插件目录,再用字符串替换改写副本源码里的类名、
plugin_config_prefix 和联邦构建产物。虚拟实例接管后,分身不再复制目录,
由装载器改写类身份、按实例 ID 隔离配置与数据,这套改写器随之失去调用者。

求证依据(全仓 git grep,覆盖 app/、tests/、scripts/、docs/、skills/):

- PluginPackageManager.clone() 无任何生产调用者。分身的唯一生产路径是
  POST /api/v1/plugin/clone/{plugin_id} -> PluginManager.clone_plugin()
  -> PluginCloneService.clone(),后者只写 PluginInstance 记录与隔离配置,
  不碰包适配器。clone() 也没有经 PluginSystemServices 暴露出去。
- clone() 的四个私有实现 _modify_plugin_files / _modify_python_file /
  _modify_federation_files / _rename_federation_assets,消费者只有 clone()
  自身与 PluginSystemServices 的同名透传。
- PluginSystemServices 的四个透传,消费者只有 PluginManager 的 _modify_* shim。
- PluginManager 的四个 shim 无任何调用者。动态可达性已单独排查:
  全仓没有 getattr/字符串反射指向这些名字;app/runtime/compat/manifest.py
  不登记它们的别名(只有 app.core.plugin -> 本模块的模块级映射);
  app/sdk/ 不再导出;PluginManager 与 PluginPackageManager 都没有
  __getattr__ 兜底,属性不存在即 AttributeError;官方插件基线
  official-plugin-baseline.json 零命中。
- @observe_compat_facade("PluginManager") 只包装类上已存在的方法,
  不会凭空合成属性,因此 shim 删除后 enforce_facade 再也匹配不到
  notices.py 里那四条 key,登记随之成为不可达数据,一并删除。

替代路径:这四条弃用声明当初承诺的替代目标是
get_plugin_system().package._modify_*(),那是一次「门面搬到适配器」的
重定位登记,本次连同能力一起下线;分身能力本身的替代路径是虚拟实例
PluginCloneService.clone(),它已在生产链路上,见
docs/v2-to-v3-overview.md §3.6「不复制插件源码,也能保持各实例配置隔离」。
既有磁盘上的旧物理分身不受影响:卸载路径读取 is_clone 属性并调用
remove_plugin_package() 的逻辑原样保留,只是不再产生新的物理分身。

这是对已公告弃用 API(DeprecationStage.SILENT,since v3.0.0)的正式移除。

连带改动:
- package.py 的 import re 仅服务于 _modify_python_file,同步移除;模块
  docstring 去掉「分身处理」。
- system.py 的 typing.cast 仅服务于被删透传,同步移除。
- tests/test_plugin_package_manager.py::test_clone_rewrites_python_and_federation_assets
  是这套改写器的唯一测试,且只覆盖已无调用者的路径,随实现一并删除。
  (删的是测试函数不是测试文件,tests/run.py 的分片装箱不变。)
- mypy 低水位随实现下降:app/runtime/extensions/plugin/manager.py 的
  assignment 8->4(四个 shim 的 version/icon 参数 str = None)、
  no-untyped-def 12->11(_rename_federation_assets 缺返回标注),
  fixture 与 docs/refactor/optimization-checklist.md 的全量 mypy 计数
  9,371 -> 9,366 同步更新。依赖、复杂度、ruff、并发等其余基线无变化。

Co-authored-by: jxxghp <jxxghp@gmail.com>
2026-09-14 07:19:46 +08:00
Aqr-K
2789440796 fix(plugin): 分身 ID 对外查询统一归一到源插件,并补齐认证远程入口的源插件字段 (#6680) 2026-09-14 07:01:18 +08:00
Aqr-K
9955698b1a fix(plugin): 加载入口补生命周期锁,手工重载带上分身 (#6679) 2026-09-14 07:01:04 +08:00
Aqr-K
57fb96fc3c feat(plugin): 分身后缀可留空自动分配,并支持按后缀恢复已停用的分身 (#6673) 2026-09-14 07:00:31 +08:00
Aqr-K
8e4a49e958 fix(plugin): 修复分身与本体在事件定向和注册表键上的身份混淆 (#6672) 2026-09-13 21:05:26 +08:00
Aqr-K
5fd04505c1 feat(plugin): 分身彻底清理,并按包含性校验收口插件数据目录解析 (#6671) 2026-09-13 21:05:04 +08:00
Aqr-K
f5e68576b9 fix(plugin): 修复实例引导合并、存在性判据与实例日志等级贯穿控制台 (#6667) 2026-09-13 18:59:48 +08:00
jxxghp
421d2febac feat(plugin): skip invalid plugin instance records during read operations 2026-09-13 16:56:26 +08:00
Aqr-K
d44480d739 Merge pull request #6666 from Aqr-K/feat/plugin-instance-target-enablement
feat(plugin): 插件实例默认调用目标与启停,未指定实例时不再兜底取第一个
2026-09-13 16:37:27 +08:00
jxxghp
c60949ffff Merge branch 'v3' into feat/plugin-instance-config-logging 2026-09-13 14:30:42 +08:00
Aqr-K
3d594ecee3 refactor(plugin): 插件配置改走独立通道,SystemConfig 不再按键值分支
插件配置此前借用 systemconfig 的 plugin.<实例ID> 单键,为了换表又在
SystemConfigOper 的 set/delete/load_snapshot 里按该前缀开分支。这是反向依赖:
一个对所有键一视同仁的通用键值存储被迫知道「插件」这个概念,读写路径依键值而
异,「这个键落在哪张表」只有读代码才说得清;规范方法按值加特例,也让此后任何新
键都要先确认自己会不会被拦截。

本次把插件配置整条链拆出,形状与本分支日志等级那条链一致:

- SystemConfigOper 整体回退到官方实现,与 f70d7c141 逐字节相同,对所有键一视同仁。
- 配置端口改按实例 ID 寻址:PluginStorage 新增 read_config / write_config /
  async_write_config / delete_config,PluginConfigStore 去掉 key_prefix 与 _key,
  不再有任何字符串键经过;plugin_exists 存在性规则、空键过滤与 force 语义不变。
- PluginInstanceOper 补齐读取与异步写入原语 get_config_data /
  async_save_config_data,同步与异步共用同一份落库决策,避免两条路径对「值未变化
  不写」和「建行用哪个源插件」各自分叉。
- _PluginBase 的 get_config / update_config 直连 PluginInstanceOper,方法名、签名、
  返回与 docstring 不变;self.systemconfig 保留,插件读 SystemConfigKey 枚举值仍
  然合法,插件公开面没有扩大。

插件直接用 SystemConfig 存取自身配置不予支持。它本就不是封装过的插件配置通道,
为它设计兼容或加守卫拦截,等于再把「插件」塞回通用存储里——那正是本次要拆掉的东
西。拆开后 plugin.<ID> 只是一个与插件无关的普通 systemconfig 键:迁移已经把旧的
plugin.* 行搬进插件实例表,读它拿到 None,写它会新增一条谁也不读的 systemconfig
记录,插件配置通道既不读也不写它,两边不会再有任何交叉。

代价是读取从内存快照变成一次按实例 ID 的主键查询。调用点都不在热路径上:宿主侧
只有插件启动/重载、配置表单接口与分身创建三处,插件侧 get_config() 惯例在
init_plugin 里取一次。写入侧没有变化,本来就是一次事务。

回归防线:新增用例断言 SystemConfigOper 对 plugin. 前缀没有任何特殊行为——写进去
就是一行普通 systemconfig 记录,读出来就是刚写的值,不会被路由到插件实例表。

兼容层零改动,三个 alembic 迁移一行未改。
2026-09-13 01:41:57 -04:00
jxxghp
8072b7d78f Refactor application architecture and compatibility boundaries 2026-09-13 12:53:00 +08:00
Aqr-K
3eee49659d fix(plugin): 旧实例键回滚往返新增的分身要能合并进独立表
`PluginInstances` 旧键刻意保留作回滚依据、从不清理,旧版本只认它,因此它在
迁移之后仍可能长出新条目或被改写。原判据是「一条一次性的永久标记」加「整表
是否为空」:标记落下且表非空之后,回滚到旧版本新建的分身会被永久跳过,保留
旧键作回滚依据的承诺随之落空。两个条件都表达不出「旧载荷相对已导入内容有没有
新增或变化」。

改为按条目记住导入时的内容指纹(`PluginInstancesImported` 的载荷从布尔改为
`{实例ID: 摘要}`):指纹对得上就跳过,独立表里那行用户怎么改都不会被旧副本盖
回去;指纹对不上说明旧版本改写过,合并;指纹没记过且表里也没有该行说明旧版本
新增过,导入;指纹没记过但表里已有该行(alembic 刚搬过去的那一批)只认领指纹
不覆盖。指纹取自原始载荷而非模型 dump,模型字段将来增减才不会让全部指纹一起
失配、把用户改动整批盖回去。指纹表与旧键内容对齐后无变化的启动不再产生写入,
也不额外查表。
2026-09-13 00:14:47 -04:00
Aqr-K
e47d7defe7 feat(plugin): 插件本体与分身可各自设置独立日志等级
全局日志等级是一个开关,调试单个插件时只能整体下调。一旦下调,被调试插件那几行
日志会淹没在其他几十个插件与宿主自身的 DEBUG 输出里,找不出来;不下调又什么都
看不见。插件实例数量只会继续增长,这个矛盾不会自己消失。

因此把等级下放到实例:plugininstance 行加 log_level / log_expires_at 两列,本体与
分身共用同一张表、同一套语义。等级落在实例自己那一行而不是另起一张按实例 ID 索引
的表——它和业务参数一样是用户按实例设置的东西,同一个生命周期,实例被删时应当随行
一起消失。清除覆盖后若本体行只剩一对身份列,整行按 carries_only_identity 回收,与
清空业务参数同一规则。

覆盖带失效时间,因为它是调试设置不是长期配置:开了 DEBUG 忘记关,下次谁也想不起
某个插件为什么一直在刷日志。过期判定在读取时惰性执行并就地清理,不另起后台线程去
扫一张最多几十条的表。expires_at 在写入侧先归一到 UTC 再分发给缓存与落盘两处:客户端
提交的时间可能不带时区,两处各按进程时区折算一次,就会与读取口返回的 UTC 值差出一个
时区,表现为「提交的失效时刻和读回来的对不上」。

运行期用 ContextVar 在宿主自己控制的四类调用点绑定当前实例——构造与 init_plugin、
事件处理器、定时服务回调、插件声明的 HTTP 端点——而不是沿用 logger() 已有的栈帧内省。
内省认的是栈顶的模块与类,插件通过宿主公共方法转发调用时栈顶是宿主而不是发起调用的
插件,会把日志记到错误的来源上;分身更甚:分身共享源码,类的 __qualname__ 保持源类名
不变,内省根本分不出是哪一个分身在打日志。内省结果继续只用于文件路由,不参与等级判定。

等级过滤必须排在全局等级判定之前。官方现状是先按全局等级短路、之后才做内省,先短路
就会把实例调低的等级挡在门外,后面再怎么识别来源都看不到这条日志。

回调在注册时被捕获、稍后才由调度器或 FastAPI 调用,那时已经不在任何绑定作用域内,
故这两处改用 wrap_for_plugin_instance 把绑定包进回调自身;同一实例重复包装直接返回
原对象,避免插件缓存声明时包装链随重载次数无限增长。

进程内覆盖表按实例 ID 常驻,删分身时必须一并清掉:只删库里那一行的话,同一进程内用
相同后缀重建的分身会继承上一个分身的等级,界面显示「跟随全局」而实际仍按旧等级输出。
进程重启后反向从库预热缓存。

迁移链在 F1 之后追加:c4e1a7b9d2f6(3.0.35)-> 487f7e681955(3.0.36 加两列)。

顺带补了 agent 策略里缺失的 media.classification.policy.get 的 template 字段说明:
少了它,api_mcp_schema.json 与技能文档的两个生成脚本都跑不起来。
2026-09-12 19:05:05 -04:00
Aqr-K
4dbb8b8204 feat(plugin): 插件配置与实例描述符迁入独立表
插件配置此前寄存在 systemconfig 的 plugin.<实例ID> 单键下,实例描述符则整份挤在
PluginInstances 这一个 JSON 键里,两者都不成立:

- plugin.<ID> 是裸字符串 key,而仓库规则本身禁止用裸字符串做 SystemConfig key,
  只允许先定义 SystemConfigKey 枚举项;插件 ID 由用户安装决定,永远枚举不出常量。
  条目数随安装量增长,混在系统设置表里会把主程序自己的设置项淹掉。
- 那个键存的其实是实例 ID,表里没有任何一列说得出它属于哪个插件,想列出某个插件
  的全部实例配置只能靠字符串前缀去猜。
- 实例描述符整份读出再整份写回,改一个分身要重写全部分身。

改为 plugininstance 一实例一行:instance_id 与 source_plugin_id 构成身份,本体与
分身由两者是否相等派生而不设模式列——模式列会是这个等式的冗余副本,两者一旦失步
同一行就会在不同读取口被判成不同角色。展示信息与业务参数同存一行,属于同一个
生命周期,分表只会让建分身、删分身退化成两张表之间的协调问题。

读写路由落在 SystemConfigOper.get/set/delete,而不是在各个调用方各改一处:第三方
插件可能直接用 self.systemconfig.get("plugin.xxx") 读写自己的配置,只改
PluginConfigStore 与 _PluginBase.get_config/update_config 必然漏掉它们。路由只做
前缀识别,插入、更新与空本体行回收都委托 PluginInstanceOper,不在配置层重抄一遍。

PluginInstances 旧键迁移后不删,留作回滚依据,并以
SystemConfigKey.PluginInstancesImported 标志防止重复导入;判据不能是「表当前为空」,
否则用户把分身全部删光后,下次启动会把它们整批导回来。

迁移链:b2d4f6a8c1e3 -> 281965691a20(3.0.34 建表并搬描述符)
-> c4e1a7b9d2f6(3.0.35 加 config_data 并搬 plugin.* 配置)。

依赖基线与启动模块数随两个新模块重算,架构文档与数据库技能表目录同步登记新表。
2026-09-12 17:39:36 -04:00
InfinityPacer
4491610adb 修复插件来源仲裁与数据库备份 review 问题
合并 PR #6658。业务相关检查已通过;剩余失败为 PR-Agent 外部 API 日限额与上游既有复杂度 ratchet,不属于本次改动。
2026-09-12 21:22:01 +08:00
InfinityPacer
747f9f921f fix(plugin): prioritize local plugin sources 2026-09-12 19:11:08 +08:00
ranzhigg
f10af54404 fix(plugin-market): share index request limits 2026-09-09 07:48:40 +08:00