Commit Graph

61 Commits

Author SHA1 Message Date
Blizzard 8f766a75a7 feat(space): 桌面端空间成员管理界面 —— 补齐"临时组队协作"前端闭环
此前能建项目空间/切换/启用全员空间,但邀请队友进空间只有后端 API、无界面,
协作故事前端断了一半。本次补上成员管理 UI,让 Space 协作端到端可用。

- api.ts: spaceMembers/addSpaceMember/setSpaceMemberRole/removeSpaceMember + SpaceMemberInfo
- SpaceMembers 弹窗: 列成员(名/邮箱/角色 badge); 空间 admin/owner 可邀请(邮箱+角色)/
  下拉改角色/移除, owner 受保护(无下拉/移除); 非管理员只读提示。复用 Dialog/Button/Select
- 顶栏加"管理成员"入口(UserCog): 活跃空间为 project/tenant 时显示; App 管弹窗开关,
  传 spaceId/spaceName/canManage(空间角色 admin/owner)/selfUserId

实机验证(Wails 桌面 + 全栈后端): demospace(owner)在项目空间邀请 teammate → 成员列表
即时出现(切换器 1人→2人) → 下拉改 teammate 成员→管理员(服务端确认 owner/admin) →
owner 自身受保护无改删入口。tsc + 48 前端测试通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 14:29:28 +08:00
Blizzard 9dab25a5f0 feat(space): 全员空间(tenant-kind) —— 整租户共享的"开关",自动纳入全部成员
补齐 Space 三类型的最后一种:tenant-kind「全员空间」= 用户最初想要的"共享工作区开关"。
与 project 空间的本质区别:自动纳入全租户成员(含之后新加入的),而非手动逐个邀请。

后端:
- EnableTenantSpace(幂等):建 kind=tenant 空间 + 把全部 active 租户成员按租户角色纳入
  (owner→owner…billing_admin→viewer);tenantSpaceID 查询;spaceRoleFromTenant 角色映射
- AddMemberByEmail 加租户成员时 autoJoinTenantSpace:若已启用全员空间则自动补纳新成员
- SpaceCreate handler:kind=tenant 路由到 EnableTenantSpace,且须租户 admin/owner(inline 403)

桌面端:
- 空间切换器加"启用全员空间"入口(Globe 图标):仅租户 admin/owner 且尚未启用时显示,
  带二次确认;启用后该按钮自动隐藏(hasTenantSpace),全员空间进切换器

验证(gateway+docker):member 启用 403 / owner 启用纳入全部现有成员(owner·member·viewer
按租户角色映射) / 新加租户成员自动纳入 / 幂等(再启用返回同一个不重复建) / viewer 在全员空间
入库 403(只读) / owner 建库 200;浏览器实机:启用后全员空间进切换器 + 启用按钮消失

至此 Space 三类型(个人/项目/全员)全部落地。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:55:29 +08:00
Blizzard a8c0bb42a7 feat(space): 共享工作区增量3b —— KB 知识库按 Space 共享(存储 re-key)
把 3a 的 Space 作用域推到 KB 层:KB/Doc/DocLink owner→space_id,作用域键
owner/name→space_id/name,同空间成员共享知识库(检索/入库/文库/双链/图谱)。

后端:
- store: KB/Doc/DocLink 加 space_id,唯一索引 (owner,*)→(space_id,*),owner 降级创建人;
  查询全改 space 作用域;SaveDoc/ListVault/GetDocByID/DeleteDocByID/ReplaceDocLinks/
  ResolveInboundLinks/ListLinks 改 space;tenantIDForSpace 补异步入库租户
- MigrateKBSpaces 启动迁移(space_id 回填 + 唯一索引换新,同 Agent 顺序坑规避)
- scopedKB owner/name→space_id/name;IngestJob 契约加 SpaceID;enqueueIngest/runIngest
  穿 space;MinIO 对象键改 space/kb/doc(避免跨空间同名撞键,老键不透明不迁)
- KB 写路由(create/ingest/ingest_file/note/delete)挂 RequireSpaceRole(member):viewer 只读
- 存储层重灌迁移端点 POST /admin/migrate-kb-storage(异步):为存量文档入队新 space
  作用域的重灌作业(复用 JetStream 入库 worker 池),先删旧键;避免同步重嵌撑爆 HTTP 超时

桌面端:
- KbView 收 spaceId(变则重拉库)+spaceReadOnly(viewer 禁建库/入库/文件/笔记);VaultPanel 同

验证(gateway+mcp-go+Milvus/Neo4j/embedding 全栈):
- PG 迁移: 20/21 KB + 50/54 doc 回填 space_id(4 未迁=pre-多租户 owner='wt' 空租户遗留,
  正确跳过),唯一索引 idx_kb_sn/idx_doc_skn 换新、旧索引删除
- KB 共享: member 见共享库 / viewer 建库·入库 403 / 切回个人空间隔离(看不到)
- 全向量链路: RagA 入库(真 dashscope embedding)→ RagB(空间member)检索命中 RagA 内容
- 存储重灌: 端点异步入队 49 作业(worker 池背压处理),重灌后老文档在新 space 键可检索

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:00:34 +08:00
Blizzard addaa1b34f feat(space): 共享工作区增量3a —— Agent 编排按 Space 共享(纯 PG 打样)
引入 Space 中间容器(租户>Space>成员),资源作用域从 owner 改为 space_id,
让"个人私有/项目临时组队/整租户共享"出自同一模型(设计见 SPACE_DESIGN.md)。
先在纯 PG 的 Agent 上打样,零存储风险,验证协作+RBAC+切换 UX。

后端:
- 新表 Space{tenant_id,name,kind,creator,archived} + SpaceMember{space_id,user_id,role}
  (如 Tenant 般不 isTenantScoped);User.ActiveSpaceID;Agent 作用域 owner→space_id,
  owner 降级为创建人(供 UI 显示 / 删他人鉴权)
- store/space.go:个人空间幂等/活跃空间解析/切换/列表/建/成员CRUD/归档
- 迁移顺序坑:结构体只放非唯一 index,MigrateAgentSpaces 回填 space_id 后再建唯一
  索引 idx_agent_sn + DROP 旧 idx_agent_on(否则存量空 space_id 撞车);启动序4步幂等
- 中间件 SpaceContext(注入 space_id) + RequireSpaceRole(照 RequireTenantRole)
- handler/space.go 空间端点 + 路由;agent.go 改空间作用域(删/覆盖他人需 admin)
- 计费零改动(Space 与 ResolveBillingTenantID 正交)

桌面端:
- api.ts space 接口;顶栏 SpaceSwitcher(含新建项目空间);StudioView 随空间切换
  重拉编排 + viewer 禁保存;Agent 列表显示创建人 + 按 mine 控删除

验证:中间件6门控单测 + DB迁移(13个人空间/9 Agent全re-key/索引换新) + 后端HTTP全
场景(member见他人编排/删他人403、viewer存403、非成员切空间400+隔离、owner删他人200)
+ 浏览器实机(切换器3空间/Studio空间编排随切换隔离刷新/创建人显示/console无错)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:51:33 +08:00
Blizzard bf5be08e96 feat(rbac): 租户角色门控落地(极简版) —— viewer/billing_admin 只读不可提交任务
角色此前能分配但不生效(viewer 也能烧租户积分)。本次让角色真正挡动作:

- store.RoleRank 角色层级:owner(4)>admin(3)>member(2)>viewer/billing_admin(1),
  未知/非成员=0;billing_admin 定位为「财务只读」不跑任务
