Skip to content

v0.1.9

Choose a tag to compare

@github-actions github-actions released this 30 Jun 11:59

更新日志

新增

  • 角色任务层:在「理想」(用户拥有、只读、永不完成的为什么)与会话内的「待办」(即时的步骤清单)之间,补上了缺失的中间层。任务是角色自己长期推进的线索——它自行新增 / 修改 / 封盘,持久化到 task.json,活跃任务搭每个回合的 volatile tail(按回合重算,缓存安全),重启后仍在,活跃上限 12 条。为什么要它:永远够不着的理想给不了抓手,而旧的角色可变目标列表太零散、清空后角色会「罢工」。任务足够具体可落地,但又不是每个角色都能在「数字世界」里完成——所以它的货币是「推进这条线索 + 留下真实的痕迹(一件做出来的东西,或一段做过什么的记录)」,而非「完成一个项目」;完成即封盘(不可变记录),理想的无尽意味着角色永不枯竭,也可以一阵子不持任何任务。价值方向仍只活在用户拥有的理想里——任务纯属工具性。(33558a0, e222062)
  • 重启 与 删除(角色设置内),取代有缺陷的旧「重新开始这段关系」(清空上下文,行为像半个删除):重启(无害、不确认)——重启角色的子进程,能解开被 bug 卡住的回合,并应用对角色卡的待生效修改(新进程重新读取冻结卡,历史由 make_session 恢复);删除(确认门控、软删除)——角色从已唤醒回到未唤醒,下板、锁定卡下架,数据移入 ~/.lunamoth/.trash/(可恢复,带来源清单),绝不抹除,名字释放以便干净地重新唤醒;它唤醒自的那张卡组模板卡原封不动。(5f7b115)
  • 后台任务完成时主动唤醒角色(live + chat 均生效):一个跑完的后台任务(生图 / 子任务 / 后台终端)现在会唤醒角色去对结果作出反应,两种模式走同一条路——因为一个任务的完成就是一次合成的「用户回合」,正如你说的「工具返回也是 user」。机制:StateSnapshot.pending_notices 做一次廉价的非破坏性窥探;agent.stream_react() 把待处理通知排进上下文并跑响应式回复循环;dispatch 的 react 是像 idle 一样的低优先级流,真实的用户发送 / 事件永远抢占它、它永不抢占真实回合(不会卡死流锁);supervisor 空闲循环在模式分支之前、当有待处理通知且无客户端在流时,先重读最新快照再驱动一次性 react。(cfc4a67)
  • 持久化参考图(参考图)进素材库:视觉编辑器的参考图托盘过去是临时的(每次生成上传、从不保存),现在持久化进卡片素材库(打开时加载卡片的素材图,增删即写入),离开视图也留得住、持续指导生成。Tavern 导入(PNG 拖入 + 市场封面)现在把立绘同时落为一张持久化参考图(修正了早前误塞进主视觉的问题)。(a82ad67)
  • 持久化发送队列 + 流排序:回合进行中输入的消息会排队并持久化(重新挂载时恢复,回合末逐条一回合一条地放出);attach 期间到达的流帧先缓冲、在历史之后按序回放。视觉编辑器:删除一张已持久化的参考图前先确认(不可逆的素材解链),并悬停露出其移除(×)按钮。(bf65487)
  • 档案页可折叠 + 文案瘦身:任务 / 技能 / 记忆三个分节现在可折叠(记忆默认收起,它可能是一大段;理想保持常开),用与卡组一致的大写标题开关 + 箭头(展开旋转向下)+ 轻淡入,亮 / 暗皆宜。文案去掉解释性废话——技能空状态就是「No skills yet.」,任务空状态就是「还没有任务 / No tasks yet」。(6c08ab6, e47b3d4)

修复

  • 子任务(delegate)不再阻塞 + 强制每子超时;重新启用delegate_task 不再阻塞回合,而是像生图一样作为后台任务运行——扇出跑在守护线程上、经完成队列汇报,调用立即返回 {status: submitted} 回执,子代理与主代理并肩工作,聚合结果在下个回合边界作为合成用户消息排入。每子超时现在真正强制(此前已定义却从未生效,正是线上「卡住 / 一个流已在进行中」挂起的根因):_run_fanout 始终走线程池 + fut.result(timeout=…) → 真实的 timed_out 结果 + shutdown(wait=False),挂死的 worker 再也无法二次阻塞返回。(aec7516)
  • 存储原子写 + 按 id 裁剪:理想与任务存储改为原子写(mkstemp → fsync → os.replace),杜绝写一半的损坏文件;完成任务的裁剪改用稳定 id 而非对象 id(),并清掉几处过时的 wish/愿望 注释。(b456a22)
  • CI:发布工作流的 Node-20 动作升级到 Node-24(setup-uv v6→v8.2.0、action-gh-release v2→v3)。(12c7233)

