import Spec.Prelude
import Spec.System.Agent.Run
import Spec.System.Agent.Usage

/-!
# Capability —— 外部能力注册与调用(ADR-0027)

`AgentRun` 内可能调用**外部能力**:PDF→MD bundle、音视频→文本、OCR 等。这些能力
不是主 agent loop 的模型 completion,不持项目锁,不占 session 语义(ADR-0026 已拒绝
nested Run)。它们是 Run 内的副作用:读 workspace 输入、调外部服务、产物落 workspace、
写一条 `UsageFact`。

本模块 pin 三件事:

1. **ExternalCapability** —— 平台注册的、org 启用的文档/媒体转换服务,由稳定
   `capabilityId` 标识。
2. **CapabilityConnection** —— org-scoped 凭证连接,复用 ADR-0024 信封机制但独立于
   model-provider connection。只有 `active` connection 可被 resolver 使用。
3. **CapabilityInvocation 良构** —— 调用的输入/输出必须落在 run 的 workspace 内
   (ADR-0018 `AgentSurface`),且调用产生的消耗必须以 `UsageFact` 记录。

凭证不经 Agent 子进程:Agent 只通过 capability adapter 间接调用,adapter 在 Hub 侧
解析 org-scoped 凭证并调用外部服务(ADR-0024 resolver boundary)。这与 model-provider
的 loopback proxy 是不同的机制——capability 调用不走 agent 的网络面,走 Hub 直连。

不变式(`PINNED`, ADR-0027):

- **凭证隔离** —— capability credential 不进 Agent 进程环境;adapter 在 Hub 侧解析。
- **workspace 边界** —— 输入路径与输出目录都必须落在 run 的 workspace 内(ADR-0018)。
- **必记 fact** —— 一次成功调用必须产生 ≥1 条 `UsageFact`(`kind = externalCapability`),
  即使 `costUsd = none`(unknown ≠ 零,ADR-0022/0026)。

`CapabilityInvocation` 作为一等持久记录(status/retries/partial output)在 pilot 不做;
`UsageFact` 的 `capabilityId + correlationId` 是唯一可追溯痕迹(ADR-0027 Deferred)。
-/

namespace Spec.System.Agent

variable (I : Identifiers) (Path : Type)

/-- 外部能力的输入种类(`PINNED`, ADR-0027)。集合 `OPEN`——新增 kind 须 surface。 -/
inductive CapabilityInputKind where
  /-- PDF 文档(`PINNED`)。 -/
  | pdf
  /-- 图片(`PINNED`)。 -/
  | image
  /-- 音频(`PINNED`)。 -/
  | audio
  /-- 视频(`PINNED`)。 -/
  | video

/-- 外部能力(`PINNED`, ADR-0027)。平台注册的文档/媒体转换服务,由 org 启用。

一个 capability 由稳定 `capabilityId` 标识(如 `pdf_to_md_bundle`),声明接受的输入
种类与计量单位。org 通过 `CapabilityConnection` 启用它;adapter 是平台侧实现。 -/
structure ExternalCapability where
  /-- 稳定能力标识(`PINNED`;如 `pdf_to_md_bundle`、`audio_video_to_text`)。 -/
  capabilityId : String
  /-- 接受的输入种类(`PINNED`, ADR-0027)。 -/
  inputKind : CapabilityInputKind
  /-- 计量单位(`OPEN`;如 `"pages"`、`"audio_seconds"`)。与 UsageFact.unit 对齐。 -/
  meteringUnit : String

/-- capability connection 的运行态(`PINNED`, ADR-0024/0027):与 model-provider /
Feishu connection 同构,只有 `active` 可被 resolver 使用。 -/
inductive CapabilityConnectionStatus where
  | draft
  | active
  | disabled

/-- Organization 的外部能力凭证连接(`PINNED`, ADR-0027)。复用 ADR-0024 信封机制
但独立于 model-provider connection:capability 服务(如 MinerU、Whisper)有自己的 auth
形状与 readiness probe,不走 OpenRouter `/v1/models`。唯一性键为
`(organization, capabilityId)`。 -/
structure OrganizationCapabilityConnection where
  /-- connection 所属 organization(`PINNED`, ADR-0027)。 -/
  organization : I.OrganizationId
  /-- 该 connection 启用的 capability(`PINNED`, ADR-0027)。 -/
  capability : ExternalCapability
  /-- 运行态(`PINNED`, ADR-0024/0027)。 -/
  status : CapabilityConnectionStatus

/-- 一次 capability 调用的良构约束(`PINNED`, ADR-0027)。输入路径与输出目录都必须落在
发起 run 的 workspace 内(ADR-0018 `AgentSurface`);`runWorkspace` 与 `pathWithin`
由平台提供(表示 `OPEN`)。逃逸即越权,拒绝。 -/
structure CapabilityInvocation where
  /-- 发起调用的 run(`PINNED`;调用是 Run 内副作用,不跨 run,ADR-0026/0027)。 -/
  run : I.RunId
  /-- 被调用的 capability(`PINNED`)。 -/
  capability : ExternalCapability
  /-- 输入路径(workspace 内,`PINNED`, ADR-0018)。 -/
  inputPath : Path
  /-- 输出目录(workspace 内,`PINNED`, ADR-0018)。 -/
  outputDir : Path

/-- capability 调用良构:输入与输出路径都落在 run 的 workspace 内(`PINNED`,
ADR-0018/0027)。这是 `AgentFileOp.Authorized` 在 capability 调用上的对应物。 -/
def CapabilityInvocation.Authorized
    (inv : CapabilityInvocation I Path)
    (runWorkspace : I.RunId → Option Path)
    (pathWithin : Path → Path → Prop) : Prop :=
  ∃ w, runWorkspace inv.run = some w ∧ pathWithin inv.inputPath w ∧ pathWithin inv.outputDir w

end Spec.System.Agent