- 可复用中间件 RequireTenantRole(db, minRole),抽 MemberRoleResolver 小接口便于单测
- POST /tasks 挂 ≥member 门控:唯一会烧租户积分的入口(KB 入库不计租户),挡住即够
- 桌面端 StudioView 收 readOnly:viewer 时「运行」禁用+只读提示(UX 兜底,真闸在后端)
- 测试:RoleRank 纯逻辑 + 中间件 7 门控 case(owner/admin/member 放行,
  viewer/billing_admin/非成员 403,未登录 401)

实机验证(gateway+docker):viewer 提交 403 → 升 member 202 → 降回 viewer 403 →
owner 自租户 202(solo 用户不受影响),三态可逆、角色驱动、中间件先于 handler。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:49:24 +08:00
Blizzard 248267984c feat(desktop): 顶栏租户切换器(多租户)—— 切换活跃租户即换工作区+计费
用户属多个租户时,顶栏显示租户下拉(Building2 图标):切换即调 POST /me/tenant
换活跃租户,随后刷新余额芯片(显示新计费租户的可花余额)。仅 >1 租户时出现。
api 加 myTenants/switchTenant。

live 验证(preview):demoB(属公司A+公司B) 切换器显两租户;从公司A(共享,余额15.84)
切到公司B(个人,余额6.38)→ 后端 active 持久化、芯片随之更新为 6.4(橙,偏低)。tsc+48 测试全过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 11:02:36 +08:00
Blizzard a878e2dc89 feat(desktop): 「用量」视图 —— 我的积分余额/消耗趋势/最近消耗(P3 续)
给终端用户一个自己的计量观测面(守边界:只读自己租户):
- 新增 UsageView(左导航 MANAGE 组「用量」,Coins 图标):余额 hero 卡
  (硬拦截+余额≤0→红/偏低→橙)、区间 KPI(积分/Token/成本/运行数)、
  积分消耗按天趋势条形图(近7/近30天可切)、最近消耗明细表(任务/模型/token/积分/成本)。
  读 /api/v1/me/usage。
- 顶栏积分余额芯片改为可点,进「用量」页(onOpenUsage → setView)。

live 验证(preview):视图显示 公司A 余额 15.92 + KPI + 趋势bar(落在实际消耗日) +
最近消耗 3 行真实数据;tsc + 48 测试全过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:31:12 +08:00
Blizzard 511ae199a4 feat(desktop): 顶栏积分余额芯片 + 提交遇余额不足透出友好文案(P3 租户感知)
让计费对终端用户可见(守 desktop/admin 边界:只读自己租户,不碰 admin 配置):
- TopBar 加积分余额芯片(Coins 图标):常态显余额;硬拦截开+余额≤0 → 红「余额不足」;
  偏低 → 橙。tooltip 显租户名 + 是否拦截。api.tenantCurrent() 读 /tenants/current。
- App 登录后拉租户上下文,每 20s 轮询 + 运行结束即刷新(余额跟手)。
- submitTask 遇 402 解析后端 error 文案透出(如「租户积分余额不足,请充值后再试」),
  在运行面板报错处显示,用户知道为何被拦。

live 验证(preview):芯片显示 公司A 余额 15.9 ✓;enforce 开+余额0 时 /tenants/current
标 enforce+余额0(芯片转「余额不足」)、提交 402 带友好文案 ✓;tsc + 48 测试全过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:18:30 +08:00
Blizzard cb6eec5614 feat(desktop): T0.2 多智能体进 Studio —— coordinator 节点 + 专家卡片 + 派发可观测
把已通的多智能体后端(MULTI_AGENT.md)接上 UI,用户可在画布拖出协调者图。

- nodeCatalog: 新增 `coordinator`「多智能体协调」节点(indigo) + 新字段类型
  `agentList` + Specialist 接口({name,use,system,tools}),与后端 parseSpecialists 对齐。
- Inspector: AgentListField —— 可增删的子智能体卡片(名/用途/系统提示词/工具逗号分隔)。
- dsl 校验: agentList 需 ≥1 个有名字的专家、名字不重复。
- RunsView: 「工具调用」面板纳入专家派发(kind=agent),改名「工具/专家」,专家项
  用 Users 图标 + indigo「专家」徽标区分(此前只筛 kind=tool,漏掉多智能体派发)。
- 导出: agents 数组经 exportDsl 原样透传进 DSL → 后端 parseSpecialists 直接消费。

tsc + vitest(19) 绿;UI 同款结构 DSL 后端可跑(协调链路 live 已验)。
DEPTH_ROADMAP T0.2 ,T0 激活 2/2 完成。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 17:14:14 +08:00
Blizzard 158fe094ab fix(hitl): 审批复盘轨迹补全 —— 中断不关 exec 流,resume 事件续录
现象:已完成的审批任务,复盘轨迹停在「已中断等待审批」、审批节点永远转圈,看着
像卡住(实际任务已 done、有输出)。

根因:任务中断时 Handle 的 defer tr.done() 给 exec 流发了 CompleteExec → 网关 exec
录制器关闭。resume 是另一次独立调用,其 exec 事件(审批通过/拒绝、续跑节点)发到
同一主题时录制器已关 → 没录进去。(token 流中断时特意没关,所以输出录到了;exec
流却被关了,不对称。)

- exec.go: execTracer 加 suspended 标志,done() 挂起时不关流。
- orchestrator.go: 中断分支置 tr.suspended=true(与 token 流一致)。
- ExecTrace.tsx: 前端兜底——运行已完成(phase done)时把悬挂的 waiting/running 节点
  收敛为 done,修旧任务(已 done、轨迹缺 resume 事件)的转圈假象。

live 验证:审批任务批准后复盘轨迹完整呈现 approval await→end「批准:放行」→
agent start→推理→end,审批节点不再转圈。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:57:11 +08:00
Blizzard fcefecd10d fix(desktop): 轮询命中终态时同步 phase —— 运行按钮不再永卡「运行中」
attachRun 的状态轮询此前只更新 lifecycle、不动 phase。token 流没收到 done 的场景
(如恢复的在途任务、或任务被外部置终态)→ phase 永卡 streaming → Studio 运行按钮
永远禁用「运行中…」,看似不能跑新编排。

修复:轮询命中终态(done/failed/timeout/rejected)时把 phase 也置 done/error,
释放运行按钮。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:48:50 +08:00
Blizzard e8dc2e02c3 fix(desktop): 审批条提交后不再永卡「提交中」+ 按 taskId 重挂载
复现:登录恢复待审任务时若捞到一个无法续跑的任务(如早期 KV 冒号 bug 留下的无
resume 记录的孤儿),点批准 → 决定发出但消费者找不到记录 → 任务永远 waiting →
审批条 busy 状态成功后从不复位 → 永卡「提交中…」spinner,看似整个应用卡死。

- decide 成功后置 submitted 并在 finally 复位 busy(此前只在 catch 复位)。
- submitted 时显示「决定已发出,等待续跑…(若长时间无响应,该任务可能已失效)」
  而非停在 spinner —— 诚实反映状态,不误导成 hang。
- Bar 加 key={taskId}:换审批任务时重挂载,避免上一个的 submitted 残留。

(孤儿任务本身已在 PG 标记 failed 清理;冒号 bug 早已修复,不再产生新孤儿。)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:40:07 +08:00
Blizzard 49d19d9b59 fix(desktop): 刷新/重开后恢复在途待审任务 —— HITL 审批条可跨会话重现
HITL 持久化中断后审批可跨重启、等数小时,但前端 ApprovalBar 只绑定内存里的
live run:用户一刷新页面/重开 app,run 清空 → 审批条消失 → 那个 waiting 任务
再也点不到批准(历史页只读回放、无审批入口)。这是 durable 模型下的真实 UX 洞。

- App: 抽出 attachRun(状态轮询 + exec 流 + token 流),新发起与恢复共用。
- 登录后探测 waiting 任务并 attachRun 挂回 → 全局审批条重现、可批准/拒绝,
  续跑 token 经重订阅的流回显(网关从 Redis 回放 exec 补回审批摘要)。
