/** * toolbox 的 sys schema 表(编码规范 §7 / ADR-002 / ADR-009)。 */ import { boolean, index, integer, jsonb, primaryKey, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core'; import { sys } from '@shared/db.ts'; export const toolTable = sys.table( 'tool', { /** 中文名 —— 人读的主名(治理台主显示);存量行以逻辑名兜底 */ displayName: text('display_name'), name: text('name').primaryKey(), version: text('version').notNull(), description: text('description').notNull(), input: jsonb('input').notNull(), output: jsonb('output').notNull(), effect: text('effect').notNull(), errors: jsonb('errors').notNull().default([]), idempotent: boolean('idempotent').notNull().default(false), timeoutMs: integer('timeout_ms').notNull().default(30_000), retries: integer('retries').notNull().default(1), connectionKeys: jsonb('connection_keys').notNull().default([]), capabilities: jsonb('capabilities'), /** 面向人的作业指导段 —— 转人时照抄上卡,不临场让模型编步骤(§6.2) */ guide: jsonb('guide'), pure: boolean('pure').notNull().default(false), ownerActivityId: text('owner_activity_id'), /** 凭证隔离:值加密落库,不入包、不入活动、不入留痕明文(ADR-009) */ connectionCipher: jsonb('connection_cipher'), enabled: boolean('enabled').notNull().default(true), registeredAt: timestamp('registered_at', { withTimezone: true }).notNull().defaultNow(), }, (t) => [index('tool_effect_idx').on(t.effect), index('tool_owner_idx').on(t.ownerActivityId)], ); /** 白名单:授权在白名单与契约,不在活动代码(OS 04) */ export const toolAllowlistTable = sys.table( 'tool_allowlist', { activityId: text('activity_id').notNull(), tool: text('tool').notNull(), }, (t) => [primaryKey({ columns: [t.activityId, t.tool] })], ); /** 工具录制回放 —— 与 llm 的 replay_model 同构(§7.3 两缝) */ export const replayToolTable = sys.table('replay_tool', { key: text('key').primaryKey(), call: jsonb('call').notNull(), result: jsonb('result').notNull(), recordedAt: timestamp('recorded_at', { withTimezone: true }).notNull().defaultNow(), }); /** * 工具转人台(§6.2 未命中转人)—— **人是工具的一种实现**。 * * 系统里没有这件工具(无契约或有契约无实现)时,不报错、不空转:开一张执行卡请人来做, * 人填回的表单值**就是这次工具调用的出参**。这张表记的就是「这次调用交给谁、答没答、答了什么」。 * * `callKey` 是内容地址(工具 + 入参指纹):同一个调用重问一次拿的是同一份答案 —— * 没有它,Loop 每拍都会为同一件事再发一张卡。 */ export const toolHandoffTable = sys.table( 'tool_handoff', { id: text('id').primaryKey(), taskId: text('task_id').notNull(), tool: text('tool').notNull(), /** 内容地址:工具 + 入参指纹 —— 同调用同答案 */ callKey: text('call_key').notNull(), input: jsonb('input').notNull(), /** 请人做的那张执行卡 */ workOrderId: text('work_order_id').notNull(), /** pending = 已发卡等答;answered = 人已填,出参可取 */ state: text('state').notNull().default('pending'), /** 人填回的值 = 工具出参 */ output: jsonb('output'), createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), answeredAt: timestamp('answered_at', { withTimezone: true }), }, (t) => [ uniqueIndex('tool_handoff_call_key').on(t.taskId, t.callKey), index('tool_handoff_order_idx').on(t.workOrderId), ], );