Agentica v1.4.14
权限档对齐 Codex(ask 不再藏写工具,增加「拒绝类似」);Web/桌面真正多账号;文件工具收成 apply_patch + write_file;Worktree 默认进仓库内;系统 skill 包内加载;桌面版首次打开自动装 Python runtime。
安装: pip install -U agentica==1.4.14(网页/桌面:pip install -U "agentica[gateway]")
下载桌面版
| 系统 | 安装包 |
|---|---|
| macOS 11+ | Apple 芯片(arm64) · Intel(x64) |
| Windows 10+ | x64 安装程序(NSIS) |
| Linux x64 | AppImage · deb |
桌面版是个壳,服务端仍是 Python gateway:装应用之前先 pip install -U "agentica[gateway]"。安装包未签名,首次启动被系统拦下时按 README 里对应系统的一步操作解除。
完整对比: v1.4.13...v1.4.14
[1.4.14] - 2026-08-25
breaking
agentica.subagent/agentica.subagent_loader挪到agentica.subagents:内置定义从agentica/agents/*.md改到agentica/subagents/bundled/*.md(和skills/bundled同结构)。运行时from agentica.subagents import SubagentRegistry;加载器是agentica.subagents.loader。不留旧模块。用户/项目目录.agentica/agents/、$AGENTICA_HOME/agents/和/agents斜杠命令不变。SkillRegistry.get_skill_instruction()/generate_skills_prompt()删除:生产 catalog 一直是SkillTool.get_system_prompt()(自进化 spawn 也是写盘后再走这条)。两条是死 API,catalog 预算改挂在活路径上,不再维护第二套按注册序截断的 XML 列表。GET /api/memory不再返回用户级 AGENTS.md:payload 改为{auto_extract, index_path, entries}(MEMORY.md 索引)。常驻规则改走GET/PUT /api/user_agents_md。PUT /api/memory删除(405),避免旧前端把 AGENTS.md 正文写进记忆结构。- 权限档
ask改为 Codex「Ask for approval」:以前ask会从 schema 里藏掉写工具(write_file/execute/apply_patch等,模型看到 Function not found);现在三个档工具都在,差别只在真正execute之前要不要停车等人。ask:读(含工作区外;文件工具 + 只读 shell,含cd && git diff | head包装)自动放行;写(write_file/apply_patch,含工作区内)、会改状态的execute、web_search/fetch_url、硬不安全路径/命令停车。auto:再放行工作区内写、普通execute和网络。allow-all不询问、不拒绝(含硬不安全;项目「拒绝类似」只约束 ask/auto)。删掉模型可见的request_path_access,改由公开grant_path_access写入沙箱白名单。默认不变:CLI / SDK 仍是allow-all,Web 仍是auto。无人值守(无 LiveTurn:IM / cron / 非流式POST /chat/--print)立即把工具结果写成Tool call denied by user.,不 500、不 sibling-abort。 - 审批增加
deny路由和「拒绝类似」:classify返回allow | ask | deny。三档权限嵌套:auto⊃ask,allow-all是 root。ask下web_search/fetch_url和工作区内写文件弹卡;auto放行网络和工作区内写,硬不安全仍弹卡。新增deny_prefix(CLIx/4,Web 拒绝按钮下拉「拒绝类似」),写入project.jsonapprovals.deny_command_prefixes等,下次同类在 ask/auto 下不弹卡直接拒。allow-all忽略项目拒绝,只 warning + 记approval_decision(allow_all_ignore_deny/allow_all_hard_unsafe),命令照跑;进程用当前 OS 用户的权限,能 sudo 就能 sudo。用户在 ask/auto 点允许之后,执行时的check_command_safetyblock / sandboxblocked_commands不再跟卡打架。 - 系统 skill 不再拷贝到
$AGENTICA_HOME/skills/.system/,改为包内原位加载:CLI / gateway 的load_system_skills()把搜索路径末尾直接指向包内agentica/skills/bundled/,ensure_system_skills()、skills/.system、内容哈希指纹同步整套删除(同仓库subagents/bundled一直是这个模型)。拷贝本来就不是正确性前提(复制失败时回退的就是包路径),却让每次产品启动都扫树做 sha256、只读 home / 容器 / CI 走 OSError 降级、.system与bundled/双源难查、模型看到的Path:在 home 里像用户资产其实升级即被覆盖;SDK 侧「不捡到 CLI 残留」的隔离测试面也随之消失(.system点开头,目录发现本来就不扫)。覆盖方式不变:~/.agentica/skills/<name>/同名 user skill 优先级更高;Web 插件 API 的editable = (location == "user")不受影响,bundled 依旧不可编辑。
fixes
- 权限档文案与
classify()对齐:ask的读(含工作区外)与auto相同,只拦写、会改状态的 shell 和联网。CLI--permissions//permissions、Web Permission 提示不再写成「只有工作区内读才自动」或「ask 只有只读工具」。 execute超大输出改为落盘预览,不再静默丢掉中间段:原先max_output_length(默认 20k)先做 40/60 头尾截断,中间直接丢;max_result_size_chars=50k的 Layer 0 落盘永远触发不了。现在超过 20k 写入会话tool-results/,上下文只留路径 + 行数 + 头尾预览,后续用read_file/grep查。成功与非零退出(失败命令的巨型 traceback)都走这条路——Layer 0 钩子挪到结果内容组装之后(错误文本先脱敏再落盘,spill 文件不碰密钥),原先挂在「仅成功」分支上,报错路径仍然丢中间。- SDK
print_response_stream不再把工具事件漏进回答:show_tool_calls=False时原先不跳过ToolCallStarted/ToolCallCompleted,Running tool: read_file会被当成正文打印。现在走和 CLI / Web 同一套classify_run_response,工具事件无论显不显示都不会进答案。 - CLI 审批卡裁掉「拒绝」、两项时跳号:
rm /path这类不能「允许类似」的命令本来就只有允许/拒绝,但 TUI 把整段卡当成一行再多塞一个换行,高度少算一行,拒绝被裁掉,屏幕上只剩1. Yes, proceed (y)。同时拒绝还写死成3.,2仍绑「允许类似」。现在按可见选项连续编号(两项就是 1 允许 / 2 拒绝;四项仍是允许、允许类似、拒绝、拒绝类似),数字键跟着走;长命令按终端宽度预留折行高度,不再把选项挤出屏幕。SDK / Web 同一套options,没有类似可批时本来就不渲染那两个按钮。 - CLI 拒绝文案不再催用户写原因,审批后命令留在 transcript:拒绝从
No, and tell the agent what to do differently (esc)改成No, deny (esc/n)(esc/n都是直接拒,不追问理由)。审批卡画在输入区 layout 里,决定后整段卸掉,$ rm …会跟着消失。现在卸卡之后把命令和✓ allowed/✗ denied打进 scrollback。 - Web 审批卡按 Enter 会整轮「已中止」:卡上「允许一次」标了 ⏎,但焦点在输入框时 Enter 被当成「空输入为停止」。有未决审批时输入框禁用(附件 / slash / 粘贴也停),只留停止;Enter 允许一次、Esc 拒绝。残留草稿不再插入当前轮。
- Web 侧栏会话状态与对话页被误删:
deny_prefix那次提交冲掉了sessions.ts的lastTs/unread/syncSessionStatus,以及ChatPage的左侧 query 导航(ChatNav)、settleWork、已读markSessionRead、以及running清位。已从上一版补回,审批卡仍带deny_prefix。
features
-
工具说明去掉二进制黑名单,改为正面能力描述:
executedocstring、tools.md、ShellTool不再写「not cat / not find / not sed -i / MUST avoid find」式禁令——实测这类禁令把强模型推向更差的绕行路径,且多处文案互相矛盾。管线(pytest | rg '^FAILED' | sort、rg … | head、find … -mtime -3 | xargs ls -lt)与文件工具各自正面展示。grepcontent 模式的limit是全局条数上限:stdout 读满limit行即杀掉 rg(不再用每文件--max-count,也不再communicate()整棵树再切片)。read_file支持tail和负offset(offset=-50即最后 50 行,配limit取窗口内最旧的若干行);tail/负offset不再被 256KB 守卫拦住(守卫只拦从头分页;报错只建议tail,正offset/limit过不了守卫)。从尾扫描超过 20 秒超时。 -
SDK
print_response/print_response_stream默认展示工具,调用行跟 CLI 同一套文案:默认show_tool_calls=True(仍可关掉)。工具行用format_tool_display(read_file foo.py (L1-80),不再打原始 dict);read_file/glob/grep成功时只留调用行,和 Web 一致。思考在工具之后再出现会再打一次💭 THINKING,时间线是思考 → 工具 → 思考 → 回答。 -
Skills catalog 按模型上下文窗口的 2% 做 token 预算(无窗口时退回 8000 字符)。单条 description 硬帽 1024 字符(从前截)。超预算先从列表尾部丢掉 description(保留名字),仍超才 omit 并注明条数。卫生规则写进 prompt:匹配才用、当轮读完
get_skill_info、不跨轮携带、不深追 references。freeze_session_guidance()仍然冻住整块,不每轮重排。 -
粘贴/输入里的非法 UTF-8 不再打崩 CLI、Web、SDK:孤立 surrogate(例如
U+DCE5)以前在.encode("utf-8")上炸掉 Enter 写历史、agent 落盘和 Langfuse OTLP。CLI 入口、Agent.run/steer、WebChatRequest/SteerRequest/GoalRequest都换成 U+FFFD,相邻汉字不动。 -
Web 侧栏会话时间改为「多久没发起 query」:原先
agoStr用的是first_timestamp(会话第一次请求),所以一直聊的对话也会显示7h。现在用last_timestamp,有本地 transcript 时再取最后一条非 steer 用户消息;文案按界面语言粗粒度显示(刚刚 / 5 分钟一档 / x 小时前 / x 天前,英文 just now / 5 min ago / x hours ago / x days ago)。选中行的时间不再叠一层background(原先--bg2盖在--accent-soft上会变成一块深色芯片)。组内会话按最近一次 query 排序;项目组顺序仍按该目录第一次对话。 -
Web 侧栏会话状态:回复中转圈 / 未读绿点 / 空闲时间:发完 query 切到别的会话时,原会话 title 右侧不再显示「刚刚」,AI 思考、tool call、压缩期间是转圈;回复结束后如果没打开过就是绿点,点进去看过才回到时间标签。
last_read_at写在 session sidecar,GET /api/sessions带unread/running,打开会话会POST /api/sessions/{id}/read。旧会话没有last_read_at不当未读。 -
Web 对话左侧 query 导航条对齐 Codex minimap 设计:左侧留白区域默认只展示极窄短横刻度,鼠标悬浮短横时在右侧弹出不透明浮窗(圆角、边框),展示截断 query(前 50 字、最多两行);点击平滑直达对应问答轮次并带微光闪烁反馈;当浏览器宽度收窄(≤ 1024px)时自动隐藏,不侵占主对话区域。
-
自动抽取的记忆带上会话证据,
search_memory回读 provenance:write_memory_entry(evidence_refs=)原先只在手动写入时用得上,抽取结果没有来源。现在MemoryExtractHooks把抽取窗口归档后写入evidence_refs;search_memory每条结果带title/memory_type/memory_source/evidence_refs。评测脚本在evaluation/reconcile_mem/。 -
auto只拦工作区外的文件写:读(含工作区外的 skill/SKILL.md)、工作区内写、全部execute、网络、以及self_manage/cronjob/ skill 等内置工具(不看action)一律过。ask的读与auto对齐(含工作区外);拦写文件(含工作区内)、会改状态的 shell、以及web_search/fetch_url。记忆、task/delegate、skill 和其它内置工具直接过。self_manage show不再把compact_token_limit当成 secret;api_key只藏中间(含fallback_models/auxiliary_model里的 key)。 -
轨迹
approval_decision带上审批卡和用户决定:停车才写。事件里有工具名、入参、question/preview、可选「允许类似」标签、档位、等待秒数(wait_s,两位小数)、以及用户点的allow/allow_prefix/deny。选了允许类似时把写入project.json的 grant(命令类 / 路径前缀)记在同一条上,不再只剩decision+tool_call_id。 -
工具审批停车(CLI / Gateway / Web 同一套
ApproveFn):Runner 在fc.execute()之前、wait_for120s 超时之外调用Agent.approve。Gateway 的ApprovalRegistry只住在LiveTurn上(不是 Agent 缓存);SSEtool_call带tool_call_id,新事件approval_request(刷新按 seq 回放并重推未决项,不看当时有没有 SSE 订阅者);POST /api/sessions/{session_id}/approvals/{tool_call_id}提交{decision: allow|allow_prefix|deny|deny_prefix}(按账号鉴权,未知 id 404)。取消或结束deny_all;有未决审批的 Agent 不被 LRU 清掉。CLI 对齐 Codex 选项(y/p/esc/x),并行 tool_call 串行弹卡,Ctrl+C 拒绝全部。Web 底部固定一张卡(拒绝 Esc + 下拉「拒绝类似」/ 允许一次 Enter / 下拉「允许类似rm -f命令」),刷新按tool_call_id挂回工具行。allow_prefix/deny_prefix批的是命令类(rm -f,不含文件名),写入该项目project.json的approvals(与work_dir/active_profile同文件),重启 CLI / gateway 仍生效;「允许/拒绝一次」不落盘。复合命令不能「允许/拒绝类似」。/permissions改为三档说明。 -
Web 对话 run 与 SSE 连接拆开:刷新 / 断网只断开订阅,后端继续跑。
POST /api/chat/runs立刻返回run_id;GET /api/chat/runs/{id}/events?after=按序号重连去重;GET /api/chat/runs/active找回当前 session 的 run;POST /api/chat/runs/{id}/cancel显式取消并等到 session lock 释放(已结束则幂等返回终态)。Stop 在 lock 释放前不允许发下一轮(新输入排队)。发送后立刻出现 WorkGroup「正在思考」转圈(有附件时是「正在准备附件」),不再空白等到首字。live_turn / Agent 缓存 / session lock / work_dir / 审批档都按(账号, session_id)分区,session_id是客户端可控字段,不能让 B 账号的 409 或删除碰到 A 的 run。list_sessions会并上该账号尚未落盘的 live run,刷新不会让刚发出的新对话从侧栏消失。 -
Web 工具调用入参跟 CLI 同一套展示:对话里不再
json.dumps原始参数。read_file/glob/grep一行路径;write_file/apply_patch文件名或文件数(结果仍是 diff);execute命令;write_todos清单;web_search/fetch_url查询或 URL;task/delegate/send_message全文;list_agents只显示名字;其余key=value摘要。轨迹页仍是原始 JSON。 -
Worktree 默认短命、落在仓库内:默认路径改为
<repo>/.agentica/worktrees/<任务>(不再是兄弟目录../<repo>-<任务>;settings.worktree.root: sibling可回到旧布局)。worktree(action="merge")合入本地 base 后删除 checkout 和wt/<任务>分支,会话回到主目录;进行中的同名use仍复用。新增remove。会话绑定期间git worktree lock(agentica pid=<pid>),活着的别人的锁和用户手加的锁都不抢;pid 已死才偷锁。删除前的安全标准是「agentica 的wt/分支 + 没有独有工作」(干净且不 ahead 本地 main),不是「是否已 push」;detached / 手建 / Claude Code 的树一律不删。退出 CLI 时,没有独有工作的wt/worktree 自动清掉,有独有工作的保持 lock。.env仍是 symlink。use发现别人持锁时会点名 pid。工具remove先校验再搬家。 -
web_search默认引擎从baidu换成exa:走 Exa 公开 MCP(https://mcp.exa.ai/mcp),不设EXA_API_KEY用共享免费池(有限流),设了走自己的额度。和 OpenCode 同一条免费接入;AGENTICA_WEB_SEARCH=baidu或provider="baidu"仍可选。环境变量配了需要 key 却没给 key 时,降级目标也跟着改成 Exa。 -
Web 对话回答列对齐 980px、正文 14px:原先
.msg-stack再套一层min(760px, 88%),宽窗口两侧留白,开着工作区面板时表格还被压到约 600px。现在 assistant 列跟.msgs同宽,用户气泡仍是聊天宽度;输入框max-width:980px与对话列对齐。段距 / 列表 / 表格 / 标题一起从 compact 终端密度调到网页可读。 -
glob/grep硬超时从 3s 调到 20s(_GLOB_TIMEOUT/_GREP_TIMEOUT):3s 会误杀本地大仓、冷盘和没有rg时的 Python fallback;仍不暴露给模型。对齐 kimi-cli / Claude Code 的 ripgrep 20s 上限。glob 继续留超时:当前实现是pathlib.glob整树走完才返回,结果上限挡不住 NFS hang。 -
设置 › 模型配置顶部只留
config.yaml路径:去掉当前 profile / 模型 / effort / context / compact 那一行摘要,列表里本身就能看到谁在用。 -
compact_token_limit:压缩工作阈值,不改模型context_window。1M 窗口可以配300000,Layer 2 在min(该值, 窗口×0.95)触发摘要;Layer 1 的 0.8/0.5 相对min(该值, 窗口)。不配则和现在一样(约 95% 窗口才摘要)。写在 config.yaml 的 profile 上,也可写settings.compact_token_limit做全局默认。CLI:/config set compact_token_limit 300000、--compact-token-limit。Web 模型配置有填才显示。SDK:ToolConfig(compact_token_limit=300000)。 -
设置 › 记忆拆成两层:上半编辑用户级
AGENTS.md(常驻规则,进 system prompt);下半「生成对话记忆」开关仍接auto_extract_memory,列出的是MEMORY.md索引条目,不再把 AGENTS.md 预览假装成抽取结果。CLI / SDK / Gateway 同一套注入:会话开头把 MEMORY.md 索引(标题+hook+相对路径,最多 60 行 / 4KB)冻进 prompt 并点名绝对路径,memory/*.md正文按需search_memory/read_file。抽取仍大约每 10 轮。已知限制:write_user_agents_md会清掉共享 Workspace 上的_context_snapshot,Gateway 下一轮所有会话会重建 AGENTS.md 前缀(快照应按会话持有,v1 不改)。 -
「Profile」中文改叫「模型配置」。
-
个人助理的 Web 地址可点:和
config.yaml一样走POST /api/open,用系统默认浏览器打开http://127.0.0.1:8881/chat。 -
个人微信默认就能扫码:
WECHAT_TOKEN_FILE默认~/.agentica/cache/wxbot_token.json,WECHAT_ALLOWED_USERS默认空。gateway 启动不再弹码;网页点「配置」才生成二维码(过期约 120 秒)。没扫或过期显示「失败」;离开再进个人助理页,码是空的,要再点配置。 -
微信 / 网页 / 桌面是同一个人:点「配置」扫码时登录的账号就是机主。IM 不再用微信 openid 另开
users/<openid>/分区;会话、记忆、AGENTS.md 都进这个账号。已连上的再点一次「配置」也会绑到当前登录账号。 -
设置 › 个人助理:设置弹窗第二个 tab(常规旁边),展示本机 Web 地址和微信 / 企微 / QQ / 飞书等 IM 的接入状态与连接方法。账号菜单同一项打开这个 tab。
/assistant仍是同一套内容的独立页。GET /api/channels增加catalog/web_url/listen。 -
桌面版第一次打开会自己装 Python runtime:安装包仍然不含解释器(壳最难更新)。找不到
agentica-gateway时用 uv 在 Application Support 装 CPython 3.12 +agentica[gateway],不进~/.agentica;已经 pip 过的继续用原来的。AGENTICA_GATEWAY_BIN/AGENTICA_DESKTOP_NO_BOOTSTRAP可跳过。 -
CLI 运行中输入提示改直白:Enter 插话显示
Steered the current query. Tip: Tab or /queue queues a request.;Tab 排队显示Queued the next request. Enter steers the current query.。原先Guidance added to the current task没说清 steer 的是当前这一问,也没点出/queue。 -
CLI 回答按 Markdown 块增量落屏:原先
stream_response把整段缓冲到finalize才一次性print(Markdown),长回答只能盯着answering…。现在围栏感知的空行一旦闭合一块(标题/列表/代码/表格)就立刻渲进 scrollback,未完成的尾部仍等结束;不用 Rich Live,避免和 prompt_toolkit 抢光标。cli_markdown=off仍整段纯文本。 -
设置 › Profile 顶部是本地
config.yaml:原先「当前配置」和预览弹窗在常规页。现在这块在 Profile 最上面,路径是可点链接,走已有的POST /api/open用系统默认编辑器打开(本机 gateway / 桌面版);打不开就 toast。预览弹窗去掉。GET /api/config/file仍给别的调用方打码读文件。 -
Web 思考与工具按真实调用顺序穿插:思考是组里一行(11px、和工具同行高),正在流式的那段默认展开、正文随页面贴底,不再用
max-height把最新思考裁在卡片里。工具同样一行一卡。正文仍会打断分组,所以时间线是思考 → 工具 → 回答 → 思考 → 工具 → 回答。流式光标只留在最下面那一个气泡。 -
Web
/goal过程流式:原先POST /api/goal只推status条,正文等整轮done才一次性出现。现在每轮走run_stream,和普通对话同一套thinking/tool_call/tool_result/contentSSE,工具组和吐字穿插出现;顶栏 tokens 进度仍用status。停止按钮、插话 chip 也挂到这一轮 live 消息上。 -
Web
/goal只限 tokens,默认不限:原先网关硬编码 turns 15 / tokens 80,000 / wall 300s,状态条三项一起显示。现在对齐 CLI 主闸:只传token_budget(GoalRequest.token_budget,-1= 不限,网页默认)。输入/goal进入目标模式:先填 token 预算(空=不限,支持 k/m),再写目标。运行中状态条只显示tokens used或tokens used/budget。CLI--turns/--wall不动。 -
Web 流式吐字贴底跟滚:回答过程中每条 token 不再立刻重跑 Markdown / KaTeX / 高亮(那会把主线程卡住,看起来像输出冻住);按动画帧合并绘制,流式时用轻量渲染。用户没上翻时始终跟到底;程序化滚动不再被误判成「用户离开底部」。
-
Web 插话(steer):输入栏只有一个按钮。空内容是停止;有内容则发送(纯文本走 CLI 同一条
agent.steer())。要排到下一轮用/queue <prompt>(/q同义),和 CLI 一样;附件和其它斜杠命令仍排队。插话气泡带图标、虚线框。来不及送达的插话在本轮结束后自动改排队。API:POST /api/chat/steer、POST /api/chat/steer/take。 -
glob/grep不再把timeout暴露给模型(grep同时去掉multiline):内部硬超时,不给模型覆盖。此前无上限的timeout让模型随手填 60 就能拆掉 fail-fast;跨行正则 fallback 又不支持,有rg和没rg会搜出两套结果。超时错误改为让模型收窄path/include,不要原样重试或改走execute。context_lines > 0时忽略before_context/after_context(与 rg-C一致),写进 docstring。execute/wait的timeout不动。 -
文件工具去掉
ls/edit_file/undo_edit:编辑只留apply_patch+write_file(read_file/glob/grep仍在),方法本身也删了,schema 里不再出现。列目录改用glob(pattern="*")——它本来就返回子目录,工具 docstring 和tools.md里点明了这一点,免得模型退回execute("ls")。Web 默认审批档是auto(ChatRequest.approval_mode),不是ask。task、web_search、memory、list_agents/send_message仍常驻。CLI / Gateway 不再保留对ls/edit_file/undo_edit/multi_edit_file的历史日志渲染分支。越权路径不再走模型可见的request_path_access(已删除),改为审批卡停车后grant_path_access。 -
权限档以 Agent 为准,工具自己那份只在脱离 Agent 单用时生效:
Agent在接线工具时(_wire_tools_to_self)把tool_config.permission_mode灌给它持有的每个BuiltinFileTool。get_builtin_tools()因此不再有permission_mode参数——同一个决定不留两个入口。工作区外写入走审批卡 +grant_path_access,不再挂模型可见的request_path_access。 -
Web / 桌面版变成真正的多账号产品,seed 的账号从
admin改名default:账号名同时是数据分区名(它命名users/<id>/,也就是会话与记忆的落盘位置),叫admin就意味着首启动会凭空造出一个users/admin/分区,而这台机器上已有的对话全在users/default/下——同一个人换个入口进来看到的是空列表。现在默认账号名直接取settings.default_user_id(即default),并对已装过上一版的机器做一次性迁移:auth.json里那条admin记录连密码带会话一起改名成default,所以旧密码继续能登、开着的浏览器不掉线。左下角的账号入口显示用户名 + 角色(原先显示的是当前 profile 名——profile 是模型配置,和「我是谁」无关)。账号名会先规范化再当目录名(清理后 2–32 位、字母开头、仅字母数字下划线)。 -
每个账号有自己独立的会话、归档与定时任务:
AgentService的每条入口(chat/chat_stream/run_goal、会话列删改归档、trace 读取)多一个owner参数,由服务端从会话 cookie 解析,agent 实例的缓存键也带上它。相应地ChatRequest/GoalRequest/MemoryRequest/CronJobCreateRequest删掉了user_id字段——请求体里的身份是客户端说了算的,等于任何登录用户都能把别人的user_id填进来。网页侧边栏只展示服务端返回的会话(不再把同一个浏览器里上一个账号的localStorage合并进来,那会让kk登录后看见default的对话)。定时任务按账号切开:/api/scheduler/jobs只列出当前账号的任务,创建时把 cookie 里的账号名写进CronJob.user_id,别人的任务 id 一律 404;调度器仍会执行所有到期任务,跑的时候用任务上的user_id作为数据分区。归档是会话上的标记,跟着会话走。「用户管理」是唯一的管理员专属功能(/api/auth/users*非管理员一律 403):模型 profile、工作目录、技能、MCP 仍是这台机器的配置,任何账号都能改。删账号只吊销登录,users/<id>/的数据留在盘上。端到端验证tmp/multi_account_smoke.py(真 gateway、真流式对话,两个账号互不可见、非管理员 403、管理员改密、删号后数据仍在)。 -
用户管理从设置弹窗里拆成右侧独立页
/users,新增 / 改密改成完整表单:原先塞在设置 tab 里的一行输入框既看不清也做不全。现在主侧栏不变、右侧是账号表;新增用户是二级窗口:用户名会先规范化再当目录名(小写、空格/连字符变下划线、其它非法字符丢掉、数字开头加u_、超长截断;清理后须 2–32 位且字母开头),初始密码(管理员自己填,至少 6 位)。规范化后与已有账号撞名则拒绝。用户名合法时提示将创建默认 Project<id>(不再带-default_project后缀,目录就是账号名)。只能新增用户角色——第二名管理员会把「谁能改谁的密码」搅成一团,seed 的那一个就是唯一管理员。去掉重置密码:改自己(内置管理员)要填当前管理员密码(提示写明初始密码打印在首次启动的终端里,形如abcd-1234),改普通用户只填新密码 + 确认。改密只在用户管理页(以及设置 › 访问);左下角账号菜单改为 退出登录,方便切账号。 -
网页去掉「让模型输出推理过程」开关,thinking 默认一直开:这不是给用户选的产品能力——支持 thinking 的模型本来就该把推理过程流出来,关掉只是让界面少一块;不支持的模型会忽略。删除
/api/config/thinking与设置里的勾选,Gateway 构建主模型时固定传thinking="enabled"。 -
轨迹是会话的附属,不再是独立 tab:主侧栏去掉「轨迹」按钮,中间那列会话列表也去掉。唯一入口是对话标题右边的「查看轨迹」;轨迹页左上角「返回对话」回到该会话。直接打开
/traces(不带sessionId)会回到聊天。 -
对话页右侧「打开工作区」:顶栏去掉「目录: …」那一行,改成打开当前会话工作目录的文件面板(Web / Desktop 同一套 SPA)。面板标题是「文件」,工具条是「根目录」加详情 / 刷新 / 上传;点进文件可预览(文本 / Markdown 渲染与源码 / 图片 / PDF / HTML 沙箱),超过 256 KiB 截断并提示下载(源码高亮到 64 KiB 为止,更大的 Markdown 默认开源码视图以免卡主线程),预览页可返回、可下载。对话里提到的路径(行内代码、
read_file/write_file/apply_patch参数、上传附件)会聚成文件卡片,点「预览」打开同一块面板。路径必须落在工作目录内(..与 symlink 逃逸拒绝)。API:GET /api/workspace/files、/content、POST /stat、/upload。 -
对话输入框右下角的上下文占用改成 CLI
/usage同款会话级拆分:鼠标悬停看到 Context Window(已用 / 窗口)、消息数、API 调用、费用、本会话输入/缓存命中/输出,以及 system prompt / 规则与工作区 / 技能 / 工具说明 / 工具定义 / 对话各自占用的 token。各行是并列拆分(加起来等于下一轮请求),不是父子:System prompt是整段 system message 扣掉已归因块后的剩余,工具定义是 API schema、不在那段字符串里。分段只报 token 数,不再配百分比、进度条和缩进;/usage同样去掉% full, estimated和 ▓ 条。数字来自同一条measure_context路径。API:GET /api/sessions/{id}/usage;流式done事件也带上这份快照,圆环百分比跟占用走。 -
侧栏每个 Project 行右侧「+」在该项目下新建对话;不再显示 project 路径(工作区已经有路径)。工作区 Details 点外面或 Esc 关掉。Markdown 预览支持 HTML 标签(如
<p align="center">)和 LaTeX;源码 / 纯文本预览右上角可复制可见文本。每轮结束后,鼠标悬停答案显示日期、↑输入 / ↓输出 tokens、input cache tokens 与 cache hit(与 CLI footer 同一口径)、tok/s、费用、耗时和复制;footer 每一项都有 tooltip(费用是「成本(USD)」)。用户消息悬停只显示简写日期时间和复制。新建对话可在输入栏选择/更换 work dir(浏览上级目录、改用临时工作区)。输入/可触发/goal、/compact和已安装 skill。可粘贴/预览图片:底模能看图则直接附上,否则由settings.media_model配置的模型描述后转交(未配置时提示如何在 config.yaml 里写media_model)。流式输出贴底滚动,上滑后页面中间出现下拉箭头可跳回最新。输入框随内容长高。 -
Web 读图提示不再写死 Gemini:底模不能看图时,tip 显示
settings.media_model里实际配置的model_name;未配置则说明在 config.yaml 里怎么写该块(需要model_name,以及model_provider或base_url)。GET /api/status增加media_model。IM 渠道未配置时的回复同样不再写「指向 Gemini」。 -
Web 输入栏、footer、/goal 与新建会话目录:去掉输入框上方的
Enter to send · …tip(换行/粘贴写进 placeholder)。每轮 footer 的 cache 改成数据库图标,夹在输入 tokens 和输出 tokens 中间。/goal运行时输入框上方显示 CLI 同款状态(Goal [active]: …+ tokens/turns),期间再发的消息进入队列而不是 409「already has an active run」。新建会话选目录时可直接输入绝对路径;路径不存在或无法访问时提示「目录不存在或无法访问,已回退」,仍停留在旧目录上可继续改。 -
设置 › 默认工作目录:历史条目可单条删除;已不存在的路径(包括 pytest 留下的临时目录)打开设置时自动丢掉。输入绝对路径点应用 / 回车,非法路径同样提示「目录不存在或无法访问,已回退」。
DELETE /api/config/dir_history?path=。 -
Web 偏好落盘到 gateway,localStorage 只做首屏缓存:主题、语言、审批档、上次打开的会话写入
$AGENTICA_HOME/gateway/prefs/<账号>.json(GET/PUT /api/prefs)。清浏览器缓存或换浏览器后仍能从本机读回;第一帧仍读 localStorage,登录后再对账。会话正文仍是 SessionLog,没有另起一份 SQLite。 -
输入框 Skills / Permission 对齐可发现性:Skills 按钮(打开的书 + 下箭头)弹出带搜索的列表,展示技能名和描述,悬停 tip 是完整 desc。Permission 三档跟
agentica.agent.permissions一致,不是 Codex 截图里的「每次询问」:ask是征求批准(读含工作区外、只读 shell 自动;写文件 / 改状态的 shell / 联网停车)、auto是代为批准、allow-all是完全访问。图标按 Codex 习惯:眼睛 / 闪电 / 三角感叹号;按钮带下箭头,括号里跟英文 id。/斜杠菜单过长时列表可滚、键盘选中项滚进视口,技能行也用打开的书。 -
源码预览按语言语法高亮:工作区打开
.md/.py/.yaml/.toml/.java等文本文件(Markdown、HTML 切到「源码」也一样),以及对话里的围栏代码块,都画成带语言标签和复制按钮的代码卡片,用 highlight.js 着色(标题蓝、引用绿、跟常见 GitHub 主题同一套 token)。Markdown 渲染视图不变。源码卡片自己管横竖滚动(不再把纵向甩给外层、横向甩给<pre>):两条都是 8px 细条、始终可见、可拖滑块,点轨道跳到对应位置。 -
工作区预览上限从 12000 字符改成 256 KiB:原先一个普通源码文件就会被截成「请下载」。
GET /api/workspace/content?preview=1现在只读文件头TEXT_PREVIEW_BYTES(256 KiB),不再整文件进内存再切;超过 64 KiB 关掉语法高亮,更大的 Markdown 默认开源码视图,避免卡主线程。 -
查看轨迹后侧栏丢掉刚建的对话:进
/traces会拆掉整棵AppShell再GET /api/sessions覆盖列表;而这个接口只列默认工作目录下的 session,刚在别的 project 里问完的对话(轨迹能打开,因为它按 session id 找)就不在列表里,覆盖时连 localStorage 一起冲掉。现在 chat/traces/users 共用一个 layout,切轨迹不再重拉列表;list_sessions列出该账号下每一个 project,并带上work_dir。 -
gateway 重启后「查看轨迹」提示盘上没有 jsonl:
_session_work_dirs是进程内存,重启后空了就去settings.base_dir找;对话其实写在另一个 project 下。session_log_for/ 删改归档现在用SessionLog.find_sessions在该账号所有 project 里按 session id 定位。轨迹页 cache 改成与对话 footer 同一套数据库图标 + tip。 -
轨迹时间线此前是一根实心条,现在是真的分段:
thinking/text/tool_call三种事件是在一次请求结束后才一并落盘的,时间戳几乎相同,于是分析出来的模型泳道要么只有一段、要么后面几段宽度为零——时间线看着像没坏,但它讲的不是这一轮实际发生的事。现在流式循环在推理段和正文段真正结束的那一刻记下time.time(),SessionLog.append_event()接受显式timestamp,落盘前按发生顺序排序,所以「思考 2.3s、回复 36ms」这种分布才画得出来。 -
工具泳道按工具名合并,图例五项:原先每个工具调用各占一条泳道,一轮里调 20 次
read_file就是 20 条几乎一样的线;现在同名工具收进一条泳道(并发调用才在道内错开分层,道尾标调用次数),并且图例五个阶段常驻(思考 / 模型回复 / 工具参数生成 / 审批等待 / 工具执行)——只画本轮出现过的颜色,会让两轮之间的同一个颜色对应不同阶段。不再画「其它」:那是墙钟减去各段之后的残差,时间线上本来就没有对应的条。 -
Web 工作组在回答结束后不再显示「运行中」:思考切片的
ms没冻住时,组头和「正在思考」按墙钟一直加,隔天变成 45h。现在所有未收口的思考都冻住(中间段用下一段工具/t0,不要用现在的墙钟);回合结束走settleWork;已经不在流式的卡片即使旧数据缺ms也不再转圈。 -
沙箱
blocked_commands不再把rm -rf /当成所有绝对路径删除的前缀:以前只卡左侧边界,rm -rf /Users/.../__pycache__和rm -rf /tmp/x都被当成删根目录拦掉(测试还把这个漏洞写成预期)。现在按操作数匹配:/是完整路径,后面不能再接Users;rm -rf /、rm -rf /*、echo x; rm -rf /仍拦。对齐 Codex / OpenCode / Penguin:工作区内删除走审批或 OS 围栏,不用字符串前缀当禁令。 -
工具审批分类器与「允许类似」:
is_destructive第三方工具(如cronjob)在 ask 下停车,不再误落到兜底allow;auto下放行(只拦工作区外文件写)。命令「允许类似」按命令类(rm -f,不含路径/文件名),rm -f a批过之后rm -f b不再问、git add不会放行git push;类短于 2 个 token 时不提供「允许类似」(bash deploy.sh不能永久放行任意bash -c);echo hi && rm这类复合命令不能用首段越权,只提供允许一次。已写入project.json的单 token 前缀(["bash"])读取时忽略。ask下工作区write_file/apply_patch也会弹卡。todo / memory / skills /ask_user_question不弹卡。Web 输入框 Enter 不再误批。tool_resultSSE 带tool_call_id。真正停过车才写approval_decision。 -
输入栏弹出层点外面或 Esc 就关掉:模型 profile、权限档、Skills 打开后只能再点一次按钮才关。现在点对话区、输入框或其它按钮会立刻收起,Esc 也先关菜单(有菜单开着时不拿去停止生成)。
-
Web 生成中空 Enter 会停止,Stop 按钮一直在:placeholder 写着「空输入为停止」,但空 Enter 直接 return;输入框有草稿时按钮还会变成发送。busy 期间按钮恒为 Stop,空 Enter / Esc 都停止。
-
创建 run 途中点 Stop 不再留下孤儿任务:
POST /api/chat/runs返回后若用户已停止,立刻cancelRunApi,避免 UI 已 aborted、后端仍占 session lock。 -
SSE 空闲 15s 发 keepalive;干净 EOF 且未见
done/error/aborted则按断线重连:前置代理闲置掐流不会再被当成回合完成。 -
已结束的 live_turn 10 分钟后从注册表丢掉:完成/取消后仍可重连回放,超时释放事件缓冲,删会话立刻清掉。
-
live_turn 事件缓冲和订阅队列有上限:运行中不再按 token 数无限 append;连续
content/thinkingdelta 在缓冲里合并;慢订阅者队列满则断开,让客户端按after=seq重连,避免长/goal+ 多标签页把网关打到 OOM。 -
Steer 打断思考时上一张思考卡片停止计时:插话把思考切到下一张卡,但上一张
ms没冻住,墙钟还在涨。现在插入 steer 或开新思考切片都会finishThink。 -
Web 侧栏按会话创建时间排序:jsonl 首行
first_timestamp是第一次请求。GET /api/sessions和侧栏都按它倒序(新在上),发消息不再把老会话顶上去;「3d」小字也是创建时间,不是最后活动。Project 分组按该目录第一次出现会话的时间排,给旧项目加 New Chat 不会把它顶到最上面。CLI/resume仍按 mtime 倒序。 -
Web 对话不再狂打
/api/workspace/stat:消息底部的文件卡片原先跟着流式正文每一帧重抽路径,数组换了引用就 POST 一次,长回答会打满日志。现在流式期间不查;路径集合没变也不查;同一帧里多条消息合并成一次请求。 -
Web 工具调用一开始就上屏:思考是流式的,但
execute这类要跑一会儿的工具要等完成才出现——flushStream()以前只在有挂起的 rAF 时才bump(),思考停了之后那条tool_callSSE 改了数据却不重绘。现在结构事件立刻绘制。 -
Web 回答过程闪屏:每 token 整段 Markdown 重挂载(
components={{}}每次新对象)、.bub pre{transition:all .2s}让代码块从 0 高度动画出来。改成 120ms 节流、冻结的ChatMarkdown(流式跳过高亮/KaTeX,结束后高亮一次)、光标改成独立 caret,去掉 pre 的全属性 transition。 -
Web 工作组耗时按思考 + 工具切片求和:组头原先用「第一刀到现在」的墙钟,思考 step 又不冻
ms,最后一轮在回答已经结束后仍因isLast每秒刷新,总时长跟着墙上的钟涨。现在每段思考结束就记下切片,组头 = 各思考耗时 + 各工具耗时;思考行也显示这段时长。回答生成时间不再算进运行过程。 -
Web tool result 不再 500 字截断:网关原先一律截成 500 字,前端再
slice(0, 8000)且完成后默认折叠,所以execute的 stdout 看起来像没了。只有read_file/glob/grep成功时不展开结果(本地读文件,调用行够了);write_file/apply_patch/write_todos/web_search/fetch_url/save_memory/search_memory/execute/task等展开时同时展示完整入参和结果(write_file/apply_patch不再只送行数摘要),默认展开,只有超过 10 万字才截。task不再收成只有tool_count的_task_metaJSON,展开子 agent 的逐步调用和完整回答。出错的结果一律展示。 -
没有
rg时grep的 context 不再静默失效:纯 Python fallback 原先丢掉context_lines/before_context/after_context,模型传了-C 3却只拿到匹配行。现在按 rg 同样的优先级切片(context_lines > 0忽略 before/after,重叠窗口合并,组间--)。 -
Web
/compact发出后立刻显示用户那一行,压缩完成前不能再发:以前要等压缩 API 回来才把/compact写进对话,期间发送按钮也不算 busy,于是用户以为没发出去再敲一次,第二条撞上 session 锁变成「already has an active run」。现在用户气泡马上出现;压缩期间 Enter / 发送键挡住(不是发出去再 409)。普通对话和/skill本来发出去就上屏,这条没改。 -
对话 footer 和轨迹同一轮的 tok/s、耗时对不上:footer 用整段
chat_stream墙钟(含建 agent)当分母,10 token / 3.6s 显示成2.8 tok/s、耗时再四舍五入成4s;轨迹用 jsonl 里 request 的 LLM 时间,同一轮是5.3 tok/s/1.90s。现在done带上last_completed_round的duration_ms/llm_ms/tps(跟 View trace 同一套分析),footer 用 LLM 时间算 tok/s、用 round 墙钟显示耗时。两边共用fmtDurationMs(不到 10s 两位小数)和fmtTps(一位小数)。 -
轨迹页顶栏的 git 分支和工作目录是会话自己的,不是 gateway 进程的:jsonl 原先每条都盖
os.getcwd()和进程里的git branch,网页会话于是一律显示git:main和…/Codes/agentica。现在按会话work_dir打戳、在那个目录探分支;没有 git 就不显示git:。已写出的旧记录打开轨迹时也用会话目录覆盖。