- 坑:StrictMode(dev)双调用 effect,原 cancelled 守卫会把首次 async 的恢复误吞
  (restoredRef 已保证只跑一次,App 是根组件不卸载)→ 去掉 cancelled。

真实 UI 验证(preview 驱动 :5173):提交 HITL 任务→waiting→刷新页面→审批条
自动重现→点批准→从 checkpoint 续跑→done,真实出稿 + 评测 1.00。tsc + vitest 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:50:47 +08:00
Blizzard d2662a1f37 fix(history): 历史运行复盘读库持久化 —— 杜绝过 Redis TTL 后卡「流式中…」
问题:历史任务超 Redis 流 10min TTL 后,SSE 回放在空流上永久阻塞 → 运行页卡「流式中…」、
轨迹/工具/输出全空。

修复:收尾把最终输出 + 执行轨迹持久化到 PG,历史复盘改读库(不依赖 Redis TTL):
- store:Task 加 output/trace 两列;SaveTaskOutput/SaveTaskTrace/GetRunDetail。
  trace 用 type:text(不是 jsonb)——否则提交时空串 "" 入 jsonb 列会 INSERT 失败、整条任务不落库。
  (已 ALTER 既有 trace 列 jsonb→text。)
- gateway:token/exec 录制器在 done 时把累计的输出/轨迹快照落库。
- 新增 GET /tasks/:id/replay 返回持久化的 {output, exec}。
- RunsView:选中历史运行改 runReplay() 读库(秒回、phase 立即 done/error),不再 SSE 回放。
  即便旧任务无持久化数据,也是 done+空态,绝不再卡「流式中…」。

