import { z } from "zod"; import { AgentRunEventRow } from "./agent-events.ts"; import { KnownCapabilityId } from "./capability.ts"; import { EntityBase, Id, JsonObject, NonNegInt, SERVER_MANAGED, Timestamp } from "./common.ts"; // 人工/AI 域:Agent 运行、证据/计划产物、待处理事项、审批、持续授权(REQ §10、§11.3、§11.4) export const AgentTrigger = z.enum(["user", "event", "schedule", "subagent"]); export type AgentTrigger = z.infer; export const AgentRunStatus = z.enum([ "queued", "running", "waiting", "succeeded", "failed", "cancelled", "budget_exceeded", ]); export type AgentRunStatus = z.infer; /** execute:授权范围内边执行边汇报;plan_only:"先给方案",只允许 read/preview/propose 类能力 */ export const AgentRunMode = z.enum(["execute", "plan_only"]); export type AgentRunMode = z.infer; /** 单次运行预算(调用次数/token/时长/费用,REQ §10.5) */ export const AgentBudget = z.object({ maxToolCalls: z.number().int().positive().optional(), maxTokens: z.number().int().positive().optional(), maxDurationSec: z.number().int().positive().optional(), maxCostUsd: z.number().nonnegative().optional(), }); export type AgentBudget = z.infer; export const AgentUsage = z.object({ inputTokens: NonNegInt.optional(), outputTokens: NonNegInt.optional(), cacheReadTokens: NonNegInt.optional(), cacheWriteTokens: NonNegInt.optional(), totalTokens: NonNegInt.optional(), costUsd: z.number().nonnegative().optional(), durationMs: NonNegInt.optional(), }); export type AgentUsage = z.infer; export const AgentRun = EntityBase.extend({ taskId: Id.nullable(), parentRunId: Id.nullable(), trigger: AgentTrigger, goal: z.string().min(1), status: AgentRunStatus, provider: z.string().nullable(), model: z.string().nullable(), thinkingLevel: z.string().nullable(), /** 授权范围(能力白名单、对象范围);子代理继承父任务上限 */ scope: JsonObject, budget: AgentBudget, usage: AgentUsage, evidenceVersion: z.string().nullable(), /** Pi 会话持久化状态;不对普通列表返回 */ sessionState: JsonObject.nullable(), summary: z.string().nullable(), toolCallCount: NonNegInt, lastEventSeq: NonNegInt, error: JsonObject.nullable(), cancelRequestedAt: Timestamp.nullable(), startedAt: Timestamp.nullable(), finishedAt: Timestamp.nullable(), createdBy: z.string().nullable(), mode: AgentRunMode, /** 后台触发按根因去重(如 investigate:);用户委托为 null */ dedupeKey: z.string().nullable(), }); export type AgentRun = z.infer; export const AgentRunCreate = AgentRun.pick({ taskId: true, parentRunId: true, trigger: true, goal: true, scope: true, budget: true, }).partial({ taskId: true, parentRunId: true, trigger: true, scope: true, budget: true }); export type AgentRunCreate = z.infer; export const AgentRunUpdate = AgentRun.pick({ status: true, provider: true, model: true, thinkingLevel: true, usage: true, evidenceVersion: true, sessionState: true, summary: true, toolCallCount: true, lastEventSeq: true, error: true, startedAt: true, finishedAt: true, }).partial(); export type AgentRunUpdate = z.infer; export const ArtifactKind = z.enum(["evidence", "plan", "proposal", "report", "snapshot", "log"]); export type ArtifactKind = z.infer; /** 证据/计划/提案产物:提案不直接晋升为事实 */ export const Artifact = EntityBase.extend({ kind: ArtifactKind, agentRunId: Id.nullable(), taskId: Id.nullable(), subjectType: z.string().nullable(), subjectId: Id.nullable(), title: z.string().min(1), mimeType: z.string(), content: JsonObject, contentHash: z.string().min(1), sizeBytes: NonNegInt.nullable(), expiresAt: Timestamp.nullable(), }); export type Artifact = z.infer; /** contentHash / sizeBytes 由服务端计算 */ export const ArtifactCreate = Artifact.omit({ ...SERVER_MANAGED, contentHash: true, sizeBytes: true }).partial({ agentRunId: true, taskId: true, subjectType: true, subjectId: true, mimeType: true, expiresAt: true, }); export type ArtifactCreate = z.infer; /** 产物内容不可变;只能调整过期时间 */ export const ArtifactUpdate = z.object({ expiresAt: Timestamp.nullable() }); export type ArtifactUpdate = z.infer; export const DecisionKind = z.enum([ "identity_ambiguous", "download_stalled", "download_failed", "path_conflict", "space_insufficient", "approval_required", "metadata_conflict", "source_unreachable", "other", ]); export type DecisionKind = z.infer; export const DecisionStatus = z.enum(["open", "investigating", "awaiting_user", "resolved", "dismissed", "expired"]); export type DecisionStatus = z.infer; export const Severity = z.enum(["info", "warning", "critical"]); export type Severity = z.infer; export const DecisionOption = z.object({ key: z.string().min(1), label: z.string().min(1), description: z.string().optional(), /** 选择后执行的能力与参数(仍经授权校验) */ capabilityId: z.string().optional(), input: JsonObject.optional(), }); export type DecisionOption = z.infer; /** 待处理中心条目;同一 dedupeKey 同时只有一个未关闭条目 */ export const Decision = EntityBase.extend({ kind: DecisionKind, status: DecisionStatus, severity: Severity, title: z.string().min(1), summary: z.string().nullable(), subjectType: z.string().nullable(), subjectId: Id.nullable(), dedupeKey: z.string().min(1), evidence: JsonObject, options: z.array(DecisionOption), resolution: JsonObject.nullable(), taskId: Id.nullable(), agentRunId: Id.nullable(), attempts: NonNegInt, nextReviewAt: Timestamp.nullable(), resolvedBy: z.string().nullable(), resolvedAt: Timestamp.nullable(), }); export type Decision = z.infer; export const DecisionCreate = Decision.pick({ kind: true, severity: true, title: true, summary: true, subjectType: true, subjectId: true, dedupeKey: true, evidence: true, options: true, taskId: true, agentRunId: true, nextReviewAt: true, }).partial({ severity: true, summary: true, subjectType: true, subjectId: true, evidence: true, options: true, taskId: true, agentRunId: true, nextReviewAt: true, }); export type DecisionCreate = z.infer; export const DecisionUpdate = Decision.pick({ status: true, severity: true, summary: true, evidence: true, options: true, agentRunId: true, nextReviewAt: true, }).partial(); export type DecisionUpdate = z.infer; /** 用户处理待处理事项:选一个选项或关闭 */ export const DecisionResolveInput = z.object({ optionKey: z.string().min(1).optional(), dismiss: z.boolean().optional(), note: z.string().max(2000).optional(), }); export type DecisionResolveInput = z.infer; export const ApprovalStatus = z.enum(["pending", "approved", "rejected", "revoked", "expired", "consumed"]); export type ApprovalStatus = z.infer; /** 单次审批:绑定 planType+planId + payloadHash + expectedRevision;换候选/路径后旧审批失效 */ export const Approval = EntityBase.extend({ planType: z.string().min(1), planId: Id, payloadHash: z.string().min(1), expectedRevision: z.number().int().min(0), scope: JsonObject, status: ApprovalStatus, decisionId: Id.nullable(), requestedBy: z.string().min(1), decidedBy: z.string().nullable(), decidedAt: Timestamp.nullable(), expiresAt: Timestamp.nullable(), reason: z.string().nullable(), }); export type Approval = z.infer; export const ApprovalCreate = Approval.pick({ planType: true, planId: true, payloadHash: true, expectedRevision: true, scope: true, decisionId: true, requestedBy: true, expiresAt: true, reason: true, }).partial({ scope: true, decisionId: true, expiresAt: true, reason: true }); export type ApprovalCreate = z.infer; /** 批准/拒绝时回传 payloadHash,服务端比对防止审批被挪用到变更后的计划 */ export const ApprovalUpdate = z.object({ status: z.enum(["approved", "rejected", "revoked"]), payloadHash: z.string().min(1), reason: z.string().max(2000).optional(), }); export type ApprovalUpdate = z.infer; export const GrantStatus = z.enum(["active", "revoked", "expired"]); export type GrantStatus = z.infer; /** 持续授权:绑定策略版本、动作、对象范围与预算 */ export const AutomationGrant = EntityBase.extend({ action: z.string().min(1), policyRevisionId: Id.nullable(), scope: JsonObject, budget: JsonObject, status: GrantStatus, grantedBy: z.string().min(1), reason: z.string().nullable(), expiresAt: Timestamp.nullable(), revokedAt: Timestamp.nullable(), revokedBy: z.string().nullable(), }); export type AutomationGrant = z.infer; export const AutomationGrantCreate = AutomationGrant.pick({ action: true, policyRevisionId: true, scope: true, budget: true, reason: true, expiresAt: true, }).partial({ policyRevisionId: true, scope: true, budget: true, reason: true, expiresAt: true }); export type AutomationGrantCreate = z.infer; /** 只能撤销或缩短有效期,不能就地扩大范围(扩大 = 新授权) */ export const AutomationGrantUpdate = z.object({ status: z.literal("revoked").optional(), expiresAt: Timestamp.nullable().optional(), reason: z.string().max(2000).optional(), }); export type AutomationGrantUpdate = z.infer; // ── Agent 会话 API(W2-D) ── /** POST /agent/runs:建 agent_run + 入队持久任务 runAgent(不在 HTTP 请求里跑模型) */ export const AgentRunStartInput = z.object({ goal: z.string().trim().min(1).max(8000), mode: AgentRunMode.default("execute"), /** 能力白名单(缺省按模式给全部可用能力);plan_only 下超出 read/preview/propose 的会被拒绝 */ capabilityAllowlist: z.array(KnownCapabilityId).min(1).max(50).optional(), }); export type AgentRunStartInput = z.infer; export const AgentRunMessageInput = z.object({ text: z.string().trim().min(1).max(8000) }); export type AgentRunMessageInput = z.infer; export const AgentRunAbortInput = z.object({ reason: z.string().max(500).optional() }); export type AgentRunAbortInput = z.infer; export const AgentRunListQuery = z.object({ cursor: z.string().min(1).optional(), limit: z.coerce.number().int().min(1).max(200).default(50), status: AgentRunStatus.optional(), trigger: AgentTrigger.optional(), }); export type AgentRunListQuery = z.infer; /** 详情里的事件分页:afterSeq(不含)之后按 seq 升序;断线后按最后收到的 runSeq 补读 */ export const AgentRunDetailQuery = z.object({ afterSeq: z.coerce.number().int().min(-1).default(-1), limit: z.coerce.number().int().min(1).max(1000).default(200), }); export type AgentRunDetailQuery = z.infer; /** * 目标完成度汇报:由服务端从业务表汇总(下载计划 → 下载任务 → 整理记录),不采信模型文本。 * "已提交"只代表进入下载器;只有整理记录 succeeded 才算入库。 */ export const AgentGoalReport = z.object({ state: z.enum(["no_actions", "waiting", "completed", "partial", "failed"]), summary: z.string(), submitted: NonNegInt, imported: NonNegInt, inProgress: NonNegInt, failed: NonNegInt, plans: z.array(z.object({ planId: Id, status: z.string(), slotId: Id.nullable(), sourceReleaseId: Id.nullable() })), downloads: z.array( z.object({ downloadTaskId: Id, planId: Id.nullable(), status: z.string(), name: z.string().nullable() }), ), organization: z.array( z.object({ recordId: Id, status: z.string(), downloadTaskId: Id.nullable(), mediaEntityId: Id.nullable(), targetPath: z.string().nullable(), }), ), /** 模型最终文本(仅供参考,不作为完成依据) */ modelText: z.string().nullable(), generatedAt: Timestamp, }); export type AgentGoalReport = z.infer; export const AgentRunMessageStatus = z.enum(["pending", "consumed"]); export type AgentRunMessageStatus = z.infer; /** 追加到会话的用户消息(纠正/补充);runAgent 任务按创建顺序消费 */ export const AgentRunMessage = EntityBase.extend({ runId: Id, text: z.string(), status: AgentRunMessageStatus, createdBy: z.string().nullable(), consumedAt: Timestamp.nullable(), }); export type AgentRunMessage = z.infer; export const AgentRunDetail = z.object({ run: AgentRun, events: z.array(AgentRunEventRow), /** 下一页起点(传给 afterSeq);null 表示已到最新 */ nextAfterSeq: z.number().int().nullable(), messages: z.array(AgentRunMessage), report: AgentGoalReport.nullable(), }); export type AgentRunDetail = z.infer; /** 单次审批的创建:服务端从计划读取 payloadHash / expectedRevision,调用方只给计划 */ export const ApprovalRequest = z.object({ planType: z.enum(["acquisition_plan", "organize_plan", "policy_revision"]), planId: Id, expiresInSec: z .number() .int() .min(60) .max(30 * 86400) .default(86400), reason: z.string().max(2000).optional(), }); export type ApprovalRequest = z.infer; export const CapabilityInvocationStatus = z.enum(["running", "succeeded", "failed"]); export type CapabilityInvocationStatus = z.infer; /** * 有副作用能力的操作账本(capability_invocation):幂等键由服务端生成且唯一;succeeded 直接返回记录的输出, * running(调用中崩溃)按同一幂等键重跑处理器对账。 */ export const CapabilityInvocation = EntityBase.extend({ idempotencyKey: z.string().min(1), capabilityId: z.string().min(1), capabilityVersion: z.number().int().min(1), agentRunId: Id.nullable(), actorId: z.string(), source: z.string(), status: CapabilityInvocationStatus, input: JsonObject, output: JsonObject.nullable(), error: JsonObject.nullable(), grantId: Id.nullable(), approvalId: Id.nullable(), attempts: NonNegInt, finishedAt: Timestamp.nullable(), }); export type CapabilityInvocation = z.infer;