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,
|
parseAdminChannelMessage,
|
||||||
parseAgent,
|
parseAgent,
|
||||||
parseAwakenTool,
|
parseAwakenTool,
|
||||||
|
parseBackthinkItem,
|
||||||
parseGraph,
|
parseGraph,
|
||||||
parsePersistedMessage,
|
parsePersistedMessage,
|
||||||
parseTool,
|
parseTool,
|
||||||
@@ -95,6 +96,14 @@ function dispatch(ev: { type: string; data: unknown }): void {
|
|||||||
if (msg) s.pushChannelEvent(msg);
|
if (msg) s.pushChannelEvent(msg);
|
||||||
break;
|
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": {
|
case "thinking.start": {
|
||||||
pendingThinking = "";
|
pendingThinking = "";
|
||||||
break;
|
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";
|
import { now, uid } from "./util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,3 +183,27 @@ export function parseAdminChannelMessage(v: unknown): AdminChannelMessage | null
|
|||||||
createdAt: typeof r.createdAt === "number" ? r.createdAt : now(),
|
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