live:新任务落库 output 305 字(含表格) + 轨迹 5 事件,/replay 正确返回;tsc+vite、gateway 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:52:26 +08:00
Blizzard 594be6a317 fix(desktop): 模型输出渲染图表 + Markdown 支持表格/代码围栏;ReAct 步数提至可配 12
针对运行页输出的两个渲染缺口 + 一个节点报错:
- 图表:模型输出走 chart 块分段渲染(文本 Markdown,```chart 块用 ChartView 出图),
  不再把图表 spec 显示成原始代码。
- 表格:轻量 Markdown 组件原本不支持 GFM 表格(| … | 显示成原始竖线)与 ``` 围栏代码,
  现补上:表格解析成 <table>、围栏渲染成 <pre>。
- ReAct 步数:reactMaxStep 由常量 8 改 env 可配(REACT_MAX_STEP,默认 12)。研究型任务
  (搜索→抓取→推理多轮)8 步偏紧易触顶报 exceeds max steps;提到 12 覆盖多数。

tsc+vite 通过,dispatcher build/vet 绿;wails 重启加载。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:33:58 +08:00
Blizzard 101d3f9bc9 refactor(desktop): 运行页改三栏 —— 模型输出固定右侧(Markdown),中间 tab 只留轨迹/工具/评测
按反馈调整运行·观测布局:模型输出不进 tab,恢复成独立右侧面板用 Markdown 渲染(与旧版一致);
中间 tab 切换 执行轨迹/工具调用/评测,标签行右上保留运行状态指示(完成✓/流式中…/出错)。
三栏:运行历史 | tab详情+状态 | 模型输出。

tsc+vite 通过;预览自查布局正确(左历史/中tab+状态/右Markdown输出),wails 重启加载新版。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:12:48 +08:00
Blizzard 7cc7f5fd26 refactor(desktop): 观测收敛进「运行」页(tab 切换),删全局底部抽屉
底部抽屉与新版运行页重复,且默认展开常驻占 ~176px、引用/评测还是空壳。按高内聚收敛:

- 运行·观测 详情区改 tab 切换:执行轨迹 / 模型输出 / 工具调用 / 评测(去掉没接的「引用」空标签);
  OutputView(含 chart 渲染) + ToolCalls 从抽屉并入运行页;评测标签接 taskEval 全量展示。
- 删除全局 BottomDrawer,释放底部空间。
- HITL 审批条抽成独立 shell/ApprovalBar,全局常驻于 TopBar 下(审批中断必须随处可见可操作)。
- 发起运行自动跳「运行」页 + 新运行自动切回「当前运行」,实时观测不丢。

tsc+vite 构建通过;wails HMR 热加载生效。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:56:52 +08:00
Blizzard 3cf3c0b070 feat(desktop): 运行·观测 重做为运行历史 + 复盘(Tier2,用上 exec Redis 回放)
RunsView 原本只能看「最近一次」实时运行;现做成完整的运行历史复盘:

后端 GET /api/v1/runs?limit=(store.RecentRuns):任务 LEFT JOIN 评测,返回
task_id/status/time + eval level/overall,供历史列表。

前端 RunsView:左侧运行历史列表(状态点 + 相对时间 + 评测分级徽标),点选任一历史运行
→ 经 streamExec/streamTokens 从 Redis 回放该次执行轨迹 + 模型输出(对已完成任务流即回放),
并取 /tasks/:id/eval 显示评测(分级/忠实度/纠偏/评语/flags)。「当前运行」固定置顶沿用实时订阅。
api 补 listRuns + taskEval。

这正是之前 exec 轨迹 Redis 回放的消费场景。live(vite + 预览):提一新任务 → 历史列表出现 →
点选 → 完整回放轨迹(2 节点/2518ms/含推理过程)+ 答案全文 + 评测 ok·1.00,无控制台报错。
tsc+vite 构建通过,gateway 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:34:07 +08:00
Blizzard 1344bf98a0 feat(desktop): 工作台重做为实时仪表盘 + /stats/overview 聚合端点(Tier1 UI 升级)
桌面端首屏原是静态宣传页(stat 全硬编码、唯一活数据是网关在线),"太单调"。
重做为活的驾驶舱:

后端 GET /api/v1/stats/overview(聚合,几条轻量查询):
- 任务今日/累计、7 日趋势、近 7 天终态分布(实例级,Task 无 owner)
- 评测均分 + 忠实度均值 + 计数;知识库文档/库数(owner 级)
- token 今日 + 7 日趋势(Redis 日计数)、近期运行 feed、服务健康(复用 health 口径)
- store.StatsOverview + RecentTasks。

前端 Home 重写为仪表盘:4 指标卡(今日任务/Token/评测均分/知识库)带 SVG 火花线、
近期运行 feed(状态点+相对时间,点进运行观测)、7 日任务量柱图、服务健康灯带、能力入口、
快捷动作。5s 轮询刷新。配色沿用现有 ink 暗底 + brand/accent。

live(vite dev + 预览截图):登录后仪表盘渲染真实活数据(今日任务/Token/评测 1.00/近期运行/
服务全绿),无控制台报错。tsc+vite 构建通过,gateway 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:13:54 +08:00
Blizzard 82b1d3e802 Revert "feat(desktop): 底部抽屉可拖拽调高/最大化 + 输出渲染 Markdown"
This reverts commit 429b04d64a.
2026-06-25 14:12:25 +08:00
Blizzard 429b04d64a feat(desktop): 底部抽屉可拖拽调高/最大化 + 输出渲染 Markdown
解决两个真实体验问题(编排页底部输出区):
- 抽屉太矮(176px)长输出放不下:改为可拖拽顶边调高(140px~85vh) + 一键最大化(82vh)/还原。
- 输出原样显示 markdown 未渲染:接入现有轻量 Markdown 组件(标题/加粗/斜/码/列表/引用/分隔/双链,
  随主题),替换 <pre>;含 ```chart 块时文本段渲 Markdown、图表段渲 SVG,保持顺序。
  复用现成组件,未引重依赖(react-markdown 试装后回退),bundle 不涨。

tsc + 生产构建 + vitest 48 全过;桌面端已 HMR。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:57:51 +08:00
Blizzard 760ca06667 feat(desktop/studio): Tier1 编排画布主题化 + 节点精修(产品灵魂打磨)
react-flow 此前 colorMode 写死 dark、背景/连线/控件全用库默认,换肤下不协调。改为:
- colorMode 绑定当前主题 → 控件/连线/手柄/选区自动随亮暗切换。
- Background 点阵色、MiniMap 遮罩随主题;隐藏库水印(proOptions.hideAttribution,更干净)。
- index.css 精修 react-flow:控件(细边/圆角/themed hover)、连线(中性描边+选中紫)、迷你图边框,对齐设计系统。
- TypedNode:卡面落到表面色(ink-850)、选中环用 brand 令牌(替硬编码紫)、加 hover 描边、手柄themed。
- 工具栏/调色板/检查器已用新 Button + 令牌类,自动一致。

tsc + 生产构建通过;桌面端已 HMR。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:47:48 +08:00
Blizzard 94f04bd204 refactor(desktop/ui): 组件库对齐 shadcn 画廊基调
按已定稿的组件画廊精修 ui/ 基础组件(均走主题令牌,亮/暗自适应):
- Button:主按钮去掉霓虹 shadow-glow(玩具感残留)→ 扁平 + active 微缩;
  次按钮落到 ink-850 表面。
- Input/Textarea/Select:底色对齐表面(ink-900) + 焦点环改 ring-2 ring-brand/25(更柔的聚焦)。
- 新增 Table/Tr/Td:细分隔线 + 弱化表头 + 行 hover,运维/数据场景标准呈现。
- EmptyState/Skeleton/Badge/Tabs/Card 已主题化,沿用。

页面经 ui/ 桶引入,组件升级即全页生效。tsc + 生产构建 + vitest 48 全过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:23:59 +08:00
Blizzard ca7ce4c26f feat(desktop): 亮/暗主题切换 —— 默认亮色,一处令牌驱动全 app 换肤
把高频硬编码色(ink 表面 / line 边框 / slate 文字)重定义为 CSS 变量(RGB 三元组,
保留 /透明度 修饰符),:root(亮) 与 .dark(暗) 两套值切换——改 tailwind.config + index.css
两处即让全 app 换肤,无需逐文件迁移。

- 亮色走 shadcn 中性灰(zinc:zinc-50 底/白卡/zinc-200 边/zinc-900 字);
  暗色精炼为中性 zinc 暗(替代原偏蓝 ink),顺带提质感。
- lib/theme.ts:useTheme + applyInitialTheme(main 启动前设类,避免首屏闪烁)+ localStorage 持久化,默认亮色。
- TopBar 加 Sun/Moon 切换钮;滚动条/输入控件 color-scheme 随主题。
- 修亮色下会失效的 bg-white/5 → bg-ink-800(Tabs/Badge/ExecTrace 的中性微底);
  模态遮罩 bg-black/55 两模式皆宜,保留。

验证:tsc + vitest 48 过 + 生产构建通过(var 色全解析)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:39:36 +08:00
Blizzard 592b2a3d97 feat(tools): chart 图表工具 —— 工具产出 JSON spec,前端 SVG 渲染(职责分离)
按「工具只产数据、渲染交前端」设计:
- 后端 chart 工具(mcp-go):校验并返回规范化图表 JSON(type=bar/line/pie + labels + series,
  校验类型/长度一致/pie 取首系列)。工具说明指示 agent 用 ```chart 围栏原样包裹返回的 JSON。
- 前端:lib/chartspec.ts 从输出抽取 ```chart 块(解析失败回退为文本不丢内容);
  components/ChartView.tsx 自绘 SVG 柱/线/饼图(无第三方图表依赖);
  BottomDrawer 输出区含图表块时分段渲染(文本 + SVG),否则纯文本。

测试:前端 chartspec 单测 12 例(isChartSpec 校验、分段抽取、非法块回退、多块、hasChart);
tsc 干净,vitest 48 过。live 自主 agent:chart 工具产出 {"type":"bar",...},
agent 正确用 ```chart 围栏嵌入答复,前端据此渲染。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 17:24:41 +08:00
Blizzard d7c6fab933 fix(hitl): 审批弹窗可靠化(状态轮询兜底)+ 审批节点入调色板并改橙色
两个实测发现的问题:
1. 审批条偶发不弹、任务卡「运行中」:exec 的 await 事件走实时 core NATS(无回放),
   UI 的 EventSource 晚连一拍就漏掉 → 永远不弹审批。改为轮询持久化的任务状态
   (GET /tasks/:id,每 1.5s),status==waiting 即弹审批条,不再依赖易抢跑的 exec 事件;
   exec 事件仅用于丰富摘要。新增 api.taskStatus + RunState.lifecycle/detail +
   App.tsx 轮询 + 终态停轮询;BottomDrawer 审批条触发改以 lifecycle==waiting 为准。
2. Studio 左侧调色板没有「人工审批」节点(只加了 NODE_KINDS,漏了 NODE_ORDER),
   且其配色与「输入」同为 amber 难以区分:补进 NODE_ORDER(分支与并行之间),
   配色改 orange(橙)与输入的 amber(琥珀金)区分。

验证:桌面端 live 重启确认审批条可靠弹出 + 批准/拒绝两条路径正常;左侧节点列表与
画布配色区分清晰。tsc 干净 + vitest 36 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:51:37 +08:00
Blizzard 16a6c4b1aa feat(hitl): 人工审批中断(Eino Phase D)—— 审批节点暂停→批准续跑/拒绝中止
专用「审批」节点方案,全栈打通。

后端:
- contract:TaskWaiting/TaskRejected 状态 + ApprovalSubject/ApprovalDecision。
- bus:PublishApproval + WaitApproval(订阅决定主题,带超时);消费者 AckWait
  30s→15min(阻塞等人审期间消息未 ack,否则重投成重复任务)。
- dispatcher:ApprovalWaiter 接口 + approvalNode——执行到审批节点发 await 事件 +
  置 waiting,阻塞等决定。批准→回 running 放行下游;拒绝/超时→errRejected 哨兵→
  剪下游→rejected,优雅收尾不计熔断。超时安全默认拒绝。
  taskExecTimeout 3→10min(审批5 < 执行10 < AckWait15)。
- 网关:POST /tasks/:id/approve(仅 waiting 态受理,幂等)。

桌面端:
- Studio 新增「人工审批」节点(nodeCatalog,可填标题/说明)。
- run.ts pendingApproval() 从 exec 流派生待审中断 + waiting 节点状态。
- BottomDrawer ApprovalBar:琥珀审批条(摘要 + 批准/拒绝 + 备注,调 api.approveTask)。
- ExecTrace waiting 状态灯。

验证:后端 curl 实测 waiting→批准→running→done;waiting→拒绝→rejected(下游未跑)。
前端 tsc + vitest 36 过(pendingApproval 4 例 + waiting 状态)。全模块 build+vet+test 全绿。
EINO_ADOPTION Phase D 标记 HITL 完成。

未覆盖:compose 路径(EINO_COMPOSE,默认关)的 approval 节点;桌面端审批条未在 GUI 实点。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:18:47 +08:00
Blizzard f37d046f2c test(desktop): 前端单元 + 关键组件测试(Vitest + RTL,31 例)
测试基建:vite.config.ts 加 test 配置(jsdom + setup),package.json 加
test/test:watch 脚本,src/test/setup.ts 引 jest-dom;tsconfig 把测试文件 exclude,
使 CI 现有 tsc --noEmit 不检查测试文件(仍绿),测试由 Vitest 自跑(先不接 CI)。

覆盖(31 例 / 4 文件):
- dsl:exportDsl 节点/边映射 + config 兜底 + branch 真假边 sourceHandle 条件;
  validate 空画布/孤立节点/必填项缺失。
- version:isNewer 版本比较矩阵;checkUpdate 有更新/同版/旧版/404限流/离线异常;
  openExternal Wails / 浏览器双路径。
- run:deriveNodes start→running、end→done+耗时、error、error 不被 start 覆盖、
  info 累计 notes、首现顺序、label 更新。
- UpdateBanner(RTL):无更新不渲染、有更新显版本+点下载调 openExternal、点忽略消失。

小改:version.ts 导出 isNewer(原私有)以便直接测版本判定矩阵。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:53:54 +08:00
Blizzard 8c980d2b37 feat(desktop,ci): GitHub Releases 分发 + 桌面端检查更新
让桌面端像开源项目一样用 GitHub Releases 分发,旧版用户自动获知更新:
- CD:.github/workflows/release.yml —— 打 vX.Y.Z 标签触发,macOS(universal)/
  Windows(amd64) 用 wails build 出安装包,softprops/action-gh-release 发布到
  GitHub Release(GOWORK=off,因 desktop 不在工作区)。
- 桌面端检查更新:lib/version.ts(APP_VERSION + 查 GitHub /releases/latest +
  语义版本比较 + openExternal 走 Wails BrowserOpenURL);UpdateBanner 启动时
  检查,有新版顶部横幅「前往下载」。404/限流/离线均静默不打扰。

注:未签名/公证(macOS Gatekeeper / Win SmartScreen 需后续证书);release
workflow 需真实 tag push 才能验证(GitHub 侧运行)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:26:02 +08:00
Blizzard 95f4752d9e feat(desktop): Agent 节点加「自主工具 (ReAct)」开关
前端补上 Phase B 的 UI 接缝:agent 节点 nodeCatalog 增 autonomous checkbox
(默认 false)。勾选 → exportDsl 带 config.autonomous=true → dispatcher
走 ReAct 自主调工具。此前能力在后端但画布点不到。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 14:29:37 +08:00
Blizzard 0edfc948ba feat(memory): P2 读路径打分 + 桌面端记忆面板(看/改/删)
读路径(Generative Agents 公式的 Recency+Importance 两项,Relevance 待 P3):
- memory.Get 改为 rankProfiles:Score=0.4·Recency+0.6·Importance,按分降序、截断 top-30。
  Recency=0.98^天 指数衰减;未评分行用兜底 importance=5(不被不公平遗忘)。纯函数 + 单测。
- 新增 Store.List(结构化、不截断)+ memory_list 工具。

桌面端记忆面板:
- gateway GET /memory(列表) + DELETE /memory?key=(软删),受保护组。
- api listMemory/deleteMemory;MemoryView 右侧从占位 → 真列表:按分排序展示
  key/value/重要度/最近时间,可内联编辑(PUT)与删除(软删);左侧登记后自动刷新。

实测:PUT 两条 → GET 返回带 importance/last_seen 的有序列表(较新者靠前)→ DELETE 一条
(软删)→ 再 GET 已消失。rank/recency/兜底 importance 单测过;前端 tsc+构建通过。

至此记忆:召回(打分) + 历史 + 自动对账(P1) + 用户可管控(面板) 闭环。Relevance(Milvus) 留 P3。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 14:48:11 +08:00
Blizzard 9657a07bb5 feat(auth): 鉴权片2 —— 前端登录闭环 + 保护路由 + 去掉 header 兜底
把 JWT 鉴权从后端核心闭环到端到端:

后端:
- middleware.RequireAuth:上下文无已验证 uid 则 401;挂在 owner 作用域业务路由组。
- 路由拆 公开/受保护:公开=auth/health + 按 task_id 寻址的 SSE 与报告导出
  (EventSource/下载无法带 Bearer);受保护=tasks/memory/kb*/agents/reports/billing。
- userID(c) 去掉 X-User-ID 兜底,仅信任 JWT 注入的 uid。
- 修 CORS:Allow-Headers 增 Authorization(否则浏览器拦截带 Bearer 的请求)。

前端:
- lib/api:token 存 localStorage + Bearer 头(不再发 X-User-ID)+ authRegister/Login/Me
  + 401 清令牌并广播 sdx:logout;submitTask/report/memory/列表加载走 Bearer 与 401 守卫。
- views/Login:登录/注册全屏门。
- App:启动校验令牌 → 无则渲染 Login,有则进主应用;identity.userId=已验证 user.id;
  监听 sdx:logout 回登录页。
- TopBar:去掉可编辑身份输入,改显登录用户 + 登出。

实跑验证(docker+gateway+preview):
- RequireAuth:无 token /kb/list、/agents → 401;/health → 200;带 token → 200。
- 前端:无 token 显登录门;注入有效 token 重载 → 进主应用、顶栏显 Dexter、KB 加载本人库、
  隔离徽标显雪花 uid。控制台无错、生产构建通过。
- 过程中发现并修复 CORS 缺 Authorization 头的真实 bug。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:32:00 +08:00
Blizzard 1dd6b0cce3 feat(report): 生成只出 Markdown 预览,导出时再渲染 Word/PDF/Markdown
把"渲染"从生成阶段解耦到导出阶段(导出时再处理):
- 生成阶段:报告正文按 Markdown 流式预览(前端 <Markdown> 已渲染),
  dispatcher 不再 eager 渲染 docx,改为经 mcp-go report_store 落盘报告源(title+sections JSON)。
- 导出阶段(按需现渲染):
  - GET /reports/:id/export?format=docx → mcp-go report_export 读源渲染 .docx;
  - ?format=md → 返回 Markdown 文本;
  - PDF → 前端把已渲染的 Markdown 送进打印视图出 PDF(CJK 零字体依赖)。
- 旧 /reports/:id/download 兼容保留(默认 docx)。

改动:
- contract: ReportSourcePath(id) = <id>.json。
- mcp-go: 新增 report_store / report_export 工具(report_render 保留给 Studio render 节点)。
- dispatcher: handleReport 末尾 renderReport → storeReport。
- gateway: DownloadReport → ExportReport(经 NATS 调 report_export)。
- 前端: ReportView 单个「Word」→「导出」组 Word/PDF/Markdown;
  desktop.printReportHtml 客户端打印;api.reportExportUrl。

实测(docker 全栈 + mcp-go + gateway + dispatcher + DeepSeek 真跑):
- 真实生成「绿茶的功效」18s 完成,report_store 落源(5章, 6280B) ✓
- export md 返回正确 Markdown(# 标题/## 小节/正文) ✓
- export docx 为合法「Microsoft Word 2007+」(含 document.xml/Content_Types) ✓
- 前端 tsc 干净 + 生产构建通过 ✓
(注:发现并修复一处环境问题——mcp-go 启动时若 Milvus 未起会阻塞在
 rag 初始化、永不订阅工具,导致所有 mcp-go 工具"no responders";起全栈后正常。
 报告生成在累积大量未完成 DeepSeek 流连接时会偶发卡顿,干净进程下正常。
 前端导出按钮的实时点击因 React 受控输入自动化限制未在预览中走通,非代码缺陷。)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:04:06 +08:00
Blizzard 1bd187874d feat(orchestration): Phase2 —— map 真并行 fan-out + branch 真/假边标签精确选路
Phase1 让引擎按图执行后,本轮补上两块:

1) map 并行 fan-out(dispatcher)
   - map 节点:planItems 把主题拆成 3–6 子项 → 复用 report 的 writeSections
     有界并发(4)逐项撰写 → 结构化 sections 存黑板 + 拼进 answer + 流式呈现。
   - 检索节点记下 owner 作用域库名(b.kb),供 map 各项并行检索复用。
   - render 节点优先用 map 产出的多章 sections 渲染,否则整段成稿当单章。

2) branch 真/假边标签(前端 + DSL + dispatcher)
   - TypedNode:分支节点渲染两个出口手柄 真(绿)/假(红),连线各带 sourceHandle。
   - exportDsl / TaskDsl:边导出携带 sourceHandle。
   - dispatcher dsl.Edge 增 SourceHandle;branchNode 优先按 true/false 标签精确
     选路,无标签的旧图退回"出边顺序"约定,向后兼容。

