refactor(copy): 术语统一——收录版本、模型服务、智能体等(DEAI-2)

- 版本目标 → 收录版本(前后端用户可见字符串、能力描述、给模型的提示)
- Agent 上游 / 上游 → 模型服务;界面上的 Agent / Agent 会话 → 智能体 / 智能体会话
- 身份歧义 / 识别有歧义 / 无法确定作品 → 没识别出作品;待识别(池) → 未识别
- 建档 → 加入媒体库;资产 → 文件;库根 → 媒体库目录;纳管 → 本工具 / 外部任务;
  哨兵 → 标记文件 / 检查目录;站点快照 → 抓取;Feed / RSS 订阅 / 来源健康 → RSS
- 代码标识符、API 字段名、迁移文件不改;glossary.md 同步;smoke 选择器跟随

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 20:35:43 +08:00
1 parent d282212a6b
commit 4480fc346d
212 files changed
+741 -715

No files matched your search

+3 -3
View File
@@ -23,8 +23,8 @@ const PROVIDER_ID = { anthropic: "umt-anthropic", "openai-compatible": "umt-open
*/
export function resolveModelFromSettings(settings: ResolvedAgentSettings): ResolvedPiModel {
if (settings.provider !== "anthropic" && settings.provider !== "openai-compatible")
throw new Error(`provider ${settings.provider} 需要按上游装配(PiAgentRuntimeOptions.upstream)`);
if (!settings.apiKey) throw new Error("未配置上游密钥");
throw new Error(`provider ${settings.provider} 需要按模型服务装配(PiAgentRuntimeOptions.upstream)`);
if (!settings.apiKey) throw new Error("未配置模型服务密钥");
const providerId = PROVIDER_ID[settings.provider];
const common = {
id: settings.model,
@@ -49,7 +49,7 @@ export function resolveModelFromSettings(settings: ResolvedAgentSettings): Resol
models.setProvider(createProvider({ id: providerId, auth, models: [m], api: anthropicMessagesApi() }));
model = m;
} else {
if (!settings.baseUrl) throw new Error("openai-compatible 上游必须配置 baseUrl(带 /v1)");
if (!settings.baseUrl) throw new Error("openai-compatible 模型服务必须配置 baseUrl(带 /v1)");
const m: Model<"openai-completions"> = {
...common,
api: "openai-completions",
+2 -2
View File
@@ -400,7 +400,7 @@ class PiAgentRun implements AgentRunHandle {
const err = record && "error" in record ? record.error : undefined;
outcome = this.failed("error", redactString(err ? `${err.code}: ${err.message}` : "运行失败"));
} else if (status === "suspended") {
outcome = this.failed("error", "上游进入延迟模式(deferred),v0.1 不支持");
outcome = this.failed("error", "模型服务进入延迟模式(deferred),v0.1 不支持");
} else {
outcome = { status: "completed", finalText: this.finalText, usage: this.usage };
this.emit({ type: "run_completed", finalText: outcome.finalText, usage: this.usage });
@@ -540,7 +540,7 @@ export function toolContent(
if (!vision)
return [
{ type: "text", text },
{ type: "text", text: "(截图未发送:当前上游模型不支持图片输入;用 browser.snapshot 读取页面文字)" },
{ type: "text", text: "(截图未发送:当前模型不支持图片输入;用 browser.snapshot 读取页面文字)" },
];
return [
{ type: "text", text },
+2 -2
View File
@@ -329,7 +329,7 @@ export class PiUpstreamClient {
private pickModel(modelId: string | null | undefined): { model: Model<Api>; inCatalog: boolean } {
const all = this.models.getModels(this.providerId);
const id = modelId ?? this.spec.model ?? all[0]?.id;
if (!id) throw new UpstreamError("model", "该上游没有可用模型,请填写模型 id");
if (!id) throw new UpstreamError("model", "该模型服务没有可用模型,请填写模型 id");
const found = this.models.getModel(this.providerId, id);
if (found) return { model: found, inCatalog: true };
const template = all[0];
@@ -369,7 +369,7 @@ export class PiUpstreamClient {
throw classifyUpstreamError(err);
}
if (res.stopReason === "error" || res.stopReason === "aborted")
throw classifyUpstreamError(new Error(res.errorMessage ?? "上游返回错误"));
throw classifyUpstreamError(new Error(res.errorMessage ?? "模型服务返回错误"));
}
return { source, model: model.id, inCatalog };
}
+27 -27
View File
@@ -718,7 +718,7 @@ export const Api = {
path: "/subscriptions/{id}/coverage",
scope: "read",
summary:
"覆盖矩阵:季 → 集 × 版本目标(电影:内容版本 × 版本目标),每格 have/inflight/missing/skipped;缓存 30s,refresh=true 强制重算",
"覆盖矩阵:季 → 集 × 收录版本(电影:内容版本 × 收录版本),每格 have/inflight/missing/skipped;缓存 30s,refresh=true 强制重算",
params: IdParam,
query: z.object({ refresh: z.enum(["true", "false"]).optional() }),
response: policy.SubscriptionCoverage,
@@ -759,7 +759,7 @@ export const Api = {
method: "post",
path: "/subscriptions/{id}/slots/{slotId}/fill",
scope: "admin",
summary: "手动补全某版本目标(不受自动补全开关与补全预算限制)",
summary: "手动补全某收录版本(不受自动补全开关与补全预算限制)",
params: z.object({ id: Id, slotId: Id }),
body: policy.SlotFillRequest,
response: z.object({ runId: z.string(), deduplicated: z.boolean() }),
@@ -768,7 +768,7 @@ export const Api = {
method: "put",
path: "/subscriptions/{id}/slot-order",
scope: "admin",
summary: "版本目标排序(第一个为主版本)",
summary: "收录版本排序(第一个为主版本)",
params: IdParam,
body: policy.SlotOrderSet,
response: z.array(policy.AcquisitionSlot),
@@ -785,7 +785,7 @@ export const Api = {
method: "get",
path: "/subscriptions/{id}/progress",
scope: "read",
summary: "订阅进度:季总集数账本、每集状态(已覆盖/在途/缺失/疑似缺集)、版本目标在途与延后",
summary: "订阅进度:季总集数账本、每集状态(已覆盖/在途/缺失/疑似缺集)、收录版本在途与延后",
params: IdParam,
response: policy.SubscriptionProgress,
}),
@@ -848,7 +848,7 @@ export const Api = {
},
acquisitionSlots: crud(
"/acquisition-slots",
"版本目标",
"收录版本",
{
row: policy.AcquisitionSlot,
create: policy.AcquisitionSlotCreate,
@@ -1226,7 +1226,7 @@ export const Api = {
method: "get",
path: "/agent/runs",
scope: "read",
summary: "Agent 运行列表(新→旧)",
summary: "智能体运行列表(新→旧)",
query: agent.AgentRunListQuery,
response: cursorPage(agent.AgentRun),
}),
@@ -1234,7 +1234,7 @@ export const Api = {
method: "get",
path: "/agent/runs/{id}",
scope: "read",
summary: "Agent 运行详情:事件按 seq 分页、追加消息、服务端核查的目标汇报",
summary: "智能体运行详情:事件按 seq 分页、追加消息、服务端核查的目标汇报",
params: IdParam,
query: agent.AgentRunDetailQuery,
response: agent.AgentRunDetail,
@@ -1616,7 +1616,7 @@ export const Api = {
method: "get",
path: "/capabilities/stats",
scope: "read",
summary: "能力调用统计:每能力调用数、失败数、平均耗时、最近调用、调用来源(内置 Agent / MCP / REST)",
summary: "能力调用统计:每能力调用数、失败数、平均耗时、最近调用、调用来源(内置智能体 / MCP / REST)",
query: mcp.CapabilityStatsQuery,
response: mcp.CapabilityStatsResponse,
}),
@@ -1774,14 +1774,14 @@ export const Api = {
method: "get",
path: "/pi/upstreams",
scope: "admin",
summary: "Agent 上游列表(不含密钥)",
summary: "模型服务列表(不含密钥)",
response: pi.PiUpstreamList,
}),
get: ep({
method: "get",
path: "/pi/upstreams/{id}",
scope: "admin",
summary: "Agent 上游详情",
summary: "模型服务详情",
params: IdParam,
response: pi.PiUpstream,
}),
@@ -1790,7 +1790,7 @@ export const Api = {
path: "/pi/upstreams",
scope: "admin",
auth: "session",
summary: "新建 Agent 上游(apiKey 只写,存 secret 表)",
summary: "新建模型服务(apiKey 只写,存 secret 表)",
body: pi.PiUpstreamCreate,
response: pi.PiUpstream,
}),
@@ -1799,7 +1799,7 @@ export const Api = {
path: "/pi/upstreams/{id}",
scope: "admin",
auth: "session",
summary: "修改 Agent 上游(provider 不可改;makeDefault 设默认上游)",
summary: "修改模型服务(provider 不可改;makeDefault 设默认模型服务)",
params: IdParam,
body: pi.PiUpstreamUpdate,
response: pi.PiUpstream,
@@ -1809,7 +1809,7 @@ export const Api = {
path: "/pi/upstreams/{id}",
scope: "admin",
auth: "session",
summary: "删除 Agent 上游(连同凭据;是默认上游时清空默认)",
summary: "删除模型服务(连同凭据;是默认模型服务时清空默认)",
params: IdParam,
response: OkResult,
}),
@@ -1818,7 +1818,7 @@ export const Api = {
path: "/pi/upstreams/{id}/test",
scope: "admin",
auth: "session",
summary: "测试上游:默认只解析凭据与校验模型(不产生费用);mode=completion 发一次最小补全",
summary: "测试模型服务:默认只解析凭据与校验模型(不产生费用);mode=completion 发一次最小补全",
params: IdParam,
body: pi.PiUpstreamTestRequest,
response: pi.PiUpstreamTestResult,
@@ -1827,7 +1827,7 @@ export const Api = {
method: "get",
path: "/pi/upstreams/{id}/models",
scope: "admin",
summary: "上游模型列表:已获取过远程列表时返回远程与静态目录的合并(远程优先),否则静态目录",
summary: "模型列表:已获取过远程列表时返回远程与静态目录的合并(远程优先),否则静态目录",
params: IdParam,
response: pi.PiUpstreamModels,
}),
@@ -1836,7 +1836,7 @@ export const Api = {
path: "/pi/upstreams/{id}/models/refresh",
scope: "admin",
auth: "session",
summary: "获取模型列表:用该上游的凭证调 provider 的模型列表接口并存库;失败或不支持时回静态目录并给出原因",
summary: "获取模型列表:用该模型服务的凭证调 provider 的模型列表接口并存库;失败或不支持时回静态目录并给出原因",
params: IdParam,
response: pi.PiUpstreamModels,
}),
@@ -1845,7 +1845,7 @@ export const Api = {
path: "/pi/models/probe",
scope: "admin",
auth: "session",
summary: "新建上游前试拉模型列表(凭证只用于这一次请求,不落库)",
summary: "新建模型服务前试拉模型列表(凭证只用于这一次请求,不落库)",
body: pi.PiModelsProbeRequest,
response: pi.PiUpstreamModels,
}),
@@ -2111,7 +2111,7 @@ export const Api = {
method: "get",
path: "/identity-pool",
scope: "read",
summary: "待识别池(可按原始标题检索;候选不建正式作品)",
summary: "未识别资源(可按原始标题检索;候选不建正式作品)",
query: identity.IdentityPoolQuery,
response: cursorPage(identity.IdentityPoolItem),
}),
@@ -2119,7 +2119,7 @@ export const Api = {
method: "post",
path: "/identity-pool/{id}/assign",
scope: "organize.execute",
summary: "为待识别资源手动指定候选 / 本地作品 / 外部身份",
summary: "为未识别资源手动指定候选 / 本地作品 / 外部身份",
params: IdParam,
body: identity.ReleaseIdentityAssign,
response: ReleaseIdentityResult,
@@ -2128,7 +2128,7 @@ export const Api = {
method: "post",
path: "/identity-pool/{id}/dismiss",
scope: "organize.execute",
summary: "忽略待识别资源(不再自动复查)",
summary: "忽略未识别资源(不再自动复查)",
params: IdParam,
body: z.object({ note: z.string().max(2000).optional() }),
response: identity.IdentityCandidate,
@@ -2156,7 +2156,7 @@ export const Api = {
path: "/identity-pool/{id}/enrich/apply",
scope: "admin",
auth: "session",
summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联待识别资源(只接受控制台管理员会话)",
summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联未识别资源(只接受控制台管理员会话)",
params: IdParam,
body: identity.EnrichApplyInput,
response: identity.EnrichApplyResult,
@@ -2177,7 +2177,7 @@ export const Api = {
method: "get",
path: "/library/{id}",
scope: "read",
summary: "作品详情:实际路径、身份映射、元数据来源、各版本目标、候选拒绝理由、下载历史与校验结果",
summary: "作品详情:实际路径、身份映射、元数据来源、各收录版本、候选拒绝理由、下载历史与校验结果",
params: IdParam,
response: organization.LibraryDetail,
}),
@@ -2195,7 +2195,7 @@ export const Api = {
method: "get",
path: "/inventory",
scope: "read",
summary: "库存叠加(站点工作台):按作品返回已有版本 / 版本目标缺口 / 下载中",
summary: "库存叠加(站点工作台):按作品返回已有版本 / 收录版本缺口 / 下载中",
query: organization.InventoryQuery,
response: organization.InventoryResponse,
}),
@@ -2472,7 +2472,7 @@ export const Api = {
method: "post",
path: "/browser/sessions/{id}/control/release",
scope: "admin",
summary: "交还控制权:Agent 恢复前必须重新读取页面,旧元素引用失效",
summary: "交还控制权:智能体恢复前必须重新读取页面,旧元素引用失效",
params: browser.BrowserSessionParams,
body: browser.BrowserControlRelease,
response: browser.BrowserControlResult,
@@ -2486,7 +2486,7 @@ export const Api = {
path: "/schedules",
scope: "admin",
auth: "session",
summary: "定时任务看板:全部登记的周期任务与 Agent 巡检、统计、调度器是否在线",
summary: "定时任务看板:全部登记的周期任务与智能体巡检、统计、调度器是否在线",
response: schedules.ScheduleBoard,
}),
create: ep({
@@ -2494,7 +2494,7 @@ export const Api = {
path: "/schedules",
scope: "admin",
auth: "session",
summary: "新建自定义 Agent 巡检(目标模板 + 能力白名单 + 预算 + 间隔)",
summary: "新建自定义智能体巡检(目标模板 + 能力白名单 + 预算 + 间隔)",
body: schedules.AgentCheckCreate,
response: schedules.ScheduledJob,
}),
@@ -2513,7 +2513,7 @@ export const Api = {
path: "/schedules/{id}",
scope: "admin",
auth: "session",
summary: "删除自定义 Agent 巡检(内置任务不可删)",
summary: "删除自定义智能体巡检(内置任务不可删)",
params: schedules.ScheduleIdParam,
response: OkResult,
}),
+1 -1
View File
@@ -256,7 +256,7 @@ export const SubscriptionBrief = z.object({
status: SubscriptionStatus,
priority: AcquisitionPriority,
statusReason: z.string().nullable(),
/** 版本目标:总数 / 已满足数 */
/** 收录版本:总数 / 已满足数 */
slots: z.object({ total: NonNegInt, satisfied: NonNegInt }),
lastSearchedAt: z.string().nullable(),
nextSearchAt: z.string().nullable(),
+5 -5
View File
@@ -133,7 +133,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"catalog.lookup": d(
"catalog.lookup",
"查作品",
"按标题/别名/外部 ID 查本地作品与元数据候选。确认作品身份、拿作品 ID 时用;查库里已有文件与版本目标用 inventory.query",
"按标题/别名/外部 ID 查本地作品与元数据候选。确认作品身份、拿作品 ID 时用;查库里已有文件与收录版本用 inventory.query",
"read",
"CatalogLookup",
"read",
@@ -177,7 +177,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"download.plan": d(
"download.plan",
"生成下载计划",
"评估候选,生成占用版本目标(slot)与预算预留的下载计划;不提交。选定资源后、提交前用;不会提交下载,也不预留版本目标",
"评估候选,生成占用收录版本(slot)与预算预留的下载计划;不提交。选定资源后、提交前用;不会提交下载,也不预留收录版本",
"preview",
"DownloadPlan",
"organize.execute",
@@ -199,7 +199,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"download.recheck": d(
"download.recheck",
"重新校验/恢复下载",
"对已纳管的下载任务强制重新校验(action=recheck),或恢复非阈值、非人工的暂停(action=resume);不换源、不删除、不改保存路径。下载失败或文件缺失时用;换源、删除不在此能力内",
"对本工具管理的下载任务强制重新校验(action=recheck),或恢复非阈值、非人工的暂停(action=resume);不换源、不删除、不改保存路径。下载失败或文件缺失时用;换源、删除不在此能力内",
"execute",
"DownloadRecheck",
"organize.execute",
@@ -287,7 +287,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"inventory.query": d(
"inventory.query",
"查库存",
"查询本地资产、版本目标满足情况与外部库存证据。查某个作品有哪些文件与版本目标时用;想盘点全部就不给 query,不要用单字母探查",
"查询本地资产、收录版本满足情况与外部库存证据。查某个作品有哪些文件与收录版本时用;想盘点全部就不给 query,不要用单字母探查",
"read",
"InventoryQuery",
"read",
@@ -299,7 +299,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"subscription.list": d(
"subscription.list",
"列出订阅",
"按状态或媒体类型分组列出订阅摘要(作品、模式、状态、版本目标满足数、上次/下次搜索)。盘点订阅、找缺版本时用;查单个作品的文件与版本目标用 inventory.query",
"按状态或媒体类型分组列出订阅摘要(作品、模式、状态、收录版本满足数、上次/下次搜索)。盘点订阅、找缺版本时用;查单个作品的文件与收录版本用 inventory.query",
"read",
"SubscriptionList",
"read",
+1 -1
View File
@@ -489,7 +489,7 @@ export const Dashboard = z.object({
}),
activeSubscriptions: NonNegInt,
slotCounts: SlotStatusCounts,
/** 版本目标满足率 = satisfied / 全部版本目标(0–1);没有版本目标时为 null */
/** 收录版本满足率 = satisfied / 全部收录版本(0–1);没有收录版本时为 null */
slotSatisfiedRate: z.number().min(0).max(1).nullable(),
/** 本地账本计数:作品不含已合并;季 = content_unit.kind=season(不含 retired);资产 = active 的视频 / 原盘目录 */
library: z.object({
+2 -2
View File
@@ -245,9 +245,9 @@ export const PiUpstreamCreate = z
})
.superRefine((v, ctx) => {
if (v.authKind !== "api_key" && v.apiKey !== undefined)
ctx.addIssue({ code: "custom", path: ["apiKey"], message: "只有 api_key 型上游可以填 apiKey" });
ctx.addIssue({ code: "custom", path: ["apiKey"], message: "只有 api_key 型模型服务可以填 apiKey" });
if (v.authKind === "oauth" && v.baseUrl)
ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "订阅登录型上游不能覆盖 baseUrl" });
ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "订阅登录型模型服务不能覆盖 baseUrl" });
if (v.provider === "openai-compatible") {
if (!v.baseUrl) ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "自定义兼容端点必须填 baseUrl" });
if (!v.defaultModel)
+12 -12
View File
@@ -79,7 +79,7 @@ export const Subscription = EntityBase.extend({
/** 上次主动搜索时间 / 下次到期(按 params.searchIntervalHours,缺省 24h) */
lastSearchedAt: Timestamp.nullable(),
nextSearchAt: Timestamp.nullable(),
/** 上次进展:任一版本目标的单元转为已入库、或总集数变化时写入;总览"上次更新"另合并计划预留时间 */
/** 上次进展:任一收录版本的单元转为已入库、或总集数变化时写入;总览"上次更新"另合并计划预留时间 */
lastProgressAt: Timestamp.nullable(),
/** 派生字段(列表返回):作品标题,未建档时为 null */
mediaEntityTitle: z.string().nullable().optional(),
@@ -97,10 +97,10 @@ export const FillBudget = z
.strict();
export type FillBudget = z.infer<typeof FillBudget>;
/** MP 分辨率(resolution)→ 版本目标硬条件 resolutions */
/** MP 分辨率(resolution)→ 收录版本硬条件 resolutions */
export const SubscriptionResolution = z.enum(["4k", "1080p", "720p"]);
export type SubscriptionResolution = z.infer<typeof SubscriptionResolution>;
/** MP 资源质量(quality / resource_type)→ 版本目标硬条件 sources / codecs;uhd 写进订阅私有规则组的包含 */
/** MP 资源质量(quality / resource_type)→ 收录版本硬条件 sources / codecs;uhd 写进订阅私有规则组的包含 */
export const SubscriptionSourcePreset = z.enum([
"bluray_disc",
"remux",
@@ -112,7 +112,7 @@ export const SubscriptionSourcePreset = z.enum([
"h264",
]);
export type SubscriptionSourcePreset = z.infer<typeof SubscriptionSourcePreset>;
/** MP 特效(effect)→ 版本目标硬条件 hdr + 软偏好 hdr 顺序;atmos 写进订阅私有规则组的包含 */
/** MP 特效(effect)→ 收录版本硬条件 hdr + 软偏好 hdr 顺序;atmos 写进订阅私有规则组的包含 */
export const SubscriptionEffect = z.enum(["dv", "hdr", "sdr", "atmos"]);
export type SubscriptionEffect = z.infer<typeof SubscriptionEffect>;
@@ -120,7 +120,7 @@ export type SubscriptionEffect = z.infer<typeof SubscriptionEffect>;
* subscription.params 的已知键(JSON 列,其余键原样保留):
* siteIds 站点范围;episodeRange 集范围(对照 MP start/total_episode);searchIntervalHours 主动搜索周期(0 = 关);
* stopAfter 截止时间;downloaderInstanceId 指定下载器;ruleGroupIds 规则组;keywords 追加检索词;
* include/exclude 订阅级关键词正则(作用于全部版本目标);libraryRootId 库根;downloadSavePath 下载保存路径;
* include/exclude 订阅级关键词正则(作用于全部收录版本);libraryRootId 库根;downloadSavePath 下载保存路径;
* autoFill/fillBudget 多版本自动补全;resolution/source/effect 质量预设(MP resolution/quality/effect)。
* include/exclude 保存时同步为订阅私有规则组(scope.subscriptionId,随订阅删除),评估只读规则组。
* 写入时兼容 MP 字段与旧键(sites/downloader/save_path/start_episode/filter_groups/keyword/autoFillVersions…),
@@ -150,7 +150,7 @@ export const SubscriptionParams = z
downloadSavePath: z.string().min(1).max(1000).optional(),
autoFill: AutoFillMode.optional(),
fillBudget: FillBudget.optional(),
/** MP resolution / quality / effect:保存时写到该订阅自己的版本目标(硬条件 + 软偏好) */
/** MP resolution / quality / effect:保存时写到该订阅自己的收录版本(硬条件 + 软偏好) */
resolution: SubscriptionResolution.optional(),
source: SubscriptionSourcePreset.optional(),
effect: SubscriptionEffect.optional(),
@@ -296,11 +296,11 @@ export type SubscriptionGroup = z.infer<typeof SubscriptionGroup>;
export const SubscriptionSort = z.enum(["lastUpdated", "nextSearch", "missing", "created", "name"]);
export type SubscriptionSort = z.infer<typeof SubscriptionSort>;
/** 版本目标在订阅范围内的计数摘要(卡片徽标 / 矩阵行尾) */
/** 收录版本在订阅范围内的计数摘要(卡片徽标 / 矩阵行尾) */
export const SubscriptionSlotSummary = z.object({
slotId: Id,
name: z.string(),
/** 同订阅内顺序最小的版本目标 */
/** 同订阅内顺序最小的收录版本 */
primary: z.boolean(),
status: z.string(),
have: z.number().int(),
@@ -347,7 +347,7 @@ export const SubscriptionOverviewItem = Subscription.extend({
source: z.enum(["subscription", "rule_group", "default"]),
budget: FillBudget,
}),
/** 任一版本目标开了升级且未达顶档 */
/** 任一收录版本开了升级且未达顶档 */
upgrading: z.boolean(),
/** 已完成分组里 continuous 订阅在等下一季 */
awaitingNewSeason: z.boolean(),
@@ -418,7 +418,7 @@ export const SubscriptionCoverage = z.object({
columns: z.array(CoverageColumn),
}),
),
/** 首行 slotId = null 为"库内任一版本";其后每个版本目标一行,按 position */
/** 首行 slotId = null 为"库内任一版本";其后每个收录版本一行,按 position */
rows: z.array(
z.object({
slotId: Id.nullable(),
@@ -714,7 +714,7 @@ export type PreferenceSuppressionUpdate = z.infer<typeof PreferenceSuppressionUp
/** Slot 模板:策略规则里用于自动建 Slot(新作品收录 / 订阅缺省 Slot);字段同 AcquisitionSlot 规则列 */
export const SlotTemplate = z.object({
name: z.string().min(1).max(64),
/** 建版本目标时写入 ruleGroupIds */
/** 建收录版本时写入 ruleGroupIds */
ruleGroupIds: z.array(Id).optional(),
hardConditions: JsonObject.optional(),
softPreferences: JsonObject.optional(),
@@ -850,7 +850,7 @@ export const RuleGroup = EntityBase.extend({
/** 语义指纹(kind + scope + 层 + 硬排除),停用/删除后写抑制记录 */
fingerprint: z.string(),
evidence: JsonObject,
/** 结构化硬条件(include / exclude 正则等),评估时并入版本目标硬条件;订阅私有组用它存表单的包含 / 排除 */
/** 结构化硬条件(include / exclude 正则等),评估时并入收录版本硬条件;订阅私有组用它存表单的包含 / 排除 */
hard: JsonObject.optional(),
/** 非 null = 属于该类默认链,按此排序 */
defaultOrder: z.number().int().nullable(),
+4 -4
View File
@@ -37,7 +37,7 @@ export const LocalMediaHit = z.object({
matchedBy: z.enum(["exact", "prefix", "similar"]),
/** 命中的别名原文(可能是译名/原名/别名) */
matchedAlias: z.string().nullable(),
/** 版本目标状态摘要 + 库存 */
/** 收录版本状态摘要 + 库存 */
slotCounts: SlotStatusCounts,
versionCount: NonNegInt,
downloadingCount: NonNegInt,
@@ -79,7 +79,7 @@ export type SearchReleaseParsed = z.infer<typeof SearchReleaseParsed>;
/**
* 库存叠加状态(只在关联到作品时给出):
* downloading 有下载中任务 > gap 有未满足的版本目标 > owned 已有版本 > subscribed 只有订阅 > none 作品已登记但无库存/目标
* downloading 有下载中任务 > gap 有未满足的收录版本 > owned 已有版本 > subscribed 只有订阅 > none 作品已登记但无库存/目标
*/
export const SearchInventoryState = z.enum(["downloading", "gap", "owned", "subscribed", "none"]);
export type SearchInventoryState = z.infer<typeof SearchInventoryState>;
@@ -188,10 +188,10 @@ export const WorkInventory = z.object({
/** 已入库版本数(主视频资产) */
versionCount: NonNegInt,
downloadingCount: NonNegInt,
/** 版本目标:总数 / 已满足 */
/** 收录版本:总数 / 已满足 */
slotTotal: NonNegInt,
slotSatisfied: NonNegInt,
/** 未满足的版本目标(empty / attention / upgrade_pending) */
/** 未满足的收录版本(empty / attention / upgrade_pending) */
missing: z.array(z.object({ slotId: Id, name: z.string() })),
});
export type WorkInventory = z.infer<typeof WorkInventory>;
+5 -5
View File
@@ -864,10 +864,10 @@ export const SETTINGS_SECTIONS: readonly SettingsSectionDef[] = [
{
id: "library",
label: "媒体库与目录",
description: "库根、哨兵状态、默认整理方式、命名模板",
description: "媒体库目录、默认整理方式、命名模板",
legacyTabs: [{ tab: "library-roots", anchor: "roots" }],
anchors: [
{ id: "roots", label: "库根" },
{ id: "roots", label: "媒体库目录" },
{ id: "organize", label: "整理方式" },
{ id: "naming", label: "命名模板" },
],
@@ -918,11 +918,11 @@ export const SETTINGS_SECTIONS: readonly SettingsSectionDef[] = [
},
{
id: "agent",
label: "Agent",
description: "Agent 上游、运行预算、自动调查、网页搜索",
label: "智能体",
description: "模型服务、运行预算、自动调查、网页搜索",
legacyTabs: [{ tab: "pi" }],
anchors: [
{ id: "upstream", label: "上游" },
{ id: "upstream", label: "模型服务" },
{ id: "runtime", label: "上下文与子会话" },
{ id: "budget", label: "预算" },
{ id: "investigate", label: "自动调查" },
+1 -1
View File
@@ -1,4 +1,4 @@
// 多版本自动补全(docs/design/subscriptions.md §2.2):非主版本目标在主版本已满足的单元上补下其他版本。
// 多版本自动补全(docs/design/subscriptions.md §2.2):非主收录版本在主版本已满足的单元上补下其他版本。
// 开关按 订阅 → 规则组(链上后者优先)→ 系统缺省(开)取值;补全预算按订阅、按自然日计,超出记延后。
export type AutoFillModeValue = "inherit" | "on" | "off";
+2 -2
View File
@@ -76,7 +76,7 @@ export const NAMING_VARIABLES: readonly NamingVariableDef[] = [
ours: true,
},
{ name: "issue", group: "ours", label: "综艺期号", example: "12", ours: true },
{ name: "slotName", group: "ours", label: "版本目标名", example: "4K HDR", ours: true },
{ name: "slotName", group: "ours", label: "收录版本名", example: "4K HDR", ours: true },
{ name: "libraryRoot", group: "ours", label: "库根名", example: "电视剧-A", ours: true },
];
@@ -124,7 +124,7 @@ function sourceOf(p: Partial<ParsedRelease>): string | undefined {
}
/**
* 渲染上下文(MP 变量名):作品信息 + 发布解析 + 季集元数据 + 版本目标/库根。
* 渲染上下文(MP 变量名):作品信息 + 发布解析 + 季集元数据 + 收录版本/库根。
* 标题用本地作品名,不用发布标题。空值不放进上下文(同 MP 剔除 None)。
*/
export function buildNamingVars(
+2 -2
View File
@@ -376,14 +376,14 @@ export function currentPri(spec: SlotSpec, current: QualityFacts): number {
return effectiveLadder(spec, pseudo, {}, ruleChainOf(spec, pseudo, {})).pri;
}
/** 版本目标引用的规则组链求值;没有规则组时为 null */
/** 收录版本引用的规则组链求值;没有规则组时为 null */
export function ruleChainOf(spec: SlotSpec, r: ParsedRelease, ctx: EvaluationContext = {}): RuleChainResult | null {
if (!spec.rules?.groups.length) return null;
return evaluateRuleChain(spec.rules.groups, r, ctx, catalogOf(spec.rules.atoms));
}
/**
* 实际生效的阶梯:版本目标自身 soft.tiers 视为排在最前的匿名组,规则组链里有带层的组时 pri 取最后一个带层的组。
* 实际生效的阶梯:收录版本自身 soft.tiers 视为排在最前的匿名组,规则组链里有带层的组时 pri 取最后一个带层的组。
*/
export function effectiveLadder(
spec: SlotSpec,
+3 -3
View File
@@ -11,9 +11,9 @@ export interface SubscriptionGroupInput {
mode: "once" | "continuous";
/** 范围内各季总集数是否都已知(电影恒为 true) */
totalsKnown: boolean;
/** 任一版本目标有已入库或在途单元 */
/** 任一收录版本有已入库或在途单元 */
anyProgress: boolean;
/** 所有版本目标在范围内全覆盖(且总集数已知) */
/** 所有收录版本在范围内全覆盖(且总集数已知) */
allSlotsComplete: boolean;
}
@@ -86,7 +86,7 @@ export interface CoverageRowResult {
}
/**
* 一行(一个版本目标或"库内任一版本")的格状态:已入库 > 在途(含整季通配预留)> 范围外/未播出 > 缺失。
* 一行(一个收录版本或"库内任一版本")的格状态:已入库 > 在途(含整季通配预留)> 范围外/未播出 > 缺失。
* 在途按计划单元键与列键求交(unitsOverlap:s{季}* 通配与同季各集相交)。
*/
export function coverageRow(i: CoverageRowInput): CoverageRowResult {
+1 -1
View File
@@ -116,7 +116,7 @@ export interface SlotUpgradePolicy {
export type SlotCompletionSpec = { kind: "movie"; cuts?: (Cut | "default")[] } | { kind: "units" };
/** 版本目标引用的规则组链(已按 scope 过滤、按引用顺序排列)与自定义条件 */
/** 收录版本引用的规则组链(已按 scope 过滤、按引用顺序排列)与自定义条件 */
export interface SlotRuleChain {
groups: RuleGroupSpec[];
atoms?: CustomAtom[];
+1 -1
View File
@@ -125,7 +125,7 @@ describe("变量表与上下文", () => {
"libraryRoot",
]);
});
it("季集元数据、版本目标、库根、外部 ID", () => {
it("季集元数据、收录版本、库根、外部 ID", () => {
const v = buildNamingVars(
{
title: "绝命毒师",
+1 -1
View File
@@ -22,7 +22,7 @@ const base = (groups: RuleGroupSpec[], extra: Partial<SlotSpec> = {}): SlotSpec
});
const rel = (t: string, subtitle?: string) => parseRelease(t, subtitle ? { subtitle } : {});
describe("版本目标接入规则组", () => {
describe("收录版本接入规则组", () => {
it("硬排除命中 → reject(rule.hard_exclude),trace 带组名与修订号", () => {
const g = group("不要原盘", "", {
id: "g1",