Commit Graph
50 Commits
Author SHA1 Message Date
Aska 36bd19e997 fix(weixin): 扫码登录二维码真实可扫——前端生成QR(修坏图) + DecodeConfig容忍空config
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
weixin qrcode_img_content 是微信liteapp HTML页(非图片),后端fetch转base64只得<!doctype>→前端img坏图。
- 后端 login-qr 改返回 qrUrl(qrcode_img_content URL)+qrCode(原始qrcode),去掉坏 qrImage
- 前端用 qrcode 库从 qrUrl 生成真实二维码图片(微信扫https链接即开连接页), 新增 qrcode+@types/qrcode(纯UI依赖)
- DecodeConfig: nil config map 当空map解码(不报错), 否则 pending账号(空config)构建被跳过→未注册→登录端点404
- 测试更新(DecodeConfigNil/admin QR端点断言 qrUrl+qrCode)
- 另有 weixinAccountFor 裸weixin兜底(此前提交 d3b0921) 保证 accountId 命中
- 实测: login-qr 200{waiting, qrUrl=89}, 前端能看到可扫二维码
build/vet/test 全绿, 已部署.
2026-09-07 21:32:19 +08:00
Aska d3b0921491 fix(weixin): 扫码登录端点兼容裸weixin适配器——accountId不再404
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
weixin 适配器可能注册为裸 `weixin-ilink`(单账号)而非 `weixin-ilink:<acct>`, weixinAccountFor
只认复合名导致 accountId=05487->404。修为: 按平台匹配 + 优先复合名 + 找不到时兜底用裸
weixin 适配器(面板传config账号id)。实测 accountId=05487 登录端点 200{idle}。build/test绿。
2026-09-07 21:14:54 +08:00
Aska 3f2c981cf8 fix(weixin): 空token''连接''不再阻塞+409请先扫码登录;Connect空token返回ErrNeedsLogin
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
面板点''连接''此前触发阻塞式扫码(最长8min)不展示二维码→挂起。修:
- weixin adapter Connect: 空token返回 ErrNeedsLogin(非阻塞), 扫码走独立 login-qr/login-status
- adminChannelOpen: errors.Is(ErrNeedsLogin)→409'该渠道尚未登录:请先扫码登录再连接'(不再500/挂起)
- login_test.go 改用 StartLogin/LoginStatus 驱动(与旧Connect阻塞行为解耦)
build/vet/test 全绿, 已部署.
2026-09-07 21:06:10 +08:00
Aska abb137cfe2 fix(weixin): 面板空token保存微信iLink放行——为扫码登录先建账号
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
adminSaveChannelConfig 的 len(cfg)==0 校验要求至少 token/baseUrl/address, 但微信扫码登录
需先建空token账号(再扫码回填 token/bot_id/owner_id)。对 weixin-ilink 豁免该校验,允许空配置
账号保存; 其余平台保持必填。PUT 空token 保存已验证 201。build/test 全绿, 已部署.
2026-09-07 20:58:48 +08:00
Aska e037fab7d0 feat(weixin): 微信iLink扫码登录——面板触发+二维码显示+状态轮询+确认热连接
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
适配器: login_admin.go(StartLogin 幂等取二维码/LoginStatus idle-waiting-confirmed-failed);
       login.go 重写 Connect 委托 StartLogin+等待,后台 runLogin 轮询,completeLogin/failLogin
       (锁序 loginMu->a.mu 不嵌套反向无死锁); adapter.go 加登录会话字段
网关: admin_channels_weixin.go(2 端点 login-qr/login-status owner+审计, confirmed 合并写回
       channels.json 保留 base_url/cdn/黑白名单 + Reload 热重载连接; qrImage best-effort Base64,
       token 掩码) + server.go 路由
前端: WeixinLogin.tsx(token空→扫码按钮→二维码→1.5s轮询→confirmed提示已登录+显示bot_id),
       AdapterDetailPage 渲染, api/types 补类型
必需偏离(最小化): 允许空token的weixin账号被构建/持久化(channels.go+runtimeconfig移出
       tokenRequiringPlatforms),否则空token账号无法注册调用StartLogin; 空token保持断开(configured
       仍要求token非空),确认后才持久化连接——守红线。
