Files
UHD_Media_Tool/packages/db
rootandClaude Opus 5.5 003815591c feat(agent): 运行时从 pi-agent-core 0.99.2 迁到 pi-durable 1.1.0 + pi-ai 1.1.0
- PiAgentRuntime 改用 pi-durable Harness:每个 run 一个 JSONL 存储目录 sessionDir/<runId>/ + root 对话;
  系统提示 / 能力工具 / 白名单与预算钩子装在每次运行自己的扩展里;watchEvents 翻译成 AgentRunEvent
- 挂起(工具内 close)立即封口,调用保持未完成,重开后 resume 重放;replayGate 用调用级 memo
- 旧格式 <runId>.jsonl 打开时抛 LegacySessionError;skills 加载与 <skill> 块格式内置(源自 pi-agent-core,MIT)
- tool_call_end 新增可选 durationMs;用量取 pi.usage 合计(整段会话总量)
- Azure provider 改名 azure,迁移 0093 改存量;移除 pi-agent-core,锁 pi-durable/pi-ai/chord 1.1.0
- 文档:ADR-003、pi-providers、runbook、agent README、CHANGELOG

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-08 13:42:36 +08:00
..
2026-10-08 12:51:57 +08:00

@umt/db

PostgreSQL 访问层:Kysely 客户端与连接池、表类型(src/types.ts)、迁移文件(migrations/)与迁移器。表名 snake_case,与 REQ §12 实体名一致。

