feat(backthink): 后台思考页接 WS 实时——ws.ts 处理 backthink 事件头插 store
后端每次思考落库发 ws.backthink(bus→hub→WS),此前 ws.ts 无 case、addBackthink 是死方法。 新增 case `backthink`→parseBackthinkItem→s.addBackthink 头插;parseBackthinkItem 兼容 Go默认字段(ID/Trigger/At/Conclusion/ShouldPush/PushResult)与admin camelCase。 配合页面(行key=id)实现增量插入、不整页重渲。build 99模块绿。
This commit is contained in:
1 parent
3386679d45
commit
7a81071ec8
2 files changed
+34
-1
No files matched your search
@@ -5,6 +5,7 @@ import {
|
||||
parseAdminChannelMessage,
|
||||
parseAgent,
|
||||
parseAwakenTool,
|
||||
parseBackthinkItem,
|
||||
parseGraph,
|
||||
parsePersistedMessage,
|
||||
parseTool,
|
||||
@@ -95,6 +96,14 @@ function dispatch(ev: { type: string; data: unknown }): void {
|
||||
if (msg) s.pushChannelEvent(msg);
|
||||
break;
|
||||
}
|
||||
case "backthink": {
|
||||
// design/12 §4 — a background-think pass result was persisted. Head-insert into
|
||||
// the store so the admin backthink panel appends it in realtime (rows keyed by
|
||||
// id → React reconciles incrementally, no whole-page re-render).
|
||||
const item = parseBackthinkItem(r);
|
||||
if (item) s.addBackthink(item);
|
||||
break;
|
||||
}
|
||||
case "thinking.start": {
|
||||
pendingThinking = "";
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MODES, type AdminChannelMessage, type AgentInfo, type ChatMessage, type FlowGraph, type Mode, type ToolUsed } from "./types";
|
||||
import { MODES, type AdminChannelMessage, type AgentInfo, type BackthinkItem, type ChatMessage, type FlowGraph, type Mode, type ToolUsed } from "./types";
|
||||
import { now, uid } from "./util";
|
||||
|
||||
/**
|
||||
@@ -183,3 +183,27 @@ export function parseAdminChannelMessage(v: unknown): AdminChannelMessage | null
|
||||
createdAt: typeof r.createdAt === "number" ? r.createdAt : now(),
|
||||
};
|
||||
}
|
||||
|
||||
/** Decode a background-think `backthink` WS payload into a BackthinkItem. The bus
|
||||
* publishes the raw `backthink.Record` (Go default field names ID/Trigger/At/
|
||||
* Conclusion/ShouldPush/PushResult); the admin wire uses camelCase, so this reads
|
||||
* both defensively and returns null when the id cannot be resolved. */
|
||||
export function parseBackthinkItem(v: unknown): BackthinkItem | null {
|
||||
const r = toRecord(v);
|
||||
if (!r) return null;
|
||||
const idRaw = r.ID ?? r.id ?? r.record_id ?? r.recordId;
|
||||
if (idRaw === undefined || idRaw === null) return null;
|
||||
const atRaw = r.At ?? r.at;
|
||||
let at = 0;
|
||||
if (typeof atRaw === "number") at = atRaw;
|
||||
else if (typeof atRaw === "string") {
|
||||
const t = Date.parse(atRaw);
|
||||
if (!Number.isNaN(t)) at = t;
|
||||
}
|
||||
const trigger = typeof r.Trigger === "string" ? r.Trigger : typeof r.trigger === "string" ? r.trigger : "";
|
||||
const conclusion = typeof r.Conclusion === "string" ? r.Conclusion : typeof r.conclusion === "string" ? r.conclusion : "";
|
||||
const pr = toRecord(r.PushResult) ?? {};
|
||||
const shouldNotify = (r.ShouldPush ?? r.shouldNotify ?? r.should_push) === true;
|
||||
const pushed = (pr.Pushed ?? pr.pushed ?? r.pushed ?? r.push_pushed) === true;
|
||||
return { id: String(idRaw), trigger, conclusion, shouldNotify, pushed, at };
|
||||
}
|
||||
Reference in new issue
Block a user