实测(gateway+dispatcher+DeepSeek 真跑):
- map:input→map→render,DeepSeek 拆出 5 章并行撰写(347–512字),trace 见
  section:0..4 并发 + 有界并发(section3 等槽);render 因 mcp-go 不在优雅降级 ✓
- branch 标签:把 true 边故意列第二位,条件真仍走 true 标签的分支A、假走分支B,
  证明按标签而非边序选路 ✓
- 桌面端:分支节点正确渲染 真/假 两手柄,无 console 报错 ✓

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:01:51 +08:00
Blizzard 5d76652bff refactor(kb): 文件主表 + 文档关联改用雪花ID(弃用按名关联)
把 sundynix_doc 明确为"文件主表",补齐文件基础信息字段;
文档间 [[双链]] 改为以 Doc.ID 关联,查询/渲染一律按文件 ID。

store:
- Doc 增 Ext(后缀)/MD5(内容指纹) 字段;ObjectKey 即"存放链接"
- DocLink 由 (FromName,ToName) 改为 (FromID,ToID,ToName)
  · FromID/ToID 关联 Doc.ID;ToName 保留用于悬空链接展示与回填
- SaveDoc 返回新建/更新文件的雪花 ID(供建链)
- 新增 GetDocByID(按 ID + owner 取正文,防越权)
- ReplaceDocLinks 以 fromID 重建出链,按 [[名称]] 解析目标 ID
- 新增 ResolveInboundLinks:目标入库后回填指向它的悬空链接
- ListLinks 只返回已解析(to_id 非空)的 ID→ID 边
- migrateDocLinkToID:旧按名双链表无 from_id 列则重建为按 ID 关联