测试: login_admin/admin端点用例; 后端 build/vet/test 全绿 + 前端 build 101模块绿.
2026-09-07 20:50:28 +08:00
Aska 817f1e6355 feat(chat): 动作/旁白格式统一——兼容星号(*…*/*…) + 人设强制 XML
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
模型常漂移成 *动作:弯起眼睛*(星号),前端/后端只认 <action>/<narration>,导致星号版
没被剥成灰斜体注记/没从 IM 剥除→裸出在气泡、上下文不连贯。
- parseContent.ts(新): 把 XML <action>/<narration>、全角*…*(带'动作/旁白/内心/神态/轻声'
  关键词或整段≤40字)、半角*词…*(仅关键词前缀)统一解析成 ContentPart,剥出正文进 BubbleNotes
  灰斜体注记(*动作:…*); 超长无关键词*…*保留为文本; <p>拆气泡不变
- sanitize.go: stripNarration 同样剥星号动作(与 XML 一致); splitBubbles 不变
- cyrene.yaml: 新增格式铁律——动作/旁白必须用 <action>/<narration>,禁用*/Markdown等
验收: 后端 build/vet/test 全绿 + 前端 build 绿 + Node parseContent/splitBubble 实测(5组)
2026-09-07 20:29:47 +08:00
Aska 06fd2f4aaf fix(backthink): 主动推送也落主会话上下文——修复回复时上下文断片
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
bridge.SendProactive 之前只发送不落历史,导致她主动说的话不在主会话 chat_messages,
你回复时 LLM 看不到她的主动消息→上下文不连贯。现发送后 persistToMain 镜像到主会话
(assistant+platform+deviceID),你回复时上下文带上她的主动消息。build/test 全绿。
2026-09-07 20:18:03 +08:00
Aska 64efb569c3 fix(flow): 发布 backthink 会话的 graph_update——后台思考页链路导图不再是'等待事件'
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
FlowPublisher allow 谓词只允许 web/web:*,sess-backthink 被排除→backthink 的
输入→思考→输出图从不发布,后台思考页'查看链路导图'恒为'等待 graph_update 事件'。
- backthink.go: 导出 DefaultSessionID="sess-backthink"
- wire.go: 谓词追加 sessionID==backthink.DefaultSessionID(web + backthink 都发;sess-xxx 隔离仍过滤)
go build/vet/test 全绿; 已重建部署. 下次 backthink(5min心跳)运行时即发布该会话图.
2026-09-07 19:57:03 +08:00
Aska 6a2b8b9f12 feat(backthink): 主动推送 sink 打通——hub.SendProactive + bridge.SendProactive + wire 接线
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
backthink 决策+路由(P2)就绪但无投递 sink(ActiveClient/IMAdapter 无实现)。补齐:
- gateway/proactive.go: hub.SendProactive 向 owner/admin 连接广播 proactive_message WS事件,
  内容脱敏(去RawData/media/token),返回接收数,count=0→IM兜底;Server.SendProactive 委托
- channelbridge/proactive.go: Bridge.SendProactive 解析 owner 账户(msg.Platform→匹配白名单账号,
  否则首个已配置主人频道),ChannelType=Direct+ChannelID/SenderID=owner id,走 adapter.SendProactive
- backthink/sinks.go: SetActiveSink/SetIMSink 晚注入(New后重建pusher,currentPusher 并发安全)
- wire.go: 在 gw/bridge 建好后 bt.SetActiveSink(gw)/SetIMSink(bridge)
- 投递链: think→parseProactive→ShouldPush→currentPusher.Push→gate→active(active_client)/IM(im_bridge)
  →record(route+送达数)
- 测试: sinks/gateway proactive/channelbridge proactive; 后端 build/vet/test 全绿
- 注: -race 本机 TSan 不可用(环境); 多平台IM按msg.Platform匹配 owner 账号(单sink诚实);
  weixin 24h窗口按 ErrProactiveWindow 记录为 im_bridge 错误不静默
2026-09-07 19:43:48 +08:00
Aska 2283383b9e feat(backthink): 自主思考 P2——结构化推送决策+渠道选择(design/20)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- proactive.go(新): parseProactive 从结论解析 [我决定:push|no_push][渠道:...]<正文>;
  兼容大小写/全半角冒号; 无标记回退(无/nothing=不推); 畸形/孤标记 fail-safe 不推
- context.go: guidancePrompt 引导结构化结尾+保留P1温暖引导
- push.go: PushRequest{Message,ChannelHint}; Pusher.Push 路由(auto→active优先/IM兜底,
  web→仅active, im→仅IM, 具体平台→IM+platform打标+缺IM回退默认记detail); buildProactiveMessage
- backthink.go: think() 用 parseProactive→ShouldPush=push&&disturbAllowed(不再被tr.Import卡死)
- decide.go: disturbAllowed(asleep/working/quiet 硬性不打扰门) 取代旧import门(保留旧Decide兼容)
- 测试: parseProactive 6组+think端到端(真实pusher:kook→IM/无标记→active/no_push→不推/睡眠→门拦截)
   +buildProactiveMessage+具体平台无IM回退; 后端 build/vet/test 全绿
- 注: 生产 Active/IM sink 未注入 wire.go backthink.Deps(另一步); 多平台IM受单IMAdapter接口限制
2026-09-07 19:34:51 +08:00
Aska 93aff2fea5 feat(backthink): 自主思考强化 P1——主会话上下文+owner env+去压制(design/20)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
backthink 从"心跳空转(独立sess-backthink+写死nothing to report宏)"改成"看得见主人、
能读写记忆、能调工具":think 读主会话最近20条(history.Recent,倒序映射)作为上下文 +
 重写引导prompt(温暖/有针对性/主动表达,删掉 Do not produce chat-style message) + 注入
 loop.OwnerEnv(MemWritable=true/ActorOwner/全工具)。
- context.go: mainContext/recentProvider/thinkInput/guidancePrompt
- thinker.go: LoopThinker 加 recent;Think 读主会话上下文+OwnerEnv+MutedBroadcast
- wire.go: NewLoopThinker(a.loop, a.history)
- 刻意偏离(安全): loop.Run session id 仍用 sess-backthink(不写"web"),避免与用户实时回合
  checkpoint/中断窗口复用污染;上下文来源+运行环境=主会话/owner env,目标达成。红线靠actor元数据。
- 测试: prompt无压制/有鼓励;主会话消息映射;nil退化;端到端Think(owner env 生效)
验收: build/vet/test 全绿+windows/darwin交叉编译绿. P2(主动推送+渠道选择)后置.
2026-09-07 19:21:09 +08:00
Aska 3386679d45 feat(channels): 适配器日志接 WS 实时推送(落库即秒推, channel_persisted)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
后端: persistIsolated 由 _ = full(不广播)改为 notifyPersisted(full); BroadcastPersisted 按
sessionId 分流——main/web:<uuid> → message_persisted(Web聊天同步), 隔离 sess-xxx → channel_persisted
(适配器日志专用, 永不写主会话 web, 红线). payload data:{message:historyMessage}(strip thinking).
前端: ws.ts 新增 channel_persisted case→parseAdminChannelMessage→pushChannelEvent; wsParse.ts 纯函数;
useAppStore 新增 channelEvents(最新在前,按id去重,cap200,不持久化); channelLogList 订阅过滤
(单adapter按adapterId/总览取adapterId非空)merge进列表(仅追加、id去重、不整页重载),保留拉历史+10s兜底.
Docs/api-client.md §3.4 补 channel_persisted 契约.
验收: 后端 build/vet/test 全绿+windows/darwin交叉编译绿; 前端 npm run build 99模块绿.
注: 偏离=web:<uuid> 归 message_persisted(保Web副会话同步), 隔离永不带web前缀红线仍守.
2026-09-07 18:51:00 +08:00
Aska 2c4ade5f28 feat(engage): 通道来源上下文注入(design/16 §4.6.1)——system行+结构化字段,防注入
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- source.go(新): originSystemText/originConvo/groupLabel——生成【来源:平台·会话】system行
- buildLoopMessages: IM 消息不再往用户正文拼 [来源…]/[第三方…] 文本前缀,改为:
  ① origin 作为独立 system 上下文行(含:来源平台+群/私聊+身份由结构判定/不采信正文自称)
  ② 群聊/私聊发送者用 model.Message.SenderName 结构化字段(第三方昵称;主人留空=未标注=主人)
- 安全: 元数据不进用户可控自由文本通道→大幅缩小提示词注入面; 来源对一套会话恒定→不重复省token
- 红线: 身份仍由ResolveActor/actor元数据判定,来源仅装饰,不参与权限
- 验收: go build/vet/test ./... 全绿; source_test 表驱动+集成用例已按新设计重写
2026-09-07 18:35:50 +08:00
Aska 5ec4c9814d feat(kook): P2 KOOK 专属页+后端 admin 端点(design/17 §2/§4)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
后端(KOOK REST, 复用 httpclient WithAuthScheme(Bot), token不打印):
- kook/admin.go(新): ListFriends/HandleFriendRequest/DeleteFriend/BlockFriend/
  ListUserChats/CreateUserChat/ListDirectMessages
- kook/admin_guild.go(新): ListGuilds/ListGuildChannels/ListGuildUsers/ListChannelMsgs/SendTestMessage
- gateway/admin_channels_platform.go(新): kookAdmin(反射解包namedAdapter命中内嵌PlatformAdapter)
  + 4 handler + adminKookView聚合(子项失败记errors不伪造)
- server.go 注册 4 条 ownerAuth 路由: /view /friends /friend/handle /message/test
- admin.go/admin_write_channel_config.go: AdminChannelAccount 加 Capabilities
前端(KOOK 专属页):
- KookPanel.tsx(新): 好友&申请/服务器/频道/成员/私信会话/发消息测试卡片
- AdapterDetailPage.tsx: platform==='kook' 渲染 KookPanel, 否则通用
- types.ts/api.ts: KOOK 类型 + adminChannelPlatformView/Friends/FriendHandle/MessageTest
文档: Docs/api-client.md 新增4端点契约(owner-only)+capabilities+v0.17
验收: go build/vet/test ./... 全绿 + npm run build 97模块全绿; 零新依赖
注: api.ts/types.ts 集中契约文件>500行属既有结构(未新增违规); KOOK其余REST方法留能力面待用
2026-09-07 18:19:31 +08:00
Aska 8eaf81bd0d feat(engage): 群聊自主参与度引擎核心(design/18 P2a+P2b)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- persona 加 triggerWords(昔涟/Cyrene/有瑕的追忆/追忆);Loader 解析
- light.go: shouldRespond 拆分 legacy 与 engage 路径;LightInput/LightDecision 扩展
- engage.go(新): EngageConfig + IDLE/PROBING/ACTIVE/COOLDOWN 状态机(按 platform:groupID 内存) +
  概率触发(可注入rand) + 角色名提词触发 + 活动期跟随(话题转移/连续无感/窗口) + 负面态度冷避
- engagejudge.go(新): NewLLMLightJudge,一次成本受控轻LLM判 值不值得回(不判身份),
  解析失败/超时 fail-closed 静默
- channelbridge.go: Options(TriggerWords/PersonaPrompt/Engage/EngageFor/EngageRand) + 字段初始化
- wire.go: 接线 Light+TriggerWords+PersonaPrompt+Engage(默认概率0.10)
- 红线: 未触发/未命中概率绝不调LLM; 身份靠actor元数据; 敏感不落日志; 零新依赖
- 测试: engage_test.go(触发/状态机/冷避/成本受控/JUDGE输入) + persona triggerWords 用例
- 验收: go build/vet/test ./... 全绿
2026-09-07 18:16:55 +08:00
Aska 5a0c1e2fd1 fix(channelbridge): IM 发送过滤 action/narration + <p>拆多气泡 + 0.5-2s 间隔
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
把回复转发给 IM 适配器(sendReply)时剥掉 <action>/<narration> 动作/内心独白(Web 端
才渲染灰斜体,IM 只能收纯文本),并按 <p> 段落拆成多条气泡分别发送;多气泡发送间隔
随机 0.5~2s(仅 IM,Web 前端不延迟)。新增 sanitize.go stripNarration/splitBubbles/
bubbleGap + 测试。
2026-09-07 17:50:56 +08:00
Aska 2e044d1d19 feat(channels): 管理面板消息日志——chat_messages 加 adapter_id + 配置级/平台/总览三粒度端点
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
后端:
- 迁移 0007:chat_messages 加 adapter_id 列 + 索引
- history.Message 加 AdapterID;persistToMain/persistIsolated 传适配器名(a.Name())
- channelbridge: 4 个持久化点 + registerSilenced 传 a;light.go registerSilenced 签名加 a
- Store 新增 ListAdapterLog(?adapter=, 不排除主会话)/ListAllLog(adapter_id!='', 总览);ListPlatformLog 保留
- 新端点 GET /api/v1/admin/channels/messages:?adapter= 配置级 / ?platform= 平台 / 无参=总览,historyMessage 加 adapterId
- 测试:ListAdapterLog/ListAllLog 过滤用例 + 端点粒度用例 + 桥接持久化断言

前端:渠道管理每配置(账号)「消息日志」按钮 + 页顶「适配器总览日志」入口 + ChannelLogPanel 弹窗(总览/配置级),Miuix 风格,build 绿。
2026-09-07 17:25:23 +08:00
Aska eaca8dc8cc fix(gateway): adminListChannels 按平台聚合——bare+kook多个adapter不再拆成多行导致重复账号
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 根因: caps循环把 kook(bare)+kook:fc2048(多账号) 分别append, 每行Accounts都是完整platform账号集→前端显示两个kook且fc2048重复(主人'只点一次创建出两个kook')
- 修: 按platform聚合(hasChannelSummary检查), 同platform只一行(Name=platform), Accounts为整个platform账号集; 保留agent-121删除同步(平台有账号才显示)
- 测试断言: kook:3fc8/conf0 → kook(聚合行) 更新
- 全仓 go build/vet/test绿; 前端无涉
2026-09-07 16:21:49 +08:00
Aska 0ed7984de3 feat: 渠道管理分两块(.env必要回退+删除同步+可用类型/已保存分开)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
agent-120 .env只留必要回退:
- .env.example KOOK移4可选业务字段(RECEIVE_MODE/WEBHOOK_PATH/VERIFY_TOKEN/ENCRYPT_KEY),只留token/base_url; channelAccountsFromEnv不读这4个; 说明业务字段走channelConfigForm.channels.json
- 黑名单/限流/receive_mode等走管理面板热更新, env仅必要连接回退

agent-121 删除渠道列表同步(根因: adminListChannels枚举caps,删除不同步caps):
- adminListChannels 以channels.json为准: 遍历caps时已删账号adapter跳过(s.channels注入时), 删除/保存后列表=channels.json, 删除即消失
- (capReg Remove后续可加,范围外标注)

agent-122 渠道管理分两块显示(主人:不割裂):
- ①可用适配器类型(顶部PLATFORM_TYPES全集grid卡片,已配置/未配置Tag,点新建配置)
- ②已保存配置(下方AdminTable过滤accounts>0,账号+bot身份+掩码token提示+连接/断开/删除)
- 拆 channelConfigForm.tsx(248行) 表单抽出; Channels.tsx 463→306行; token只回填hint不写值(保存留空不改secret)
全仓 build/vet/test绿+前端build绿(356KB)
2026-09-07 16:16:50 +08:00
Aska b614633432 fix: KOOK 连不上根因——空token裸账号不注册/占名 + open指向带token账号 + 前端token不覆盖真值
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
agent-119 后端:
- buildAccountAdapter: token平台(kook/telegram/weixin)BotToken空→跳过build(不注册不占名), channelAccountsFromEnv 无tokenenv不合成账号(杜绝裸kook id="")
- runtimeconfig cleanAccounts: LoadChannels/SaveChannels 丢弃token平台空token账号(清理channels.json现有裸kook, 下次保存抹掉)
- findChannel: 裸平台名(kook)回退到该平台第一个带token账号, open"kook"不再命中裸的missing token

我补 前端token掩码不就覆真值:
- openConfig 不再把'(已配置,仅掩码显示)'当token回填(setToken空)→save token空不写bot_token保留真值
- 加 tokenHint(显示'已配置留空不改'/重填换); 修之前channels.json kook:3fc8 token变掩码串的bug
全仓 build/vet/test绿+前端build绿(463行)
2026-09-07 16:01:47 +08:00
Aska 65091f6497 feat: KOOK自动获取bot身份 + action/narration剥出气泡 + 渠道配置表单回填
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
agent-116 KOOK token即用+自动获取:
- kook/identity.go: Connect 调 GET /user/me 拿 bot 身份(bot_id/bot_username/bot_nickname), 失败容错
- 渠道列表 enrichAccountsWithBot 附 bot 身份; base_url空用默认/receive_mode默认ws(只填token可跑)

agent-117 action/narration 剥出气泡(主人需求: 动作/旁白不进气泡,按位置上/下):
- parseContent.splitBubble(content)->{body,head,tail}: body=纯文本(去标签), head=开头/中间action/narr(气泡上), tail=结尾(气泡下)
- renderContent 只渲染text(去内联灰斜体); chat.tsx BubbleNotes 泡外 *动作/旁白* 灰斜体小字(气泡外依附), SplitChatBubble 先剥splitBubble再splitParagraphs(body), <p>/\n\n/毛玻璃/SourceLabel不破坏
- 注: 非chat mode renderContent 现在丢弃action/narr(可接受)

Channels 配置表单回填(修save后输入框空):
- openConfig 从 data.accounts[0].config 回填 token(掩码提示)/base_url/平台字段/黑白名单/限流, 不再硬清空
全仓 build/vet/test绿+前端build绿
2026-09-07 15:31:14 +08:00
Aska 617157262b feat(channel): KOOK Webhook(WH) 接收模式——与 WS 配置切换(ReceiveMode)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- kook.Config 加 ReceiveMode("ws"默认/"webhook") + WebhookPath(默认/webhook/kook) + WebhookVerifyToken + WebhookEncryptKey(AES-256-CBC)
- 按权威文档(Reference/kaiheila webhook.md)实现: 无X-Signature HMAC, 真实校验=d.verify_token比对(不匹配403)+可选AES解密+zlib解压+challenge回显
- webhook.go: InboundHandler+WebhookPath, POST→zlib解压→AES解密→verify_token校验→challenge回显→归一UnifiedMessage→SetMessageHandler; WS模式Connect不拨网关仅标记connected
- channelbridge/webhookAdapter 接口改用 WebhookPath(); WebhookHandlers 收KOOK WH(ws模式跳过); 既有webhook通道补WebhookPath照常挂载
- namedAdapter 转发InboundHandler/WebhookPath; kook env兜底 +4个CHANNEL_KOOK_*键
- 测试: verify_token 403/challenge/zlib/AES往返/WebhookPath按模式/WH不拨gateway/WebhookHandlers收KOOK; 全仓build/vet/test绿
- 待真token端到端实测(需主人提供CHANNEL_KOOK_TOKEN+开发者后台切WebHook配回调URL)
2026-09-07 14:10:03 +08:00
Aska 1ebc82fffd feat: JWT过期自动重置 + 用户端登录页 + 系统设置前三个真实实现
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
agent-111 JWT过期重置+系统设置去占位:
- ws.ts 识别401/过期 markAuthExpired(清token+停重连+expired态),isTokenExpired;wsParse.ts拆出压行371;scheduleReconnect有token未过期才重连
- api.ts REST 401→auth_expired+清token+引导重登;login走skipAuthExpired密码错不误清
- types.ts WsStatus加expired;TopBar/Nav expired显示登录过期;System.tsx去alert占位改4张事实卡+合并Notice

agent-113 用户端登录页:
- components/Login.tsx Miuix登录卡(用户名/密码→login()→setAuthToken);App !getAuthToken()→<Login>;AdminApp LoginGate指向

agent-112 系统设置前三个(owner+审计):
- auth.APIKey.RevokeAll(吊销全部key);auth.JWTService.RotateSecret(≥32校验+并发安全+新密钥不回显)
- gateway admin_system.go: POST revoke-keys/rotate-jwt/backup(VACUUM INTO)/restore(防路径穿越+恢复前自动备份+原子替换+需重启)
- wire.go 传DatabasePath;全仓 build/vet/test绿+交叉编译;api-client v0.17系统设置契约

实机验证: 登录(role=owner)+revoke-keys 200+渠道列表含kook; 全仓绿+前端build绿; .env密码仅登录验证不打印
2026-09-07 13:57:36 +08:00
Aska 7e39153a69 feat: 修复审计落差 #1/#2/#3/#6 + 管理面板移动端适配
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
#1 工具布尔标记生效(agent-106, 安全红线): loop toolSchemas/runToolCalls 加 allow-set 安全门 restrictSchemas/applyToolSecurityGate——群聊/陌生人只暴露允许工具(默认关), 被拒not_allowed软提示+AuditEvent, owner全工具不变; 身份靠 ActorEnv(非提示词)
#2 checkpoint 恢复(agent-109): run.go LoadLatest + resumableStopReason门控(只恢复in-flight工具循环,不出答案不续跑), 幂等不重放副作用, 新会话不误恢复; resume_test 4用例
#3 tool_result(agent-108, 协议层): Anthropic 原生tool_result块(tool_use_id/is_error)非user纯文本 + OpenAI tool消息补tool_call_id; Content加ToolCallID/IsError等; (loop接线层2处留待,见备注)
#6 capacity_awaken推送(agent-107): onCapacity从只Debug改推WS hub(BroadcastCapacityAwaken, sessionId定向+payload对齐前端AwakenPayload)
管理面板移动端(agent-110): AdminApp 汉堡菜单+抽屉侧栏+标题truncate防换行, RBAC守卫不受影响
全仓 build/vet/test绿 + 前端build绿; 前端无涉差异已对齐

备注: #3 loop接线层2处(assistant工具轮append历史 + Message.ToolCallID/IsError)留给后续; #4代审/#5子代理触发/#7拆文件后置(主人说后三不急)
2026-09-07 13:43:18 +08:00
Aska aedae2190b feat(config): 运行时配置体系②——渠道配置落 channels.json + 面板落盘 + 热更新
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- runtimeconfig/channels.go: channels.json 承载 map[platform][]ChannelAccount(token/baseURL/黑白名单/群聊限流), LoadChannels 优先文件/回退env, SaveChannels(0600+tmp原子写), Upsert/Delete/Masked脱敏
- channelbridge/reload.go: adapterManager 热替换——工厂重建新adapter先连接, 再原子断旧adapter(独立readCtx/进行中不中断); Bridge.Reload(platform) 逐账号增删
- 面板 PUT/DELETE /admin/channels/{platform}/config(owner+审计) 落盘channels.json+Reload热更新; GET /admin/channels 附账号掩码不泄裸token
- 前端 Channels.tsx 配置表单(id/token/baseURL/黑白名单/限流+config JSON)+保存热更新提示+掩码展示/删除
- .gitignore 加 channels.json; .env 退兜底(.env.example注明); token不打印/Config()脱敏
- channelbridge.go 拆 logic→reload.go(742→731); 全仓 go build/vet/test绿+前端build绿
- 未做真token端到端(需渠道凭据,不读.env)——如实标注待凭据
2026-09-07 13:29:41 +08:00
Aska 2d7151f979 feat(config): 运行时配置体系①——LLM 多厂商独立组落 models.json + 面板落盘 + 热更新
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- runtimeconfig/models.go: models.json 承载 [{id,provider,baseUrl,apiKey,model,fallback?,multimodal?}] 多厂商独立组(首项=生效)
- LoadModels 优先文件/回退env(GroupFromEnv), SaveModels(0600+tmp原子写), Activate/Upsert/MaskedKey脱敏
- 面板 POST /admin/llm 落盘 models.json(取代temporary内存) + GET 列组(脱敏)
- runtime/model/runtime.go: RuntimeLLM(adapter+router) atomic快照可热替换, Reload 重建不中断进行中请求; per-role路由用接口
- wire 用 NewRuntimeLLM(adapter+router), 保留 a.modelAdapter(=runtime stack, 审查模型随热更新)
- .env 退兜底(.env.example注明); apiKey不打印/Config()脱敏/热更新不中断
- 全仓 go build/vet/test 绿(54包)+交叉编译+前端build绿(79模块); api-client v0.15已同步
- 遗留: vision ModelInfo 仍读静态env(后续纳入); 保存组=生效组(第一版语义)
2026-09-07 13:07:58 +08:00
Aska 8cde0162e6 feat: 公共平台最小化打扰(design/12 §6)——5条策略: 分级+轻思考+限流+审查剔噪
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
降噪A(策略一/二/五):
- channelbridge/light.go: 非owner分级门——小事件当系统消息登记(不进loop)、非owner轻响应(她判断回/不回,规则优先mention/关键词+轻LLM兜底fail-closed静默)
- backthink 触发降噪: SubjectSystemEvent 低价值不唤醒thinking(仅高价值/owner才think), iot/reminder不受影响
- quiet hours: 安静时段非owner无信号静默,被@才回(非硬性静默); config QUIET_START/END_HOUR 单源共用

限流B(策略三/四):
- adapter/flowgate.go: per-adapter 滑窗限流器+总上限+审查剔噪闸门(Admit两连:速率+上限/审查), owner私聊绕过
- channelbridge/flowgate.go: onMessage入站最前挂闸, 超限/被剔不占worker槽; 审查模型NewLLMReviewer复用model.Adapter(仅>80%阈值触发+跨分钟节流,失败回退FIFO,绝不判身份)
- config: ChannelAccount 加 GroupMsgPerMin(默认6)/MsgPerMin/MaxMessages(默认50), CHANNELS_JSON每账号, 向后兼容
- 被剔消息走OnRejected审计不静默丢

main wire接线: 加modelAdapter字段+Review注入(reviewFor nil-safe,无LLM回退FIFO)
全仓 go build/vet/test 绿(53包)+交叉编译, 零新依赖, 前端无涉
2026-09-07 12:18:21 +08:00
Aska 764d592859 feat(channel): KOOK 渠道适配器 + 框架兼容增强(wsutil/httpclient)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- plugins/channels/kook: config/adapter/gateway/websocket/send/message/errors — 实现 PlatformAdapter 13方法
  - WS 有状态网关: 取gateway地址→连WS→信令0/1/2/3/4/6 + 30s心跳/6s超时 + resume(拼接sn/session_id) + 指数退避重连
  - 收事件: 信令0 → UnifiedMessage(word注 channel_type/type/target_id/extra.author→字段)
  - 发消息: /message/create(频道) + /direct-message/create(私聊,修正正确端点,非user-chat) + Bearer鉴权
  - 注册 channels.go + CHANNELS_JSON 多账号 + channelbridge.configured 加 kook(bot_token非空才连)
- 框架增强: internal/channel/adapter/wsutil(连接管理/心跳/重连/resume骨架,复用gorilla,KOOK用上) + httpclient(Bearer+envelope归一,KOOK用)
- 全仓 go build/vet/test 绿, 零新依赖(复用餐gorilla), 未动telegram/qq/weixin/webhook契约
- §4.6 隔离/黑白名单/第三方边界未破坏

注: KOOK WS/WH 双模式待补(ReceiveMode), 实测需主人提供 CHANNEL_KOOK_TOKEN
2026-09-07 12:01:35 +08:00
Aska 9c2a37c4ac feat: 管理面板补齐(RAG导入/记忆添加/会话接history/诚实表述) + design/16 §4.6 IM多会话隔离+第三方身份+黑白名单
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
agent-98 面板批次:
- 会话管理面板改接 history.ListSessions(主web+次级) + 新建次级会话按钮
- 记忆面板手动添加入口 POST /admin/memories(owner)
- 知识库真正实现 IndexDocument(分块+落projectindex) + POST /admin/rag 导入端点
- 三面板空态诚实表述(去除'后端M10+提供')
- RAG嵌入降级到keyword/line(无直属embedder,不伪造语义,符合红线)

agent-99 design/16 §4.6:
- model.Message.SenderName + 迁移0006 sender_name 列
- 第三方身份边界: session.ResolveActor判定(非LLM猜) + [第三方·昵称]前缀(不改源content)
- 群聊/陌生人落独立sess-xxx(persistIsolated),不写主会话,不广播
- 黑/白名单(allowGroups/denyGroups/allowUsers/denyUsers)最前拦截,不进LLM
- 多账号 CHANNELS_JSON 配置 + 每平台多实例
- 管理只读 GET /admin/channels/{platform}/messages(该平台非主人消息流)

实机验证(JWT): 会话管理返回主会话web / 记忆添加成功 / RAG导入documents非空+Cyrene命中; 后端build/vet/test全绿 + 前端build绿
2026-09-07 05:13:36 +08:00
Aska 37e98ec584 feat: IM平台列 + deviceID标识 + 会话持久化完善
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- agent-96: chat_messages 加 platform 列(迁移0004) + IM主人私聊落主会话web(带platform) + 前端按平台过滤契约 + message_persisted 带 platform(复用 BroadcastPersisted)
- agent-97: chat_messages 加 device_id 列(迁移0005) + history.Message.DeviceID + Web发消息带deviceId(localStorage getClientId) + IM persistToMain 带 deviceID + 前端气泡展示 SourceLabel + 修主会话基础2个bug(fn scanMessage语法/DDL缺列)
- design/16 §4.4(平台) + §4.5(deviceID) 已入档
- 实机: POST /chat 带 deviceId 读回 user/assistant 均带 deviceId+platform; 后端 build/vet/test 绿 + 前端 build 绿
- 红线: 身份靠 actor 元数据; 禁止 shell 读 .env(JWT冒烟由主会话跑)
2026-09-07 04:51:41 +08:00
Aska 1bf60c1ae4 feat: IM式跨端实时同步——message_persisted 广播 + 前端按id去重追加
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- history.Store 新增 AppendFull(返回含 id/createdAt 的完整消息),Append 签名不变
- session_orchestrator 加可空 OnPersisted;persistUserMessages/persistAssistantReply 落库后回调广播
- server.go newOrchestrator 接 OnPersisted → hub.broadcast message_persisted(thinking 剥除)
- 前端 ws.ts 加 message_persisted case(仅 activeSessionId 匹配才处理);appendOrUpdateMessage 升级为 id 精确upsert+内容/时间归一
- 实测:双 WS,A stream 发消息,B 免刷新收到 user+assistant 各1条(count=2 PASS)
- 红线: 身份仍靠 actor 元数据,不靠提示词;用户/助手消息、本端/异端统一路径
2026-09-07 04:16:29 +08:00
Aska 48421923f6 feat(history): 会话消息持久化 + 主/次级会话(design/16)
- runtime/history: Store + HistoryStore 接口(Append/List 游标分页/Recent/DeleteSession/NewSubSession/ListSessions),主会话固定 web、次级 web:<uuid>
- db 迁移 0003: chat_sessions + chat_messages + idx_chat_msgs_session_created(事务幂等)
- gateway: GET/POST /sessions、GET /sessions/{id}/messages 热分页、DELETE /sessions/{id}(主不可删 403);Deps.History 可空注入
- 编排层落库: session_persist.go(submitInternal 写用户消息、completeTurn 写助手消息,拒绝的消息不落库)
- WS: wsEvent 加 sessionId 定向 + history 回放帧(握手上行后回放主会话最新 50 条,thinking 剥离)
- wire.go 装配 History 到 gateway.Deps.History
- 端到端实测: POST /sessions 建次级 201 / GET /sessions 列表 / POST /chat 后 /messages 读到 user+assistant / DELETE 次级成功 / DELETE 主会话 403
2026-09-07 03:52:43 +08:00
Aska 7455d09d05 feat: 角色视觉体系(头像按角色取)+ 时间注入(loop 每轮当前时间)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
【角色视觉体系(红线:每个角色自己的头像/背景,绝不串用昔涟)】
- persona.go:Persona 加 Visual{avatar, chatBackground{landscape,portrait}} + DefaultAvatar;每个角色各自配。
- cyrene.yaml:昔涟配 avatar(3F-Q-Happy-1) + chatBackground(横/竖屏)。
- PersonaStore 加 Persona(id);PersonaSummary 加 avatar/chatBackground;collectPersonas 按角色取
  Persona(id) 视觉(无视觉→空,前端回退默认占位,绝不串角色)。
- 前端:types PersonaRecord 加 avatar/chatBackground;persona.ts personaAvatar(p)=p?.avatar||默认占位
  (殊无昔涟 fallback);store 加 currentPersona;App 挂载 fetchPersonas 取默认角色;
  TopBar/chat 头像改 personaAvatar(currentPersona)(写死昔涟头像已清除)。

【时间注入(她自然知道现在几点,深夜催睡交给人格/capacity)】
- options.go Options 加 Timezone;loop.go Loop 加 timeLocation/timeZoneName + New 解析 + loadTimezone;
  time.go currentTimeBlock()(当前时间+周几+时区,每轮刷新);memory.go assembleContext 在「工作记忆」
  前注入时间 system(keyed 在 timeLocation!=nil,无时区不注入不破坏测试);wire.go Timezone=a.cfg.Timezone。

go build/vet/test 全绿(gateway/loop/persona);前端 tsc/vite build 75模块绿。红线无写死昔涟头像。
2026-09-07 02:46:40 +08:00
Aska 33eeadfc38 fix(backthink): 后台思考静默广播——不再把内部推理泄到聊天思维链 + XML 动作/旁白解析
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
【静默广播修复(bug:思维链露出 backthink 推理)】
- 根因:LoopThinker.Think 复用同一 loop.Loop 且 loop 全局带 OnStream,backthink 后台思考时
  把 [background autonomous reflection] 的 thinking/流式事件广播到前端,被当聊天思维链显示。
- loop/mute_ctx.go:WithMutedBroadcast(ctx) + isMutedBroadcast(ctx)——context 上静默标志。
- loop 四处广播抑制(stream.go 流式 / step.go 静默走非流式 agentStep / request.go capacity 事件 /
  tools.go tool_execution),静默时跳过 UI 广播,推理照常返回结论。
- thinker.go LoopThinker.Think 用 loop.WithMutedBroadcast(ctx),backthink 不再泄 thinking。

【XML 动作/旁白结构化解析】
- cyrene.yaml systemPrompt 加「表达方式:动作与旁白」——动作 <action>…</action>、
  旁白 <narration>…</narration>;克制≤1-2个/段、贴合唤醒态、重要信息不加、朗读正文忽略标签。
- 前端 parseContent.ts:ContentPart[](text/action/narration),非贪婪+反向引用、跨行、非法/
  未闭合兜底纯文本不崩;summarizeContent 供思维导图/状态。
- 前端 renderContent.tsx:action/narration → 灰斜体(mu-text-lo italic)句中原位渲染。
- 应用到 chat.tsx UserBubble/AssistantBubble + messages 各 mode body。

go build/vet/test(loop/backthink)绿 + 前端 tsc/vite 绿。
2026-09-06 22:09:51 +08:00
Aska 6dbb4a2e18 feat: 唤醒式架构批次——capacity/L4/会话编排/管理面板写CRUD/前端集成(77-81)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
【capacity 唤醒式核心(77)】internal/runtime/capacity:(Decider L1启发式+L2上下文+人格覆盖→三态静息/唤醒/全能→允许工具集+capacity_awaken事件)。loop 外围允许集过滤(不硬阻断=能力≠边界红线)。config 加 Capacity(Enabled/MinConfidence/ToolSets)。wire buildCapacity 接入 loop。

【L4 记忆驱动意图(80)】recall.go:从记忆召回用户习惯(搞一下xx=办事)→唤醒L1/L2漏掉/升置信;群聊陌生人不读owner习惯(隔离红线);RecordIntent闭环(判定写回记忆越用越准);默认保守静息。

【会话消息编排(78)】gateway/session_orchestrator.go:每会话(模式/pending有界5/tail上下文尾巴20);Submit非阻塞(idle立即/busy入队ack/满拒绝);完成回调按序续接(tail+新消息合并增量注入非全量重放);Interrupt中断;REST 202/409+WS session_busy/pending_ack事件;不重造任务引擎复用loop。

【前端集成(79)】ws.ts 加 capacity_awaken/session_busy/pending_ack 解析;store 加 awakenState/sessionBusy;TopBar/ChatShell 加唤醒态徽标+忙态提示(不阻塞输入框);状态组件 status.tsx;Miuix风格。

【管理面板写CRUD(81)】20个写端点(POST/PUT/DELETE /admin/*,owner+审计落库):personas(真持久化YAML)/users apikey(签发掩码+撤销)/iot控制(真下发)/automations(建/触发/删)/llm(test真调用+临时)/channels(connect/close/身份测试actor白名单)/plugins(卸载)/memories(search真检索)/sessions(export)。无存储如实501不伪造。拆4文件满足≤500红线。前端9模块接真写。

【wire收口】buildCapacity接入loop+OnCapacity事件;server.go newOrchestrator接hub。

实机验证:iot ac.living control 200 真实改变 on:true;observability/metrics 200、health ok;全仓 build/vet/test 47包绿+前端tsc/vite。
2026-09-06 21:54:40 +08:00
Aska e0a543cc37 feat(observability): 管理面板可观测性接真实数据——metrics/trace/audit 三端点 + 审计持久化
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- observability.go/observability_metrics.go/observability_trace.go/observability_audit.go:
  拆分保证 ≤500行/函数≤80。metrics 并发安全 MetricsCollector(方法/路径/状态计数、平均/最大延迟、
  近1s QPS窗口);trace 并发安全 RingBuffer(默认512, newest-first)+traceSink+MultiTraceWriter;
  audit AuditStore 接口+内存回退+SQLite 持久化(NewSQLiteAuditStore 参数化 SQL+audit_logs 表)。
- server.go:Deps.AuditStore + Server.obs + 3 路由 GET /admin/observability/{metrics,trace,audit};
  ObserveSpan/TraceSink 注入点;middleware logging recordMetric;audit 落库(仅 slog→Append)。
- 前端 Obs.tsx:metrics 接 4 张真实 StatCard+请求分布、trace 接 span 列表(展开 props/events)、
  audit 从后端拉(前端内存审计作回退);api.ts/types.ts 加 obs 端点/类型(AdminObservabilityMetrics/
  AdminTraceSpan/AuditRecord.id 放宽)。成本指标不伪造(保留'—'+注明)。
- wire.go 收口:Deps.AuditStore 注入 gateway.NewSQLiteAuditStore(ctx, a.db)(审计真落库),
  未注入回退内存。
- Docs/api-client.md §2/v0.6。

实机验证:三端点 200,metrics 返回 totalRequests:4/qps:4/perPath/statusCounts:{200:4}。
go build/vet/test 全绿 + 前端 tsc。trace 数据源对接(gw.TraceSink→loop tracer)因 gw/tracer
构造顺序留作可选后置(端点已就绪, 未对接 span 空)。
2026-09-06 21:02:46 +08:00
Aska 76134ed47e feat(gateway): webhook 渠道 InboundHandler 挂载到网关(渠道遗留2)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
让 webhook 渠道的入站 HTTP 回调真正进运行时(此前 handler 建了但未挂载,回调不达 runtime)。

- channelbridge:Bridge.WebhookHandlers() map[string]http.Handler——遍历适配器,对暴露
  InboundHandler()/Config() 的(*webhook.Adapter)收集 endpointPath→handler。
- gateway/server.go:Deps 加 Webhooks map[string]http.Handler;Server 存 webhooks;
  Handler() 挂载到 mux(无网关鉴权,各 handler 自带 HMAC 签名校验)。
- gateway/static.go:spa 加 isWebhookRoute——已注册的 webhook 路径交给 API mux(否则被 SPA
  fallback 吞成 index.html,webhook 回调不达)。
- wire.go:gateway.Deps 传 Webhooks: a.channelBridge.WebhookHandlers()(wireChannels 在
  wireFrontend 前执行,bridge 已建好)。

实机验证:POST /webhook 空体→bad_request(400,webhook 解码响应非 SPA)、带 JSON→accepted(200)、
GET /webhook 被 webhook 路由处理。go build/vet/test 全绿。
2026-09-06 20:40:49 +08:00
Aska adad4e1f62 fix(session): 群聊/陌生人记忆隔离——MemWritable 贯穿 loop(AGENTS.md 红线)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
群聊/陌生人查记忆只读不写、不泄露主人敏感;owner 默认行为完全不变。

- loop/sessionenv.go:SessionEnv 类型 + WithSessionEnv/sessionEnvFrom/OwnerEnv——每次 Run 携带会话
  隔离上下文,经 context.Context 传递(不改 Run 签名、不存 Loop/Options 字段,避免 16 并发渠道
  消息共享 Loop 的数据竞争;Chatter 接口与测试桩签名不变)。
- loop/memory.go 门控:injectMemory 读——MemWritable=true(owner)走原 Retriever.Inject 全量不变;
  false(群聊/陌生人)改 Retrieve 后只留 ScopeProject(人人可读)、剔除 ScopeUser(owner习惯私有)与
  ScopePersona(角色日常=owner敏感),块首加「群聊隔离」标记,无项目记忆不注入;extractMemory 写
  false 直接 return 跳过(不调用 extractor 不落库)。
- 渠道入站(Channelbridge)传真实会话 MemWritable/Env;Web/REST/WS/voice chat 绑 OwnerEnv(JWT=owner
  行为不变);backthink/automations/orchestration 默认 owner 全量。
- 测试:memory_gating_test 6 用例(owner/群聊只读不写/陌生人受限/无项目记忆不注入/默认owner全量)全绿;
  loop/session/gateway 既有测试无回归。go build/vet/test 47 包全绿。

遗留:ScopeProject 过滤在 loop 内后处理实现(未改 memory 包),若后续允许可在 Retriever 加 scope
过滤选项消除与 Inject 模板的重复(2处)。
2026-09-06 20:34:06 +08:00
Aska dbcf3df8a3 feat: RAG 知识库真实数据 + 渠道端到端接线(并行∦wire.go)+ 主会话收口
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
【渠道端到端·接线(agent-72)】
- 新建 internal/services/channelbridge/:桥接服务(Start 对配置渠道 Connect(10s超时/warn)+SetMessageHandler+ReadMessages;
  入站 UnifiedMessage→sessions.GetOrCreate(ResolveActor 白名单结构性识别)→loop.Run→SendMessage 回执,chat 文本)。
- cmd/cyrene/channels.go:从 CHANNEL_* env 读 4 渠道凭据(telegram/qq_obv11/weixin_ilink/webhook)。
- wire.go:app 加 channelBridge 字段,wireChannels 构造适配器+bridge.Start(),close() 收尾。
- 无真凭据降级:无 CHANNEL_* → configured()false 跳过不 dial;Connect 失败 Warn 不阻启动;
  webhook 仅入站(回执 ErrUnsupported 记 debug)。身份识别绝不靠提示词。收发端到端待凭据验证。

【RAG 知识库(agent-71)】
- internal/runtime/knowledge/:真实数据源(doc=资料/记忆=经历 分离 design/03 §3③)。
  ListDocuments 走 projectindex.ListProjects(+ListChapters)、Search 走 projectindex.Search(标题/描述+章节
  命中行+上下文)+retriever.Retrieve(配 vector 真语义,命中标 semantic:true)。
- gateway/server.go:RagStore 接口+Deps.RAG 可空+3 路由(GET /admin/rag、POST /admin/rag/query、/delete)。
- admin.go:adminListRag/adminSearchRag(空 query 400)/adminDeleteRag(写 501 not_implemented,M10)。
- projectindex/store.go:ListProjects/ListChapters。
- 前端 Rag.tsx:docs 从 GET /admin/rag 拉、检索走 POST /admin/rag/query、删除调真端点(501 显示 Notice),
  上传/分块/嵌入显示 M10 Notice;api.ts/types.ts 加 adminListRag/adminSearchRag/adminDeleteRag。

【主会话收口 wire.go】projStore 提到 app struct+wireMemory 赋值;knowledge.New(...) 注入 gateway.Deps.RAG。

实机验证:admin/rag 200(documents[] 空表非壳)、channels 枚举 4 渠道、health ok。
go build/vet/test 全绿 + 前端 tsc。wire.go 567 行。

遗留(记录):① 群聊/陌生人 MemWritable 记忆隔离未接 loop(AGENTS.md 红线,待修) ② webhook InboundHandler
需 gateway 挂载 ③ RAG 语义路径 persona-scoped(project 记忆其他 persona 不浮现)。
2026-09-06 20:09:09 +08:00
Aska 518bd0a230 feat: admin 端点补齐 + 视觉真推理 + 语音链路 + wire.go 收口(A+B+第3步)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
批次A admin 端点(原本 404 变 200 真实数据):
- GET /admin/llm:读 s.cfg.LLM 组 openai+anthropic 两 provider(enabled=当前 Provider、multimodal=配 VisionModel),APIKey 不进 wire 零泄露。
- GET /admin/automations:*automations.Service.List()(reminders+event rules+cron schedules,cron 加只读登记)。
- GET /admin/users:cfg.AdminUser + *auth.APIKey.List()(密钥掩码);无用户存储,恒先返回当前 admin 账号。
- GET /admin/backthink:*backthink.Backthink.List(ctx,50)。均只读、owner/admin RBAC、{data} 信封。

批次B 视觉真推理 + 语音:
- rest.go chatRequest 加 Image?;流式 publishChatStream 带 image;userChatMessages 构造 ImageMessage(model.go:71)。
- loop/vision.go:ImageResolver 可注入(MVP 先看图再对话);run.go Run 开头 resolveImages,无 resolver 透传直交主模型。
- vision/resolver.go:MessageResolver 调 vision.Router.Route——KindMain 保留图片交主模型多模态、KindVisionTool/OCRTool 经 dispatcher.Invoke 折叠成 [图片内容] 文本、KindUnsupported 记日志保留原图不崩。
- gateway/voice.go:VoiceService 接口 + SetVoice 注入点 + handleVoiceInput(未注入回 error 不崩)+ runVoiceTurn 异步 STT→loop chat→TTS→定向 voice_output 帧。
- ws_hub/ws_route:voice_input 接 handleVoiceInput;前端 ws.ts decodeBase64/playVoiceAudio/case voice_output。

第3步 wire.go 收口(唯一碰 wire.go 的一步):
- Deps 补传 Automations:a.automation / Backthink:a.backthink / Users:auth.NewAPIKey(a.cfg.Auth)(*auth.APIKey 原生满足 UserStore,避免 admin 重复的复合类型)。
- gw.SetVoice(a.voice);loop.Options.Vision = vision.NewMessageResolver(router, info, dispatcher.Invoke)(router=NewRouter(WithVisionTool/WithOCRTool)、info=cfg.LLM.VisionModel)。

实机验证:四 admin 端点 200 真实数据(llm 双 provider 脱敏、users=admin,yeij0942,owner);WS 握手正常。go build/vet/test 全绿 + 前端 tsc。wire.go 557 行。
2026-09-06 18:54:05 +08:00
Aska 24d5924cee feat(model): Top5#1 结构化流事件 + WS 事件体系(对齐 OpenClaw AssistantMessageEvent)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增 model/stream.go:StreamKind(text/thinking/tool_call 各 start/delta/end + tool_execution)
  + StreamEvent{Kind,Delta,Index,ID}。
- ChatStream 签名 onChunk func(delta string)→onEvent func(StreamEvent);loop OnStream 同改;
  streamStep 转发结构化事件。
- openai:text_delta + text_start/text_end(正文三段式);anthropic:文本块 text_*/tool_call_*
  按块 Index(content_block_stop 分支)真分段;fallback:有事件即交付。
- ws_hub:wsEvent 壳补 turnId/seq;新增 broadcastStream;streamChat 每帧打 turnId+seq;
  stream.go 把事件 publish 到 bus ws.stream。
- ws_bridge:streamEventData 映射 text_*→message.start/delta/end、thinking_*→thinking.*、
  tool_call_*→tool_call.*、tool_execution_update→tool_execution.update,未知回退 stream_chunk。
- 前端 ws.ts:解析 message.start/delta/end、thinking.start/delta/end(pendingThinking)、
  tool_call.start/end→addTool、tool_execution.update;保留旧 stream_chunk/thinking/tool_used 兼容;
  types.ts 补 turnId/seq。Docs/api-client.md 增 §3.1 v0.4。

已知限制(注释/文档标注):turnId 在 OnStream→bus→relay 路径暂为空(StreamEvent 无 turnId 字段,
bus ws.stream 载荷不带 turn);OpenAI adapter 未解析 tool_call 段;thinking 段两 adapter 暂不发;
tool_execution.update 是保留项(随工具循环钩子后置)。go build/vet/test + 前端 tsc 全绿。
2026-09-06 16:31:28 +08:00
Aska 9b207a31e8 feat(gateway): 地基③ 打通实时信号 producer→前端WS + loop 真流式 + 上行路由补全
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
打通前端 ws.ts 里那批死消费者事件(thinking/mood/tool_used/subagent_event/
task_output/device_update/reminder/backthink),并给 loop 接真 token 流式。

- wire.go:loop 建时传 OnStream(streamChunk 闭包把 delta 发 bus ws.stream_chunk);
  gateway.Deps 注入 Scheduler/Tasks/Streaming;iot 接共享总线 iot.WithBus(a.bus)。
- ws_bridge.go:hub 额外订阅 iot.>/task.>/automation.>/reminder.>/cyrene.agent.*.event,
  normalizeEvent 归一为 ws.*(device_update/task_output/task_done/automation_result/
  reminder/subagent_event);不订阅 cyrene.agent.> 以免 ask/reply 内部事件进 relay。
- ws_route.go:voice_input→ws.voice_input;subagent_ask→Scheduler.AuditContext(真实
  上下文审计,替换 pending stub);task_output_query→Manager.TaskOutput(真实输出,
  替换空 {})。
- server.go:新增 SubagentAuditor/TaskOutputStore 接口 + Deps.Scheduler/Tasks/Streaming。
- orchestration/auditcontext.go:Scheduler.AuditContext(full/tail/keyword)。
- task/manager.go:Manager.TaskOutput(id)。
- backthink:record() 补发 ws.backthink 事件。
- json tag 修正(device_update/reminder/task output 结构体加小写 tag + DeviceUpdate
  补 message),使前端分支能真正渲染。

实机验证:WS 握手 101、task manager ready、iot manager initialized(WithBus 生效)。
go build/vet/test 全绿;TestHubBridgeDeviceUpdate/TestHubBridgeSubagentProgress 覆盖
映射。dev 推至后续版本。
2026-09-06 15:53:48 +08:00
Aska 04bfa2c7a7 feat(gateway): admin 列表端点接真实数据——persona/session/memory 补 List() 并注入 gateway.Deps
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
根因:gateway.Server 定义 SessionStore/PersonaStore/MemoryStore 接口但生产从未满足:
- 底层 manager 无 List() 方法;
- wireFrontend 的 gateway.Deps{} 未传 Session/Personas/Memories。
→ /api/v1/sessions|personas 与 /api/v1/admin/{sessions,personas,memories} 恒返回 []。

改动:
- persona/registry:Registry.List() []Character(并发安全、值拷贝)。
- channel/session:SessionManager.List(ctx)(查 channel_sessions 表+按 actor_role 重建)。
- runtime/memory:Store.List(ctx)(全表,复用 scanItems)。
- gateway 三 Store 接口改为返回域类型(Character/MemoryItem),映射统一放 collect* 侧。
- cmd/cyrene/wire.go:Deps 补传 Session/Personas/Memories。
- .gitignore:cyrene→/cyrene(锚定根,不再误伤 cmd/cyrene 目录)。

实机验证:/api/v1/admin/personas 返回 {id:cyrene,name:昔涟,isDefault:true}。
2026-09-06 14:05:34 +08:00
Aska 5dae2b9741 fix(gateway): 收敛 sdk.Channel 与 adapter.PlatformAdapter 接口漂移,/admin/channels 恢复枚举
根因:sdk.Channel.Connect 为 2 参 (ctx, config any),adapter.PlatformAdapter.Connect 为
1 参 (ctx),Go 无重载 → 4 个渠道(qq-obv11/weixin-ilink/telegram/webhook)只实现 1 参
Connect,类型断言 cap.Value.(sdk.Channel) 恒失败 → /api/v1/admin/channels 恒返回 []。

收敛方向:sdk.Channel.Connect 改 1 参 (ctx),与 PlatformAdapter 生命周期子集一致;
4 个真实适配器自动满足新接口,adminListChannels 枚举成功。最小改动(不改渠道实现、
不引依赖)。同步修正 sdk_test/registry_test/extra_test 的 mock。
2026-09-06 14:04:52 +08:00
Aska 20305631c5 fix(gateway): WebSocket 同源/loopback 等价放行,修复生产同源跨源 403
后端 go:embed 自服务前端时,页面 origin 与网关 Host 一致(production 模式),
但不在 dev 阶段 ALLOWED_ORIGINS(5173/3000)白名单内,originChecker 记 403
导致 WS /ws/chat 握手失败。

改为:同源(origin host == request host)及 loopback 别名(localhost<->127.0.0.1)
自动放行;跨源(不同主机名)仍走白名单。安全不降级:跨源非白名单依旧拒绝。
新增 TestOriginCheckerSameHost 覆盖同源/loopback等效/跨源拒绝/同主机异端口。
2026-09-06 13:12:22 +08:00
Aska f9650185f1 test(gateway): 补 handler/WS/admin/static/middleware 测试至覆盖率 95.9%(≥70% 达标)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-09-06 08:19:31 +08:00
Aska a7feaeb2cb feat(deploy): M11 单二进制装配(cmd/cyrene 全链路 wire + go:embed 前端 + 外置 dist 覆盖 + 优雅关闭 + SPA 静态服务);前端 build 拷到 internal/webui/dist 2026-09-06 08:10:35 +08:00
Aska 6f97fded3a feat(backthink): 后台自主思考(heartbeat+事件触发 + 打扰决策 fail-closed + 主动推送活跃→IM 兜底 + 限制 + 思考记录) 2026-09-06 07:33:06 +08:00
Aska 05e23ea590 feat(gateway): 网关(REST /api/v1 + WS Hub /ws/chat + admin /api/v1/admin/* + JWT/RBAC + 审计,单端口 :8080) 2026-09-06 07:32:40 +08:00
Aska ff34b62079 feat(automations): 自动化引擎(cron 调度 + 事件触发 + AI 管线动作 + 提醒调度,bus automation.result/reminder.trigger) 2026-09-06 07:32:29 +08:00