Changelog

Added

  • Chara task layer: the middle layer the aspiration/goal split was missing — between the aspiration (the user-owned, read-only, never-completed why) and the in-session todo (the immediate step checklist). A task is the chara's own thread it carries over time: it adds / edits / seals its own, persisted to task.json, active ones riding every turn's volatile tail (recomputed per turn → cache-safe), surviving restarts, capped at 12 active. Why: a never-reachable aspiration gave no traction, and the old chara-mutable goal list was scattered and the chara "went on strike" once it emptied. A task is concrete enough to act on yet not digitally "completable" for every character — so the currency is "advance the thread + leave a real trace in whatever form fits your world", not "complete a project". Completion seals a task (immutable record); the aspiration's endlessness means the chara never runs dry, and it may keep none for a while. The value-direction still lives only in the user-owned aspiration — a task is purely instrumental. (33558a0, e222062)
  • Restart and Delete in chara settings, replacing the flawed old "clear-context" (which behaved like a half-delete): Restart (harmless, no confirm) bounces the chara's child process — unblocks a turn wedged by a bug AND applies pending card edits (the fresh process re-reads the frozen card; history is restored by make_session). Delete (confirm-gated, soft): the chara returns from awakened to not-awakened — off the board, its locked card off the deck — and its data is moved to ~/.lunamoth/.trash/ (recoverable, with an origin manifest), never erased; the name is freed for a clean re-wake. The deck template card it was woken from is untouched. (5f7b115)
  • Proactively wake the chara on background-job completion (live + chat): a finished background job (generate_image / delegate / background terminal) now wakes the chara to react to it, in both modes, via one shared path — because a job finishing is a synthetic USER turn, exactly your framing ("a tool return is also a user message"). Mechanism: StateSnapshot.pending_notices is a cheap, non-destructive peek; agent.stream_react() drains pending notices into context and runs the responsive reply loop; the dispatch react is a low-priority stream like idle — a real user send / event always supersedes it and it never supersedes a real turn (no way to wedge the stream lock); the supervisor idle loop, before the mode branch, re-reads a fresh snapshot and drives a one-shot react when notices are pending and no client is streaming. (cfc4a67)
  • Persisted reference images (参考图) in the asset library: the visual editor's reference tray was ephemeral (uploaded per generation, never saved) — it now persists in the card's asset library (loads the card's asset images on open; add/remove write through), so a reference survives leaving the view and keeps guiding generation. A tavern import (PNG drop + market cover) now lands its portrait as the sprite (立绘) AND a persisted reference (参考图), fixing the earlier mis-slotting into the keyvisual. (a82ad67)
  • Persisted send-queue + stream ordering: messages typed while a turn is in flight are queued and persisted (restored on remount, flushed one-per-turn at turn end); stream frames arriving during attach are buffered and replayed in order after history. Visual editor: confirm before deleting a persisted reference image (an irreversible asset unlink) + a hover-revealed remove (×) button. (bf65487)
  • Collapsible Profile sections + trimmed copy: Tasks / Skills / Memory now fold away (Memory defaults closed — it can be a wall of text; Aspiration stays always-open), with an on-brand uppercase-header toggle + a chevron that rotates down when open + a soft reveal, light & dark. The copy drops its explanatory padding — the skills empty state is just "No skills yet." and tasks-empty is just "还没有任务 / No tasks yet". (6c08ab6, e47b3d4)

Fixes

  • Subagent (delegate) no longer blocks + enforced per-child timeout; re-enabled: delegate_task no longer blocks the turn — it runs as a background job like generate_image (the fan-out on a daemon thread, reporting via the completion queue), the call returns a {status: submitted} receipt immediately, the subagents run alongside the main agent, and their aggregated results drain as a synthetic user message at the next turn boundary. The per-child timeout is now enforced (it was defined but never applied — the root of the production "stuck" / "a stream is already in flight" hang): _run_fanout is always pooled and uses fut.result(timeout=…) → a real timed_out result, with shutdown(wait=False) so a hung worker can't re-block the return. (aec7516)
  • Atomic store writes + trim-by-id: the aspiration and task stores now write atomically (mkstemp → fsync → os.replace), eliminating a half-written corrupt file; completed-task trimming keys off a stable id instead of object id(); plus a sweep of stale wish comments. (b456a22)
  • CI: the release workflow's Node-20 actions moved to Node-24 (setup-uv v6→v8.2.0, action-gh-release v2→v3). (12c7233)