gateway/handler:
- runIngest 计算 ext/md5,SaveDoc 取回 ID 后建链 + 回填悬空
- KbDoc 改为 GET ?id=(按文件 ID 取全文)
- KbVault 返回 id+ext;KbLinks 返回 from/to 为 ID

desktop:
- VaultDoc 增 id/ext;getDoc(docId) 按 ID 取正文
- VaultPanel 选中态/正文/反链/关系图改用 ID,名↔ID 双向映射
  渲染;保存笔记后按名定位回其新 ID

验证(gateway+PG+MinIO 实测):vault 带 id+ext;双链 ID→ID 且
A→B 悬空链接在 B 入库后成功回填;按 ID 取大文档(15006字)从
MinIO 完整取回;跨 owner 按 ID 取文档 404(隔离生效)。桌面端
文库 Tab 按 ID 选中/载入/反链渲染正常,无控制台报错。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:38:02 +08:00
Blizzard 69967ea534 refactor(kb): 文库列表/正文分离 + [[双链]]索引表(可扛大文件)
不再一次拉回整库正文、不再前端扫全文 —— 列表只读元数据,正文按需取,链接走索引。

- store: SaveDoc 维护 size+preview(前 500 字);ListVault 仅 Select 元数据(name/size/preview,
  不含 content);GetDoc 取单篇全文;DocLink 表 + ReplaceDocLinks(入库/编辑时按 from 重建出链)
  + ListLinks。
- gateway: 入库/笔记保存时正则抽 [[链接]]→ReplaceDocLinks 维护索引;
  /kb/vault 改返元数据+预览;新增 /kb/doc(单篇全文) 与 /kb/links(全库双链)。
- 前端:listVault 返元数据,新增 getDoc/listLinks;VaultPanel 列表只展示名/字数,
  选中后 getDoc 按需载正文(带加载态),反链/笔记关系图改用服务端 links 索引(不扫全文)。

验证:curl /kb/vault 仅 name/size/preview;/kb/doc 取单篇;/kb/links 返 3 条双链。
Preview:文库点「架构总览」按需载正文(平台分五层)、反向链接(1)=Dispatcher(来自索引)。tsc+vite+gateway build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:53:23 +08:00
Blizzard e1cac0eb69 fix(studio): 载入编排防御兜底,避免损坏图导致黑屏
点击「我的编排」里某条若其 graph 缺 position/type/data(旧数据/外部写入),
React Flow 缺 position 会直接崩成黑屏。loadGraph 改为对每个节点兜底:
补 type=typed、缺失或非法 position 给网格默认坐标、data.kind 不识别则回退 output、
补 label/config/status;边过滤掉两端不存在或缺 source/target 的,自动补 id。

验证(Preview):故意存一条节点全缺字段的「损坏测试」→ 载入渲染为 2 节点·1 连线、
渲染器正常(不再黑屏);正常「尽调问答 Agent」4 节点·3 连线照常载入。tsc+vite 通过;重建 .app。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:28:21 +08:00
Blizzard 4bf614a07c feat(studio): Agent 编排服务端保存 + 我的编排列表(owner 隔离)
编排好的 Agent 现在可命名保存到服务端、跨会话可见;左侧「我的编排」列出本人全部。

- store: sundynix_agent 表(owner+name 唯一,Graph=React Flow {nodes,edges} JSON 含布局,
  UpdatedAt);ListAgents(最近在前)/SaveAgent(OnConflict 覆盖图+时间)/DeleteAgent。AutoMigrate +Agent。
- gateway: GET/POST/DELETE /api/v1/agents(owner 隔离,身份取自 X-User-ID)。
- 前端:api listAgents/saveAgent/deleteAgent;StudioView 左面板下半区「我的编排(N)」列出本人编排,
  点击载入(含布局)、悬停删除;工具栏 编排名+保存(服务端),去掉 localStorage 模板。

验证:curl 保存「合同审查流程」→ wt 列表含之,alice 列表为空(隔离)。Preview:示例图填名「尽调问答
Agent」保存 → 左「我的编排(2)」即时出现两条、可点载入。tsc+vite+gateway build 通过;重建 .app。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:23:03 +08:00
Blizzard 337d4d7619 feat(studio): 完善编排 —— 检索接本人知识库 + 真实模型下拉 + 模板/示例
把编排从"演示桩"接到真实平台:检索节点查本人 owner 隔离的知识库,节点下拉用真实数据。

- dispatcher:makeToolNode 用 task user_id 给检索类工具的 kb 加 owner 前缀("uid/kb"),
  编排里的「检索(RAG)」节点真正命中本人知识库(与隔离对齐)。
- 前端 StudioView:加 identity,载入 /kb/list 与 chat 模型作为「检索.kb」「Agent.model」下拉真值;
  Inspector 支持 dynamicOptions(无真值时提示去创建)。
- 编辑体验:示例(一键加载 输入→检索→Agent→输出 可运行图)/ 清空 / 模板名+保存(localStorage,
  含布局)/ 载入下拉;ReactFlow deleteKeyCode 支持 Del/Backspace 删节点。

验证:示例图运行 → gateway 发布任务 → dispatcher 编译 → mcp-go 日志 `tool=wiki_search
args=[kb:wt/default ...]`(kb 已按 owner 作用域)→ 命中本人库 → DeepSeek 流式作答;
底部抽屉 完成 ✓ · 工具调用 1。tsc+vite+dispatcher build 通过;重建 .app。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:14:54 +08:00
Blizzard a222ca5f9e feat(kb): 知识图谱换 react-force-graph-2d(活物理 + 拖拽 + 缩放)
把自建静态 SVG 力导向换成 react-force-graph-2d(canvas + d3-force)——真实物理、
可拖拽节点、滚轮缩放、悬停高亮邻域。实体图谱与笔记关系图同一组件,全部升级。

