import { pgTable, text, timestamp, jsonb, integer, index, uniqueIndex, } from "drizzle-orm/pg-core"; /** * Task #242 (B1) — Capability 通用框架的核心表(只登记不调度)。 * * 设计语义: * - `capabilities` 一行 = 一个 capability(ligand_screening / target_discovery / ...) * - `capability_lifecycle_events` 状态转换审计(B5 才真正驱动 evolution) * - `capability_inner_loop_runs` 内循环执行轨迹(只记录,B5 才调度) * - `capability_data_foundation` 自产数据基座(B2 已建,本任务保留) * - `capability_external_knowledge_calls` 6 类适配器调用 telemetry(B2 已建) * - `network_dispatch_violations` input_contract ajv 校验未通过的行(本任务漏洞 2) * * 重要:本任务 5 个抽象骨架模块(registry/lifecycle/inner-loop/data-foundation/ * external-knowledge)只接口不实现;真实现在 B2/B5/B7。这些表在 B1 落地是 * 为了让"接口骨架返回值"有地方写,避免后续骨架接口改动连带 schema 漂移。 */ // -------------------------------------------------------- capabilities export const capabilities = pgTable( "capabilities", { id: text("id").primaryKey(), /** 稳定 handle,例如 "ligand_screening"。UNIQUE。 */ name: text("name").notNull().unique(), /** 业务域,例如 "drug_discovery" / "biology" / "ml_systems"。 */ domain: text("domain").notNull(), description: text("description").notNull().default(""), /** Lifecycle:'created' | 'cold_start' | 'graduated' | 'live' | 'degraded'。 */ currentLifecycleState: text("current_lifecycle_state") .notNull() .default("created"), /** 关联到 tool_networks.name(向后兼容期 capability 复用旧 network 执行)。 */ boundNetworkName: text("bound_network_name"), /** Free-form 元数据(B2/B5/B7 各自往里塞)。 */ meta: jsonb("meta").notNull().default({}), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byDomain: index("capabilities_domain_idx").on(t.domain), byState: index("capabilities_state_idx").on(t.currentLifecycleState), }), ); export type CapabilityRow = typeof capabilities.$inferSelect; export type InsertCapabilityRow = typeof capabilities.$inferInsert; // -------------------------------------------------------- lifecycle events export const capabilityLifecycleEvents = pgTable( "capability_lifecycle_events", { id: text("id").primaryKey(), capabilityId: text("capability_id").notNull(), fromState: text("from_state"), toState: text("to_state").notNull(), /** 'manual' | 'auto_promote' | 'auto_rollback' | 'cold_start_complete' | ... */ reason: text("reason").notNull(), payload: jsonb("payload").notNull().default({}), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byCapability: index("capability_lifecycle_events_cap_idx").on( t.capabilityId, ), byCreatedAt: index("capability_lifecycle_events_created_at_idx").on( t.createdAt, ), }), ); export type CapabilityLifecycleEventRow = typeof capabilityLifecycleEvents.$inferSelect; export type InsertCapabilityLifecycleEventRow = typeof capabilityLifecycleEvents.$inferInsert; // -------------------------------------------------------- inner-loop runs export const capabilityInnerLoopRuns = pgTable( "capability_inner_loop_runs", { id: text("id").primaryKey(), capabilityId: text("capability_id").notNull(), /** * `[{ step: 'observe' | 'diagnose' | 'plan' | 'search' | 'code' | 'act' | * 'judge' | 'fix', input, output, durationMs, llmRole? }]`。 * 真 schema 在 B5 落 ddl 注释 + 类型(本任务不约束)。 */ stepsJson: jsonb("steps_json").notNull(), sourceRunId: text("source_run_id"), durationMs: integer("duration_ms"), /** 'ok' | 'aborted' | 'failed' */ status: text("status").notNull().default("ok"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byCapability: index("capability_inner_loop_runs_cap_idx").on( t.capabilityId, ), byCreatedAt: index("capability_inner_loop_runs_created_at_idx").on( t.createdAt, ), }), ); export type CapabilityInnerLoopRunRow = typeof capabilityInnerLoopRuns.$inferSelect; export type InsertCapabilityInnerLoopRunRow = typeof capabilityInnerLoopRuns.$inferInsert; // -------------------------------------------------------- data foundation // // 已在 B2 (#255) 建表;保留原 schema 不动以维持向后兼容。本任务的 // `capability/data-foundation/index.ts` 抽象层会读这张表。 export const capabilityDataFoundation = pgTable( "capability_data_foundation", { id: text("id").primaryKey(), capabilityId: text("capability_id").notNull(), kind: text("kind").notNull(), payload: jsonb("payload").notNull(), sourceRunId: text("source_run_id"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byCapability: index("capability_data_foundation_capability_idx").on( t.capabilityId, ), byKind: index("capability_data_foundation_kind_idx").on(t.kind), byCapabilityKind: index("capability_data_foundation_cap_kind_idx").on( t.capabilityId, t.kind, ), }), ); export type CapabilityDataFoundationRow = typeof capabilityDataFoundation.$inferSelect; export type InsertCapabilityDataFoundationRow = typeof capabilityDataFoundation.$inferInsert; // -------------------------------------------------------- external-knowledge calls // // 已在 B2 (#255) 建表;保留原 schema 不动。 export const capabilityExternalKnowledgeCalls = pgTable( "capability_external_knowledge_calls", { id: text("id").primaryKey(), adapter: text("adapter").notNull(), capabilityId: text("capability_id"), latencyMs: integer("latency_ms").notNull(), hitCount: integer("hit_count").notNull().default(0), cacheHit: text("cache_hit"), error: text("error"), paramsFingerprint: text("params_fingerprint"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byAdapter: index("capability_ek_calls_adapter_idx").on(t.adapter), byCapability: index("capability_ek_calls_capability_idx").on( t.capabilityId, ), byCreatedAt: index("capability_ek_calls_created_at_idx").on(t.createdAt), }), ); export type CapabilityExternalKnowledgeCallRow = typeof capabilityExternalKnowledgeCalls.$inferSelect; export type InsertCapabilityExternalKnowledgeCallRow = typeof capabilityExternalKnowledgeCalls.$inferInsert; // -------------------------------------------------------- dispatch violations export const networkDispatchViolations = pgTable( "network_dispatch_violations", { id: text("id").primaryKey(), networkName: text("network_name").notNull(), toolName: text("tool_name").notNull(), /** SHA256 前 16 hex of canonical(args) — 不存原始 args 防隐私泄漏。 */ argsHash: text("args_hash").notNull(), /** ajv `errors[]` 全量 JSON。 */ detailsJson: jsonb("details_json").notNull(), /** 'enforced' | 'observed_only'(feature flag 关时只记录不阻断)。 */ mode: text("mode").notNull().default("enforced"), /** 关联会话/用户(诊断用)。 */ conversationId: text("conversation_id"), ownerUserId: text("owner_user_id"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ byNetwork: index("network_dispatch_violations_net_idx").on(t.networkName), byCreatedAt: index("network_dispatch_violations_created_at_idx").on( t.createdAt, ), uniqArgsHashRecent: uniqueIndex( "network_dispatch_violations_uniq_recent", ).on(t.networkName, t.argsHash, t.createdAt), }), ); export type NetworkDispatchViolationRow = typeof networkDispatchViolations.$inferSelect; export type InsertNetworkDispatchViolationRow = typeof networkDispatchViolations.$inferInsert;