集成:冻结文件按各包请求一次补齐——对话表加建立人(开登录后按人隔离对话)及迁移和索引,钩子回执、投递记录的状态注释;前端约定补待办的节点名与运行类别、生效记录的幂等键等字段、发布结果的级别与哈希、视图来源的种类、各组件数据形状的说明、钩子、助手的澄清问题与应用润色的请求和结束事件

This commit is contained in:
ThearchyHelios
2026-10-14 20:06:27 +08:00
parent e341beb1cb
commit 04ffce252a
5 changed files with 78 additions and 4 deletions
+2
View File
@@ -234,6 +234,7 @@ _COLUMN_MIGRATIONS: list[tuple[str, str, str]] = [
("mcp_servers", "headers", "JSON DEFAULT '{}'"),
("mcp_servers", "endpoint_id", "VARCHAR(32)"),
("conversations", "app_id", "VARCHAR(32)"),
("conversations", "created_by", "VARCHAR(100)"),
]
#: 迁移补上的列要建的索引:(索引名, 表, 列或多列)。create_all 不碰已有的表,表上新列的索引也就不会建。
@@ -247,6 +248,7 @@ _INDEX_MIGRATIONS: list[tuple[str, str, str | tuple[str, ...]]] = [
("ix_runs_app_id", "runs", "app_id"),
("ix_runs_wf_ver_status", "runs", ("workflow_id", "version", "status")),
("ix_conversations_app_id", "conversations", "app_id"),
("ix_conversations_created_by", "conversations", "created_by"),
]
+3
View File
@@ -813,6 +813,9 @@ class Conversation(Base, TimestampMixin):
archived: Mapped[bool] = mapped_column(default=False)
# 应用编辑时助手对话归属的应用(kind=app)。其余为空
app_id: Mapped[str | None] = mapped_column(String(32), default=None, index=True)
# 谁建的这段对话(api/deps.actor_of:开登录时是认证过的用户名)。开登录后 user 角色只看得到、只改得动自己的对话;
# 存量为空:关登录时建的,开登录后只有 builder 以上看得到
created_by: Mapped[str | None] = mapped_column(String(100), default=None, index=True)
# 列表按活跃度排。不能用 updated_at —— 改个标题就会把这条顶到最前面,
# 而用户对"最近聊过的"的预期是按说话时间排,不是按编辑时间
last_active_at: Mapped[datetime] = mapped_column(UTCDateTime, default=utcnow, index=True)
+2 -1
View File
@@ -107,6 +107,7 @@ class HookReceipt(Base):
idem_key: Mapped[str] = mapped_column(String(200))
run_id: Mapped[str | None] = mapped_column(String(32), default=None)
task_id: Mapped[str | None] = mapped_column(String(32), default=None)
status: Mapped[str] = mapped_column(String(20)) # accepted | rejected
# processing(按幂等键认领、处理中)| accepted | rejected | signature(idem_key 以 sig: 开头:记一个签名用过了,防换键重放)
status: Mapped[str] = mapped_column(String(20))
detail: Mapped[str | None] = mapped_column(Text, default=None)
received_at: Mapped[datetime] = mapped_column(UTCDateTime, default=utcnow)
+1 -1
View File
@@ -131,7 +131,7 @@ class NotifyDelivery(Base):
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=new_id)
channel_id: Mapped[str] = mapped_column(ForeignKey("notify_channels.id", ondelete="CASCADE"), index=True)
subject_kind: Mapped[str] = mapped_column(String(20)) # task | run | action
subject_kind: Mapped[str] = mapped_column(String(20)) # task | run | action | channel(渠道的测试消息)
subject_id: Mapped[str] = mapped_column(String(32))
event: Mapped[str] = mapped_column(String(40))
run_id: Mapped[str | None] = mapped_column(String(32), default=None)
+70 -2
View File
@@ -247,6 +247,8 @@ export interface LintIssue {
export interface PublishResult {
ok: boolean
version?: number
level?: AppLevel | null
spec_hash?: string
issues: LintIssue[]
/** 钉住项相对上一版升了版、需要选处置的:[{alias, from, to}] */
needs_disposition?: { alias: string; from: number; to: number }[]
@@ -271,6 +273,8 @@ export interface Stamp {
}
export interface ViewSourceState {
/** 来源的种类:按需视图(on_demand)才有「刷新」 */
kind?: 'latest_sealed' | 'on_demand' | 'instance'
state: 'ok' | 'empty' | 'running' | 'stale' | 'error'
run_id: string | null
stamp: Stamp | null
@@ -292,7 +296,18 @@ export interface ViewNode {
id: string
type: string
props: Record<string, any>
/** 服务端解析好的数据;形状随组件而定(metric:{text, state, evidence};table:{columns, rows, masked}……) */
/**
* 服务端解析好的数据(backend/app/apps/resolve.py 的 _data),形状随组件而定;绑定了视图的都带 state 和可选的 message:
* - stamp:{view, state, stamp};metric:{state, text, name, unit, evidence}
* - series:{state, mode: single|dual|incomparable|recompute, unit, series: [{version, label, points: [{run_id, as_of,
* value, text, evidence}]}], breaks: [{after_run?, from, to, text}]}
* - table、chart:{state, columns, rows(遮罩的格子已换成「已遮罩」), column_types, masked, total_rows, truncated, evidence}
* - report:{state, node, doc_artifact, evidence};field:{state, text, value?, evidence?};status:{state, value, text}
* - form:{form};entry_button:{entry, label, disabled, reason?};instance_list:{who, entry, items}
* - instance_header:InstanceRow + {state, status_text, stamp};instance_timeline:{state, status, status_text, steps}
* - task_list:{scope, items};task_form、signoffs:{state, task, task_ui, assignments?}
* - action_status:{state, node, records: [{id, state, environment, attempt, payload_hash, error, receipt?}]}
*/
data?: Record<string, any> | null
children?: ViewNode[]
}
@@ -347,6 +362,10 @@ export interface Task {
approval_id: string | null
run_id: string
node_id: string | null
/** 节点在图上的名字 */
node_label?: string | null
run_class?: 'formal' | 'exploratory' | null
workflow_id?: string | null
app_id: string | null
app_name?: string | null
app_version: number | null
@@ -389,6 +408,15 @@ export interface ActionRecordRow {
id: string
run_id: string
node_id: string
idem_key?: string
endpoint_id?: string | null
request_at?: string | null
response_status?: number | null
lookup_attempts?: number
confirm_task_id?: string | null
workflow_id?: string | null
workflow_version?: number | null
capability_version?: number | null
app_id: string | null
capability: string
environment: 'prod' | 'test' | 'simulated'
@@ -477,7 +505,8 @@ export type OriginKind =
export interface LookObject {
kind: 'table' | 'capability' | 'workflow_version'
source?: string
source_id?: string
/** 点名了不在这一轮范围里的数据源时为 null,missing 里是点名的表 */
source_id?: string | null
named: string[]
picked?: string[]
joined?: string[]
@@ -500,3 +529,42 @@ export interface LookEvent {
elapsed_ms: number
objects: LookObject[]
}
// ---------------------------------------------------------------------------
// 入站钩子(§2.4)与助手流(§2.9 L1、§3.11)
// ---------------------------------------------------------------------------
export interface AppHookRow {
id: string
name: string
kind: 'start' | 'signal'
entry_id: string | null
enabled: boolean
created_at: string
/** 只在新建和轮换时出现一次 */
secret?: string
}
/** 助手问澄清问题:这一轮就此结束(已经改了的,后面跟 final,autorun 为 false) */
export interface AskEvent {
op: 'ask'
question: string
}
/** POST /api/copilot/app-stream 的请求体;流里的应用操作就是 app_add / app_update / app_remove / app_move / app_page / app_set */
export interface AppStreamRequest {
instruction: string
app_id?: string
spec?: AppSpec
bindings?: AppBindings
conversation_id?: string
provider?: string
model?: string
}
export interface AppStreamFinal {
op: 'final'
spec: AppSpec
issues: LintIssue[]
explanation: string
}