- GraphView 重写:ForceGraph2D,nodeCanvasObject 自绘节点(按度着色/缩放)+标签,
  linkCanvasObject 放大后显示关系文字,onNodeHover 高亮邻域、onNodeClick→onNode(笔记图跳转)。
- 调力:charge=-180 / link distance=52 拉开布局;onEngineStop zoomToFit 自动取景。
- 保持原 props(triples/height/onNode),三处调用零改动。

验证(Preview):笔记关系图渲染 笔记B/项目A概述/模块X 大节点 + 链接边、自动取景、可拖拽缩放。
(实体图谱在 default 库因累积了几十个测试实体显得密,新建干净库则清爽。)tsc+vite 通过;重建 .app。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:55:34 +08:00
Blizzard 10ac5a5277 feat(kb): 笔记可编辑(按 doc 替换重索引)+ 笔记关系图([[双链]])
Obsidian 化继续:笔记能编辑/新建,文档间 [[双链]] 连成可点关系图。

按 doc 重索引(编辑不重复累积):
- Milvus 加 doc 字段(旧 schema 自动重建);insert 带 doc;deleteDoc(kb,doc) 重入库前清旧块。
- Bleve 索引 id 含 doc + deleteDoc 按 kb+doc 清旧块。
- rag.Ingest(kb, doc, text):写入前按 doc 删旧块再写(Neo4j MERGE 仍幂等,附加式)。
- kb_ingest 工具加 doc 参数;gateway runIngest 把 doc 透传,forceDoc 支持编辑保持笔记名稳定。

编辑/新建:
- gateway POST /kb/note {kb,name,content}:落库 + 以 name 为 doc 重入库(替换旧块,搜索/图谱同步)。
- 前端 VaultPanel:阅读/编辑切换(textarea 预填原文,保存调 saveNote)、新建笔记、乐观更新。

笔记关系图:
- GraphView 加 onNode(节点可点);VaultPanel 阅读/关系图切换,关系图 = 文档间 [[双链]] 三元组
  力导向(点节点跳转该笔记)。

验证:curl 编辑 笔记B → 检索只返编辑后内容(旧块已清,不重复)。Preview:关系图渲染
笔记B—链接→项目A概述/模块X 且节点可点;编辑器预填原文可改可存。tsc+vite+后端 build 通过;重建 .app。

注:Milvus 加 doc 字段会触发集合重建(旧向量丢,文库原文在 PG 可重灌);Neo4j 图谱按附加式合并,
编辑删除的实体不会自动消失(图谱倾向增长)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:22:03 +08:00
Blizzard 55c85302b6 feat(kb): Obsidian 式文库 —— 笔记浏览 + [[双链]] + 反向链接(Tab 化)
把知识库做出 Obsidian 感:入库的每份文件/笔记留原文,可浏览、可读、可互链。

- store: sundynix_doc(owner+kb+name 唯一,存原文),SaveDoc(OnConflict 覆盖)/ListVault。
- gateway: runIngest 留存原文(文件用文件名、文本用首行作笔记名);GET /kb/vault?kb= 取文库(owner 隔离)。
- Markdown 组件:解析 [[名称]] / [[名称|别名]] → onLink 可点(Obsidian 双链)。
- KbView 改 Tab(入库 / 文库 / 检索 / 图谱):
  - 文库 = 左文档列表 + 右 Markdown 笔记([[双链]]点击跳转)+ 反向链接面板(扫全库 [[本笔记]])。
  - 检索、图谱各占整页;图谱放大到 460。

验证(Preview):入两条带 [[双链]] 的笔记 → 文库列出 2 篇 → 打开「项目A概述」渲染出可点的
[[模块X]][[模块Y]] + 反向链接显示「模块X」→ 点 [[模块X]] 跳转到该笔记、其 [[项目A概述]] 亦可点。
curl 证隔离:alice 取 wt 的 vault → 空。tsc+vite+gateway build 通过;重建 .app 重启窗口。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 15:06:31 +08:00
Blizzard 3a175e46f3 feat(kb): 批量文件入库(文件列表) + 项目/案件知识库 + owner 作用域隔离
回应三点诉求:一次入一批文件、按文件夹/项目/案件组织、且只有我能查我的库。

隔离(核心):知识库实际分区键 = "owner/name",owner 由网关从 X-User-ID 注入,
客户端只发库名、发不了 owner —— 故任何人都只能查到自己 owner 前缀下的数据。
- gateway: scopedKB(owner/kb) 注入 ingest/search/graph;ingest/search/graph 全部带身份头。
- store: sundynix_kb 注册表(owner+name 唯一 + kind),ListKB/EnsureKB(OnConflict DoNothing)。

项目/案件组织:
- gateway: GET /kb/list(owner 隔离列表)、POST /kb/create(folder/project/case/general);
  入库时 EnsureKB 自动登记。
- 前端: KbView 顶部知识库下拉 + 新建(项目/案件/文件夹/通用),检索/图谱/入库都绑定所选库。

批量文件:
- 前端: 选择文件(multiple) + 选择文件夹(webkitdirectory) + 拖拽一批 → 每文件一个 job,
  文件列表实时显示各自状态(排队/解析/向量化/写入/抽取/完成/失败)+ 完成/失败计数。

验证:curl 证隔离 —— wt 入 default→可检索;alice 查同名 default→[] 空;alice 列表不含 wt 案件库。
Preview 证 UI —— 知识库下拉含 案件-2024-001(案件)+default(通用)、owner 隔离徽标、批量/文件夹按钮。
tsc+vite+gateway build 通过;重建 .app 重启窗口。

注:身份目前来自 X-User-ID 头(可信前端),生产应换 JWT 鉴权中间件——隔离机制(owner 前缀)已就位。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:50:33 +08:00
Blizzard 84efa0a11c feat(desktop): 命令面板 ⌘K —— 键盘优先工作站入口
Pillar ① 交互骨架升级第一步:全局命令面板。

- components/CommandPalette.tsx:⌘K/Ctrl+K 唤起,搜索 + 多词子串过滤 + 分组(页面/动作)
  + 键盘上下选择 + Enter 执行 + Esc 关闭 + 选中项滚动入视野,纯前端两模式通用。
- App:全局 keydown 监听切换面板;命令清单(8 个页面跳转 + 生成报告/入库知识/新建编排动作)。
- TopBar:加「搜索与命令 ⌘K」触发 pill。

验证(Preview):⌘K/点 pill 唤起 → 输入“报告”过滤出 2 条 → 点「前往·报告生成」自动跳转
报告页并关闭面板。tsc + vite build 通过;重建 .app 重启原生窗口。

注:OS 级全局唤起(⌥Space 后台常驻 + 系统托盘)受 Wails v2 主线程模型限制(与
golang.design/x/hotkey 抢主线程冲突),留作 Wails v3 / cgo 助手的后续,不在本次。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:32:36 +08:00
Blizzard 72e008bfe8 feat(kb): 入库可视化做厚 —— 文件解析/知识抽取过程 + 力导向知识图谱
把"进度条"升级成可观测的入库工作台,回应三点诉求:解析过程、知识抽取过程、丰富图谱。

- contract: IngestEvent 加 Preview(解析文本预览)+ Triples[]TripleView(抽出的三元组)。
- 后端回流:rag.Ingest 抽实体阶段把 LLM 抽出的三元组实时回流(边出现边渲染);
  gateway 解析完成回流文件类型 + 文本预览片段。
- 前端 GraphView.tsx:零依赖自建力导向布局(斥力+边弹簧+居中静态收敛),实体=节点
  按度着色(枢纽紫/关联青/叶子)、关系=带标签边、hover 高亮邻域、节点过多按度裁剪。