规则

  • 迁移是唯一事实来源。所有 schema 变更只能新增迁移,已发布迁移不得修改。迁移用原生 DDL 书写(src/migration-helpers.ts:entityTable() 统一加 uuid 主键 gen_random_uuid()、created_at/updated_at 与 updated_at 触发器;oneOf() 生成状态 CHECK)。migrations/ 内相对导入必须带 .ts 扩展名(Kysely 用原生 import() 加载)。
  • src/types.ts 是生成物:deno task db:migrate && deno task gen:types。CHECK 枚举自动变成字面量联合(如 AcquisitionSlotStatus)。test/schema.test.ts 会在隔离 schema 里重新反射并比对,漂移即失败;packages/contracts/test/db-sync.test.ts 再校验 contracts 的 Row schema 与枚举和这里一致。
  • 状态列一律 text + CHECK,不用 PG enum(便于迁移加值)。JSONB 只放证据、规则参数、原文快照与演进字段;稳定查询字段用关系列。
  • 凭证只存 secret_ref(→ secret.id),明文不入库、不进日志。
  • createDb() 把 int8 解析为 number(超出 2^53 抛错)、date 保持 YYYY-MM-DD 字符串。写 jsonb 数组时须先 JSON.stringify(pg 会把 JS 数组当 PG 数组)。
  • 测试对真实库(默认 postgres://umt:umt@localhost:55433/umt)在临时 schema 中跑迁移往返;无库环境设 UMT_SKIP_DB_TESTS=1。在非 public schema 迁移时要传 createMigrator(db, { migrationTableSchema })。

迁移清单

文件 内容
0001_init settings、api_key、audit_event、outbox_event
0002_foundation pgcrypto / pg_trgm、umt_touch_updated_at()、secret;outbox 投递字段(ADR-002);api_key.updated_at
0003_instances library_root、downloader_instance、media_server_instance、path_mapping
0004_content media_entity、content_unit、title_alias
0005_identity external_identity、identity_revision
0006_sources site、site_account、feed_source、source_release、observation
0007_assets media_asset、asset_unit_coverage、asset_location、path_binding
0008_policy policy_revision、subscription、acquisition_slot、preference_tag、preference_suppression
0009_execution task、acquisition_plan、download_task、asset_operation;回填 media_asset.download_task_id、acquisition_slot.reserved_plan_id
0010_agent agent_run、artifact、decision、approval、automation_grant
0011_ops reconcile_run、server_mapping
0012_organization organization_record(change_seq 序列 + 触发器)、organization_file_result;回填 asset_operation.organization_record_id
0013_tasks_tables 并入 @umt/tasks 自有表:tasks_run_meta、tasks_fact、tasks_op_ledger(if not exists,可接管 W1 旧库)
0014_agent_run_event agent_run_event:Agent 运行事件,主键 (run_id, seq),删 run 级联
0018_sources_snapshot site.adapter 增加 api;source_release.resolution_locked(人工锁);site_snapshot(快照/补漏/实时补查的覆盖范围)
0019_identity_pool identity_candidate(待识别池)、resolution_attempt(识别尝试)、identity_redirect(合并跳转)
0020_metadata_fields metadata_field(来源候选字段)、field_override(用户覆盖与锁定)

表清单(45 张)

底座 / 运维

表 用途与关键不变量
settings 全局设置 key→jsonb;key 为 GlobalSettings 的分组名(automation/cache/organize/retention/pi/budget)
api_key API Key(只存 hash);scope ⊂ read/organize.read/organize.execute/admin,可撤销
audit_event 审计日志,只追加
outbox_event 业务事务内写入的事件;分发器扫 status='pending' and next_attempt_at<=now(),idempotency_key 唯一,作为引擎入队幂等键
secret 加密凭证(ciphertext/nonce/auth_tag/kdf/key_version);其他表只存 secret_ref
reconcile_run 五类校验器的运行记录,resume_cursor 支持中断续扫;mode=read_only 用于恢复后对账
server_mapping Emby 兼容库存条目 ↔ 本地对象;(media_server_instance_id, server_item_id) 唯一;未知 ≠ 缺失,不构成纳管授权

实例

表 用途与关键不变量
library_root 媒体库根(执行进程可访问路径);management_mode register_only/managed,只登记范围不被全局规则隐式升级为自动下载
downloader_instance qB 实例;managed_tag 管理标签;cache_volume_key 聚合共用缓存卷的水位
media_server_instance Emby/Jellyfin 实例
path_mapping 下载器/媒体服务器路径前缀 → 本地路径;同实例同 remote_prefix 唯一;view_kind 与实例列一致(CHECK)

内容 / 身份

表 用途与关键不变量
media_entity 本地作品,UUID 为主身份;合并时 status=merged 且 merged_into_id 指向目标(旧 ID 跳转);field_locks 人工锁字段刷新不得覆盖
content_unit 季/集/特别篇/分段;编号(season/episode/source_issue/air_date)可修订,不是唯一身份;numbering_locked 人工锁
title_alias 原名/译名/简称/站点原标题;normalized 上建 gin trgm 索引做模糊召回
external_identity 本地对象 ↔ 外部 ID;(provider, entity_type, external_id) 对 accepted 部分唯一,同一本地对象每个 provider+type 只有一个 accepted;候选(proposed/conflicted)可并存;404/超时只记 verify_status
identity_revision 身份每次变更的修订历史,(external_identity_id, revision) 唯一,只追加
identity_candidate 待识别池:每条 source_release 至多一条;候选只存 JSON(不建正式作品),保存原因/证据版本/复查时间(有界退避)
resolution_attempt 每次识别尝试的结果与证据版本,只追加
identity_redirect 作品合并后的旧 ID 跳转,from_entity_id 唯一
metadata_field 各来源的候选字段值(来源/语言/时间/来源修订);刷新只写这里
field_override 用户字段覆盖与锁定,优先于自动来源;目标 + 字段唯一

来源

表 用途与关键不变量
site 站点(adapter/限流/熔断/快照游标);关闭站点即停止网络访问与新候选
site_account 站点账号,凭证走 secret_ref
feed_source RSS/列表/查询入口;真实 URL(含 passkey)只在 url_secret_ref,对外只给 url_display;按 next_fetch_at 调度
source_release 站点资源;(site_id, site_release_id) 唯一,多 Feed 观察汇到同一行;source_published_at/first_seen_at 分开
observation 一次原始观察;(feed_id, guid) 唯一,content_digest 变化即条目更新
site_snapshot 一次列表快照 / 补漏 / 实时补查:页码、条目数、状态与错误(新鲜度来源)

文件

表 用途与关键不变量
media_asset 文件资产(与位置分离);附属文件 parent_asset_id 指向主视频;内容版本 content_edition 与交付特征 delivery 分开
asset_unit_coverage 资产覆盖哪些内容单元(合集/分段),(media_asset_id, content_unit_id) 唯一
asset_location 资产的一个位置;同一库根 + 相对路径在非 removed 状态下唯一;状态 available/unavailable/suspected_missing/confirmed_missing/removed
path_binding 作品/季目录绑定;一旦建立不因元数据刷新改变,只有显式整理可替换(旧行标 superseded);同作品同季只有一个 active,同一路径只绑定一个 active;历史双目录用 conflict

规则

表 用途与关键不变量
policy_revision 策略版本(草案/发布/回滚);(policy_key, revision) 唯一,每个 policy_key 只有一个 published
subscription 追踪目标(单次/持续,仅本季/后续季,优先级);作品未建档时用 title_query
acquisition_slot 独立收录目标;状态 empty/reserved/downloading/verifying/satisfied/upgrade_pending/attention;(media_entity_id, content_unit_id, name) 唯一(NULLS NOT DISTINCT)
preference_tag 前端可编辑偏好标签;fingerprint 在未删除标签中唯一
preference_suppression 取消标签的抑制记录;active 时 Agent 不得据同一 fingerprint 自动加回

执行

表 用途与关键不变量
task 业务工作单/时间线投影(引擎只管推进);(kind, idempotency_key) 唯一;按 (status, next_run_at) 调度
acquisition_plan 下载计划 + Slot/预算预留;每个 Slot 同时只能有一个 reserved/submitted 计划(部分唯一);idempotency_key 唯一
download_task 下载器任务映射;(instance_id, torrent_hash) 唯一;pause_reason 区分用户/阈值暂停;next_check_at 对账调度
asset_operation 副作用账本;operation_id 唯一;attempt_token 做 fencing

人工 / AI

表 用途与关键不变量
agent_run Agent 运行、预算/用量、会话状态、事件序号
artifact 证据/计划/提案产物;提案不直接晋升为事实
decision 待处理中心条目;同一 dedupe_key 同时只有一个未关闭条目
approval 单次审批,绑定 plan + payload_hash + expected_revision
automation_grant 持续授权(动作 + 范围 + 预算 + 策略版本),可撤销
agent_run_event Agent 运行事件(9 种,见 contracts AgentRunEventType);(run_id, seq) 主键,先落库再推 SSE;payload = 事件主体去掉 type

任务引擎(@umt/tasks 内部使用,不经 API 暴露)

表 用途与关键不变量
tasks_run_meta 引擎 run 的业务侧元数据(traceId/优先级/取消标志)
tasks_fact signal 前先落库的事实,waitForFact 以它为准
tasks_op_ledger 操作账本 fencing:attempt_token,旧 attempt 提交被拒

整理

表 用途与关键不变量
organization_record 整理批次(公开整理记录 API 的来源);change_seq 在任何 update(含文件明细变化)时取新序列值并唯一,change_xid 记录变更事务号
organization_file_result 逐文件前后路径与结果;增删改会刷新父记录 change_seq

游标读取:where change_seq > $after and change_xid < pg_snapshot_xmin(pg_current_snapshot()) order by change_seq limit $n。第二个条件排除仍在进行中的事务,避免"序号小但提交晚"被跳过;返回的最大 change_seq 即 nextCursor。