"""插件入口契约。 定义插件宿主 Protocol(``PluginHost``)与插件入口类型别名 (``CHANNEL_ENTRY``)。插件通过 ``CHANNEL_ENTRY`` 函数注册,由宿主在 loaded 阶段调用,返回 ``PluginManifest``。 """ from __future__ import annotations from collections.abc import Callable from typing import Any, Protocol, runtime_checkable from yuxi.channels.contract.dtos.plugin import DomainEvent from yuxi.channels.contract.dtos.route import MatchTier from yuxi.channels.contract.plugin.capability import CapabilityProver from yuxi.channels.contract.plugin.extension_point import ( ConfigSource, EventSubscription, StageSlot, ) from yuxi.channels.contract.plugin.manifest import ( ChannelManifest, PluginManifest, ) from yuxi.channels.contract.ports.driven import ( AgentRunPort, AuditLogRepositoryPort, CachePort, ChannelAccountRepositoryPort, ChannelSessionRepositoryPort, ConfigPort, ConversationPort, IdempotencyRepositoryPort, IdentityResolverPort, LoggerPort, OutboxRepositoryPort, PairingRepositoryPort, PersistenceHealthPort, PersistencePort, QueuePort, TracerPort, UserIdentityRepositoryPort, ) class PluginAdapter(Protocol): """插件适配器标记 Protocol。 标识 ``PluginHost.registerAdapter`` 接受的适配器实例类型。渠道插件 可注册的适配器包括 ``contract.plugin.adapters`` 中定义的 23 类适配器 协议(InboundAdapter / OutboundAdapter / SessionAdapter / RichMessageAdapter / StreamingAdapter / DirectoryAdapter / CommandAdapter / WizardAdapter / DoctorAdapter / WhitelistAdapter / ToolsAdapter / MessageOpsAdapter / StatusAdapter / MentionAdapter / IdentityResolverAdapter / LifecycleAdapter / LoginAdapter / ProbeableAdapter / ContentModerationAdapter / PullerAdapter / StreamConnectorAdapter / AttachmentUploadAdapter / WebhookTestAdapter),加 渠道上下文提供者端口(ChannelContextProviderPort),它们各自定义独立 的处理方法,无公共方法基类,故本 Protocol 作为类型边界用于静态检查 与文档化,不含公共方法声明。 """ ... #: 路由匹配器类型别名。 #: #: 接收绑定上下文,返回匹配的路由绑定或 ``None``。插件通过 #: ``registerMatcher`` 注入自定义匹配函数,供 ``RouteResolver`` 遍历 #: 层级时调用执行匹配逻辑。使用 ``Any`` 避免运行时解析 #: ``BindingContext``(TYPE_CHECKING 导入,见 ``TierMatcher``)。 Matcher = Callable[..., Any] @runtime_checkable class PluginHost(Protocol): """插件宿主 Protocol。 由宿主实现,向插件提供被驱动端口获取、扩展点注册、能力边界声明与 事件发布能力。使用 ``@runtime_checkable`` 装饰以支持 ``isinstance`` 检查。 资源访问约束(INV-I7 / FF-ADAPTER-01 / FR-32): - 插件 **不得** 直接访问宿主的 ``settings``、``logger``、数据库连接池、 Redis 客户端,**必须** 通过 ``PluginHost`` 提供的端口获取方法获取依赖。 - 插件 **不得** 修改宿主的中间件链、路由表、事件订阅器,**只能** 通过 扩展点注册方法注入自身逻辑。 - 插件能力声明 **必须** 通过 ``CapabilityProver`` 验证,验证失败 **不得** 启动插件。 插件必须通过 ``PluginHost`` 完成以下三类操作: 1. 端口获取:通过 ``getXxxPort()`` 方法获取被驱动端口,访问配置、日志、 持久化、缓存、追踪、队列、会话、Agent 运行与身份解析能力。 2. 扩展点注册:通过 ``registerXxx()`` 方法注册适配器、阶段槽位、事件订阅、 配置源与能力证明者,向宿主注入自身逻辑。 3. 能力边界声明:通过 ``declareXxx()`` 方法声明可访问端口、可注入管道与 资源配额,由宿主强制约束。 """ def getConfigPort(self) -> ConfigPort: """获取配置端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getLoggerPort(self) -> LoggerPort: """获取日志端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getPersistencePort(self) -> PersistencePort: """获取持久化端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getCachePort(self) -> CachePort: """获取缓存端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getTracerPort(self) -> TracerPort: """获取追踪端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getQueuePort(self) -> QueuePort: """获取队列端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getConversationPort(self) -> ConversationPort: """获取会话端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getAgentRunPort(self) -> AgentRunPort: """获取 Agent 运行端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getIdentityResolverPort(self) -> IdentityResolverPort: """获取身份解析端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getChannelAccountRepositoryPort(self) -> ChannelAccountRepositoryPort: """获取渠道账户仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getChannelSessionRepositoryPort(self) -> ChannelSessionRepositoryPort: """获取渠道会话仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getPairingRepositoryPort(self) -> PairingRepositoryPort: """获取配对仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getAuditLogRepositoryPort(self) -> AuditLogRepositoryPort: """获取审计日志仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getOutboxRepositoryPort(self) -> OutboxRepositoryPort: """获取发件箱仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getUserIdentityRepositoryPort(self) -> UserIdentityRepositoryPort: """获取用户身份仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getIdempotencyRepositoryPort(self) -> IdempotencyRepositoryPort: """获取幂等性仓储端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def getPersistenceHealthPort(self) -> PersistenceHealthPort: """获取持久化健康端口。 @consistency: 无状态(stateless),返回注入的端口实例。 """ ... def registerAdapter(self, adapter_type: str, adapter: PluginAdapter) -> None: """注册适配器。 参数: adapter_type: 适配器类型。 adapter: 适配器实例。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册相同 adapter_type 会覆盖前一次的实例. """ ... def registerStageSlot(self, slot: StageSlot) -> None: """注册管道阶段槽位。 参数: slot: 阶段槽位实例。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册相同锚点 + 优先级的 slot 会触发冲突策略. """ ... def registerEventSubscription(self, subscription: EventSubscription) -> None: """注册事件订阅。 参数: subscription: 事件订阅实例。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册会创建多个订阅,同一事件被多次处理. """ ... def registerConfigSource(self, source: ConfigSource) -> None: """注册配置源。 参数: source: 配置源实例。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册相同 key 会覆盖前一次的源. """ ... def registerCapabilityProver(self, prover: CapabilityProver) -> None: """注册能力证明者(FR-08 增强)。 注册 ``CapabilityProver`` 实例,供宿主对插件清单声明的静态能力 进行运行时证明。宿主在 ``initialized`` 阶段调用 ``proveCapability`` 对每项能力进行证明,证明失败的插件 **不得** 启动(FR-32)。 参数: prover: 能力证明 Protocol 实例。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册会覆盖前一次的 prover 实例. """ ... def registerMatchTier(self, tier: MatchTier) -> None: """注册路由匹配层级(FR-04)。 参数: tier: 匹配层级配置。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册相同层级名会覆盖前一次的配置. """ ... def registerMatcher(self, name: str, matcher: Matcher) -> None: """注册匹配层级的匹配器(FR-04)。 为已注册的层级绑定匹配函数,供 ``RouteResolver`` 在遍历层级时调用 执行匹配逻辑。插件通过本方法注入自定义匹配规则。 参数: name: 层级名称(必须已通过 ``registerMatchTier`` 注册)。 matcher: 匹配器函数,接收绑定上下文,返回路由绑定或 None。 @consistency: 无状态(stateless),仅写入注册表。 @idempotent: False — 重复注册相同 name 会覆盖前一次的 matcher. """ ... async def publishEvent(self, event: DomainEvent) -> None: """发布领域事件。 宿主负责将事件分发到订阅者。支持的事件类型: PluginDiscovered/PluginLoaded/PluginStarted/PluginPaused/ PluginResumed/PluginStopped/PluginUnloaded/PluginFailed/ PairingApproved/PairingRevoked/ConfigChanged/ChannelDegraded/ChannelRecovered。 参数: event: 领域事件。 @consistency: 最终一致(eventual),订阅者按各自策略处理。 @idempotent: True — 重复发布相同事件产生等价结果(订阅者需自行幂等). """ ... #: 插件入口类型别名。 #: #: 每个渠道插件 **必须** 通过 ``CHANNEL_ENTRY`` 契约注册,**不得** 隐式全局 #: 注入(FR-32)。由宿主在 loaded 阶段调用,返回 ``PluginManifest``,包含 #: 元数据、适配器、阶段、事件订阅等。 #: #: 签名 ``(host, manifest) -> PluginManifest``:宿主传入 discover 阶段从 #: ``manifest.json`` 解析的 ``ChannelManifest``,插件直接复用为返回清单的 #: ``manifest`` 字段,**不得** 在 entry.py 内重新构造声明性字段 #: (capabilities / config_schema / env_vars / accessible_ports / #: resource_quota / lifecycle / credential_strategy 等),避免双真相源 #: (F-03)。插件仅负责适配器实例化、client 实例化、LifecycleHandler #: 构造与 ``registerAdapter`` 调用。宿主在加载期校验返回清单的 #: ``manifest.id`` 与入参 ``manifest.id`` 一致。 CHANNEL_ENTRY = Callable[[PluginHost, ChannelManifest], PluginManifest]