- 前端 KbView 重做:入库从"阶段徽标+进度条"→竖向时间线(解析预览/切块块/向量化进度/
  抽取知识三元组 chips + 实时小图谱逐步浮现);右侧知识图谱从扁平列表→GraphView,
  入库完成自动刷新整库图谱。

验证(Preview):入库一段多事实文本 → 时间线逐阶段点亮、抽出 17 条三元组实时浮现、
右侧力导向图渲染 sundynix-agentix/知识库 为枢纽 + 带标签关系边。tsc+vite+后端 build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:22:50 +08:00
Blizzard d5dfb7a928 fix(rag,desktop): Milvus 集合丢失自愈 + 检索框布局塌陷
真实演示中暴露的两个 bug:

1) Milvus 重连健壮性(mcp-go/internal/rag/milvus.go)
   基础设施重启后向量集合丢失,但 ensure() 的 m.ok 缓存认定集合仍在、跳过重建,
   导致 insert/search 报 "collection not found",必须重启进程才恢复。
   修复:新增 invalidate() + isCollectionGone();insert/search 遇"集合不存在"类错误
   时清缓存 + 重 ensure(重建集合)+ 重试一次。
   实测:运行期 drop 集合后再入库 → 日志"清缓存重建后重试写入" → 写入成功且可检索(自愈,无需重启)。

2) 检索框布局塌陷(desktop frontend/src/ui/Input.tsx)
   Phase A 给 Input 基类内置了 w-full,与检索行调用方的 flex-1 / w-16 冲突,
   查询框被挤成一条缝。修复:基类去掉 w-full,宽度交由调用方(Field 内 flex-col 自动撑满,
   或显式 w-full/flex-1/w-16)。
   实测(Preview):查询框 412px、topK 64px;报告页输入仍撑满(764/220px),无回归。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:05:53 +08:00
Blizzard d5ae2f71d4 feat(desktop): 工业化升级 D —— 内容可信(Markdown 渲染 + 健康五灯全真)
- components/Markdown.tsx:零依赖、行级 Markdown 渲染(# 标题 / **粗** *斜* `码` /
  - 与 1. 列表 / > 引用 / --- 分隔 / 段落),流式安全(每 token 重渲染容忍残缺)。
  报告正文与运行输出从裸 <pre> 换成真排版,瞬间像份报告。
- 健康聚合:mcp-go 加 rag.Status() + health 工具(milvus/neo4j/embedding 就绪);
  gateway GET /api/v1/health 聚合 gateway/nats/db/redis(本地) + milvus/neo4j(经 mcp-go);
  health.ts 轮询 /health,TopBar 五盏灯(Gateway/DB/NATS/Milvus/Neo4j)从"灰=未知"变真实绿/红。

验证:浏览器(Preview)跑报告——正文以标题/有序列表/引用/分隔线/二级标题排版呈现;
五盏灯全绿(/health 返回 db/gateway/milvus/nats/neo4j/redis 全 true)。tsc + vite build + 后端 build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:34:54 +08:00
Blizzard 4d9d1ac615 feat(desktop): 工业化升级 B —— 真桌面集成(Wails Go 桥 + 原生能力)
让它真正像桌面 App 而非套壳网页:启用闲置的 Wails Go 桥,接原生文件框/系统打开/
通知/无边框标题栏,且全部对浏览器预览(make web)优雅降级。

- app.go:SaveReportAs(原生"另存为"框 + 下载落盘)、OpenReport(下到临时目录 +
  系统默认应用打开 docx)、Notify(macOS osascript 通知)、download/openInSystem 跨平台
- main.go:macOS TitleBarHiddenInset —— 隐藏标题栏、内容铺满到顶、保留红绿灯交通灯
- lib/desktop.ts:window.go.main.App 运行时桥 + isDesktop/isMacDesktop 探测;
  saveReportAs/openReport/notify 在无 Wails(浏览器)时分别降级为 <a download>/新标签/Toast
- ReportView:桌面端「另存为 Word」(原生框) +「用系统打开」+ 完成弹系统通知;
  浏览器端保持「下载 Word」
- KbView:拖拽文件入库(HTML5 dataTransfer,两种模式通用)+ 拖拽高亮 +「选择文件」按钮
- TopBar:顶栏设为 Wails 可拖拽区(--wails-draggable),控件标 no-drag;
  macOS 桌面端左留白让位交通灯

验证:GOWORK=off wails build 打出 .app(绑定生成 + mac 标题栏);启动真实原生窗口
截图确认无边框标题栏 + 交通灯内嵌 + 顶栏可拖拽(见会话截图);浏览器(Preview)确认
window.go 不存在时降级正确(下载链路 + 拖拽占位)。tsc + vite build 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:04:16 +08:00
Blizzard 72bd43965f feat(desktop): 工业化升级 A —— 设计系统地基(primitives + lucide + 语义令牌)
把"手搓内联 class + Unicode 字符图标"换成统一组件与真实图标,为后续工业化打底。

- 依赖:装 lucide-react(描线图标,按需 tree-shake)
- 令牌:tailwind.config 加语义色 brand/accent/success/warn/danger + 圆角档位;
  强调色字面量(violet/cyan/emerald…)收敛到令牌,便于整体换肤
- primitives(src/ui,零重依赖自建):Button/Input/Textarea/Select/Field/Card/Panel/
  Badge/Dot/Tabs/Skeleton/EmptyState/Dialog/Toast(+useToast)/cn,桶文件统一引入
- 迁移:TopBar/LeftNav/BottomDrawer + Home/Report/Runs/Kb/Placeholder/ExecTrace/
  MemoryPanel/StudioView 全部换 primitives + lucide 图标;导航/能力卡/按钮告别
  ▤◆▣▦ 等 Unicode 字符;错误改用全局 Toast;空状态用 EmptyState
- App 包 ToastProvider

验证:tsc + vite build 通过;浏览器(Preview)走查工作台/报告页——真实图标、统一卡片/
按钮/输入;跑报告端到端正常(执行轨迹 lucide 状态图标点亮、章节耗时/字数/检索片段、
完成弹 Toast + 下载 Word)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:39:42 +08:00
Blizzard cdc5b3a847 feat(observability): 执行可视化 — 节点级实时轨迹(运行·观测)
把任务执行做成可观测:Dispatcher 在每个节点/阶段发结构化 ExecEvent,
经独立 NATS 通道回流,前端逐节点点亮(状态/耗时/工具入参产出)。

- shared: contract.ExecEvent + ExecSubject(sundynix.exec.<id>,与 Token 流分流);
  bus.PublishExec/CompleteExec/SubscribeExec(core NATS,复用结束头)
- dispatcher: execTracer(自增 Seq 保序 + span 自动计耗时);
  Orchestrator 加 ExecSink;通用图(init 召回 / 各 tool 入参→产出 / prompt / model
  首token+token数)与报告编排(规划大纲 / 各章并行 start-end / 渲染)全程埋点
- gateway: SubscribeExec + GET /tasks/:id/exec SSE(与 token 流并行)
- desktop: streamExec + deriveNodes(按 node 归并 start/end/error/info);
  复用组件 ExecTrace(竖向轨道,按 kind 着色,运行中脉冲灯);
  新 RunsView(运行·观测:轨迹+输出双栏);BottomDrawer 轨迹/工具调用 tab 接真实数据;
  ReportView 加执行轨迹栏;左导航「运行」置就绪

实测:
- 报告任务 /exec:规划(2680ms,4章) → 4 章并行(seq 交错,各~7-8s 重叠=真并行,
  每章带 docs 知识库检索预览+成稿字数) → 渲染(docx 落盘)
- 通用图 /exec:tool:kb_search(678ms,入参→Milvus 产出) → prompt(2消息) →
  model(首token 860ms / 4 tokens)
- 浏览器(Preview):报告页执行轨迹逐节点点亮、章节带耗时/字数/检索片段,完成后下载 Word

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 14:29:28 +08:00