From 04ffce252af9375ddf9c491f417dc282ce993e6f Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Wed, 14 Oct 2026 20:06:27 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90=EF=BC=9A=E5=86=BB=E7=BB=93?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8C=89=E5=90=84=E5=8C=85=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E8=A1=A5=E9=BD=90=E2=80=94=E2=80=94=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E8=A1=A8=E5=8A=A0=E5=BB=BA=E7=AB=8B=E4=BA=BA=EF=BC=88?= =?UTF-8?q?=E5=BC=80=E7=99=BB=E5=BD=95=E5=90=8E=E6=8C=89=E4=BA=BA=E9=9A=94?= =?UTF-8?q?=E7=A6=BB=E5=AF=B9=E8=AF=9D=EF=BC=89=E5=8F=8A=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=92=8C=E7=B4=A2=E5=BC=95=EF=BC=8C=E9=92=A9=E5=AD=90=E5=9B=9E?= =?UTF-8?q?=E6=89=A7=E3=80=81=E6=8A=95=E9=80=92=E8=AE=B0=E5=BD=95=E7=9A=84?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=B3=A8=E9=87=8A=EF=BC=9B=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=BA=A6=E5=AE=9A=E8=A1=A5=E5=BE=85=E5=8A=9E=E7=9A=84=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=90=8D=E4=B8=8E=E8=BF=90=E8=A1=8C=E7=B1=BB=E5=88=AB?= =?UTF-8?q?=E3=80=81=E7=94=9F=E6=95=88=E8=AE=B0=E5=BD=95=E7=9A=84=E5=B9=82?= =?UTF-8?q?=E7=AD=89=E9=94=AE=E7=AD=89=E5=AD=97=E6=AE=B5=E3=80=81=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E7=BB=93=E6=9E=9C=E7=9A=84=E7=BA=A7=E5=88=AB=E4=B8=8E?= =?UTF-8?q?=E5=93=88=E5=B8=8C=E3=80=81=E8=A7=86=E5=9B=BE=E6=9D=A5=E6=BA=90?= =?UTF-8?q?=E7=9A=84=E7=A7=8D=E7=B1=BB=E3=80=81=E5=90=84=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BD=A2=E7=8A=B6=E7=9A=84=E8=AF=B4=E6=98=8E?= =?UTF-8?q?=E3=80=81=E9=92=A9=E5=AD=90=E3=80=81=E5=8A=A9=E6=89=8B=E7=9A=84?= =?UTF-8?q?=E6=BE=84=E6=B8=85=E9=97=AE=E9=A2=98=E4=B8=8E=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=B6=A6=E8=89=B2=E7=9A=84=E8=AF=B7=E6=B1=82=E5=92=8C=E7=BB=93?= =?UTF-8?q?=E6=9D=9F=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/db/base.py | 2 + backend/app/db/models.py | 3 ++ backend/app/db/models_apps.py | 3 +- backend/app/db/models_process.py | 2 +- frontend/src/typesApp.ts | 72 +++++++++++++++++++++++++++++++- 5 files changed, 78 insertions(+), 4 deletions(-) diff --git a/backend/app/db/base.py b/backend/app/db/base.py index 109864f8..61a85d7f 100644 --- a/backend/app/db/base.py +++ b/backend/app/db/base.py @@ -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"), ] diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 2fc80af8..9e66bbe4 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -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) diff --git a/backend/app/db/models_apps.py b/backend/app/db/models_apps.py index 255251e7..ff20e35f 100644 --- a/backend/app/db/models_apps.py +++ b/backend/app/db/models_apps.py @@ -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) diff --git a/backend/app/db/models_process.py b/backend/app/db/models_process.py index cf973f24..c368a765 100644 --- a/backend/app/db/models_process.py +++ b/backend/app/db/models_process.py @@ -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) diff --git a/frontend/src/typesApp.ts b/frontend/src/typesApp.ts index 3ac45a5e..b5ac4c40 100644 --- a/frontend/src/typesApp.ts +++ b/frontend/src/typesApp.ts @@ -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 - /** 服务端解析好的数据;形状随组件而定(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 | 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 +}