Chat 消息、issue 评论、autopilot——在 Multica 里全都被建成一条 task,进入同一个队列,由本机 daemon claim 后新起一个 agent 子进程执行。上下文的连续性靠 --resume,物理隔离靠按 task 建、按 (agent,issue) 复用的 workdir。这份文档把 chat / issue 两条链路、resume 的边界、工作目录逻辑、父子 issue 关系一次讲透,全部有代码出处。
chat_session_id 续,issue 按 (agent_id, issue_id) 续。workdir 按 task 建、按 (agent,issue) 复用——同一 issue 下不同 agent 各有各的目录和会话,父子 issue 之间物理完全独立,只有元数据关联。
Web 聊天、issue 评论、飞书/Slack、autopilot 定时——入口不同,落库都是 agent_task_queue 里的一行。
chat.SendChatMessage 返回 {MessageID, TaskID};service/task.go 的 enqueueIssueTask;daemon claim handler handler/daemon.go。CreateChatTask),绑 chat_session_id。TestDirectChat_TaskOwnsItsOwnInputBatch 钉死 U1→T1、U2→T2,不会合并成 "U1\n\nU2"。TestDirectChat_RunningTaskDoesNotAbsorbNewMessage:"second send must create a distinct task")。chat_session 上存的 session_id(要求 RuntimeID 与当前 task 匹配)。GetLastChatTaskSession(chat_session_id) 取最近一条带 session 的 task——"否则一次失败的轮次会静默丢掉整段对话记忆"。PriorSessionID → 新进程 --resume。handler/daemon.go(chat 分支,GetChatSession / GetLastChatTaskSession)。这是 issue 链路的核心键。resume 查找 = GetLastTaskSession(agent_id, issue_id)。
pkg/db/queries/agent.sql)接受 completed 和"非毒化的 failed":一个中途崩溃(被 daemon 重启/runtime 掉线/sweeper 超时孤立)的 task 也可能已经建立了真实会话,resume 它能保住在途上下文(MUL-1128)。
handler/daemon.go:1813(issue 分支)· 注释:"Resume the prior session ... including comment-triggered follow-ups, so the agent keeps the issue's conversation context across turns"。以下任意一条成立,就从干净会话重开。
RerunIssue)或飞书 /new。daemon 直接跳过整个 resume 查找。prior.RuntimeID == task.RuntimeID。CLI 会话状态存在那台机器本地磁盘(Claude Code 存 ~/.claude/projects/<编码cwd>/),换机器无从 resume。GetLastTaskSession 过滤掉这些失败终态:iteration_limit、agent_fallback_message、api_invalid_request(如超大图片→Anthropic 400)、codex_semantic_inactivity(Codex 卡死)。避免反复复现坏状态。gateResumeToReusedWorkdir:CLI 的 session 按 cwd 索引,若原 workdir 已不在(issue 完成后 GC / daemon 重装 / 手动清理),回退到新 workdir,daemon 主动丢弃 prior session id——否则拿别的 cwd 的 session 去 resume,CLI 一秒内退出、整个 run 失败。| 场景 | 结果 |
|---|---|
| 同 agent 同 issue 反复评论(常态) | 一路 resume 续上下文 |
| 点了"重跑" | fresh 新会话 + 新 workdir |
| issue 被另一台 runtime 接走 | fresh |
| 上一轮 API 400 / Codex 卡死 | fresh(毒化会话被过滤) |
| issue 完成很久、workdir 被 GC 后再评论 | fresh(workdir 未复用) |
Prepare),但整条对话的文件留在第一个 task 的 workdir 里,后续 task 用 Reuse 复用同一物理目录。(agent_id, issue_id) 走,A 复用 A 的、B 复用 B 的,物理隔离,各自也有独立会话。execenv.Prepare / execenv.Reuse / PredictRootDir(internal/daemon/execenv/execenv.go)。issue_id → 自己独立的 (agent, issue_id) resume 线 → 独立的 workdir + 独立的 agent session。WHERE agent_id=$1 AND issue_id=$2 从不跨 issue,不会走父子关系。issue.parent_issue_id。TestCreateSubIssueInheritsParentProject)。issue_child_done.go:子 issue 完成时向父 issue 汇总/通知。handoff_note,或 agent 用 multica issue 命令去读另一个 issue),绝不通过共享文件系统或共享会话。