@turmind/halo 1.6.4 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +17 -9
  2. package/admin-out/404.html +1 -1
  3. package/admin-out/_next/static/chunks/87-6a2716c98a819c4e.js +1 -0
  4. package/admin-out/_next/static/chunks/989-066a874961bd519f.js +1 -0
  5. package/admin-out/_next/static/chunks/app/page-812bcbb46db628b6.js +1 -0
  6. package/admin-out/_next/static/css/e890aa0d13e94290.css +1 -0
  7. package/admin-out/index.html +1 -1
  8. package/admin-out/index.txt +5 -5
  9. package/bundled-docs/design/canvas-extensions.md +38 -7
  10. package/bundled-docs/design/cron.md +12 -4
  11. package/bundled-docs/design/halo-city.md +14 -149
  12. package/bundled-docs/design/storage.md +6 -2
  13. package/bundled-docs/design/stt-local.md +106 -0
  14. package/bundled-docs/design/web.md +1 -1
  15. package/bundled-docs/dev/api.md +11 -8
  16. package/bundled-docs/dev/desktop-packaging.md +37 -9
  17. package/bundled-docs/dev/env.md +1 -1
  18. package/bundled-docs/requirements/chat.md +8 -0
  19. package/bundled-docs/requirements/workspace.md +4 -0
  20. package/dist/index.js +1342 -538
  21. package/package.json +2 -1
  22. package/templates/skills/cron/SKILL.md +2 -1
  23. package/templates/skills/cron/manage-cron.py +2 -1
  24. package/admin-out/_next/static/chunks/147-d4200c70445aabb6.js +0 -1
  25. package/admin-out/_next/static/chunks/989-5d7743ff56d00dd7.js +0 -1
  26. package/admin-out/_next/static/chunks/app/page-8881b4f87a3d5ae8.js +0 -1
  27. package/admin-out/_next/static/css/3b4dcd0ac6b8eae8.css +0 -1
  28. /package/admin-out/_next/static/{kfp1cMc36J6hjZ06L9mm- → 8v1hCXbUqJhyMuK7LVeet}/_buildManifest.js +0 -0
  29. /package/admin-out/_next/static/{kfp1cMc36J6hjZ06L9mm- → 8v1hCXbUqJhyMuK7LVeet}/_ssgManifest.js +0 -0
@@ -1 +0,0 @@
1
- "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[989],{19882:(e,t,o)=>{o.d(t,{I18nProvider:()=>d,useI18n:()=>c,useT:()=>l});var n=o(41440),s=o(54604),a=o(43672);function r(e){return"zh"===e?"zh-CN":"en-US"}let i=(0,s.createContext)({lang:"en",bcp:"en-US",t:e=>e,setLang:()=>{},refreshFromServer:async()=>{}});function c(){return(0,s.useContext)(i)}function l(){return(0,s.useContext)(i).t}function d(e){let{en:t,zh:o,children:c}=e,[l,d]=(0,s.useState)("en");(0,s.useEffect)(()=>{d(function(){let e=localStorage.getItem("halo_lang");return"zh"===e||"en"===e?e:navigator.language.startsWith("zh")?"zh":"en"}())},[]);let u=(0,s.useCallback)(async()=>{try{var e,t,o;let n=(await a.F.settings.getSchema()).sections.find(e=>"general"===e.namespaceId),s=null==n?void 0:n.fields.find(e=>"language"===e.key),r=(o=null!=(t=null!=(e=null==s?void 0:s.value)?e:null==s?void 0:s.default)?t:null,"string"==typeof o&&o.toLowerCase().startsWith("zh")?"zh":"en");d(r),localStorage.setItem("halo_lang",r)}catch(e){}},[]);(0,s.useEffect)(()=>{u()},[u]);let m=(0,s.useCallback)(e=>{d(e),localStorage.setItem("halo_lang",e),a.F.settings.patch("global","general.language",r(e)).catch(()=>{})},[]),p=(0,s.useCallback)((e,n)=>{var s,a;let r=null!=(a=null!=(s=("zh"===l?o:t)[e])?s:t[e])?a:e;if(n)for(let[e,t]of Object.entries(n))r=r.replaceAll("{".concat(e,"}"),String(t));return r},[l,t,o]);return(0,n.jsx)(i.Provider,{value:{lang:l,bcp:r(l),t:p,setLang:m,refreshFromServer:u},children:c})}},24554:(e,t,o)=>{o.d(t,{AppI18nProvider:()=>i});var n=o(41440),s=o(19882),a=o(45966),r=o(42883);function i(e){let{children:t}=e;return(0,n.jsx)(s.I18nProvider,{en:a.en,zh:r.zh,children:t})}},38115:(e,t,o)=>{o.d(t,{LangSwitcher:()=>r});var n=o(41440),s=o(19882);let a=[{value:"en",label:"EN"},{value:"zh",label:"中"}];function r(e){let{className:t}=e,{lang:o,setLang:r}=(0,s.useI18n)();return(0,n.jsx)("select",{value:o,onChange:e=>r(e.target.value),className:"px-1 py-0.5 text-xs rounded border border-[var(--border)] bg-transparent text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:border-[var(--foreground)] transition-colors cursor-pointer ".concat(null!=t?t:""),children:a.map(e=>(0,n.jsx)("option",{value:e.value,children:e.label},e.value))})}},42883:(e,t,o)=>{o.d(t,{zh:()=>n});let n={"channels.wechat":"微信","channels.observerAdminOnly":"Observer(仅管理员可设)","tg.title":"Telegram Bot","tg.desc":"添加 BotFather 创建的 bot,用 Telegram 对话即进入绑定的 workspace","tg.add":"添加 Bot","tg.loading":"加载中...","tg.empty":"还没有 Bot。在 Telegram 找 @BotFather 创建一个,然后点右上角添加。","tg.saveFailed":"保存失败: {error}","tg.switchFailed":"切换失败: {error}","tg.confirmDelete":"确定删除 Bot @{name}?","tg.deleteFailed":"删除失败: {error}","tg.label":"名称","tg.labelPlaceholder":"Bot 名称","tg.workspace":"绑定 workspace(绝对路径)","tg.accessLevel":"权限级别","tg.readonly":"Readonly — 仅读取(默认)","tg.wsWrite":"Workspace — workspace 内读写","tg.full":"Full — 完整权限","tg.allowedUsers":"允许的用户(逗号分隔 user_id 或 @username,留空=不限制)","tg.save":"保存","tg.cancel":"取消","tg.whitelist":"白名单","tg.pathMissing":"(路径不存在)","tg.disable":"停用","tg.enable":"启用","tg.edit":"编辑","tg.delete":"删除","tg.addTitle":"添加 Telegram Bot","tg.tokenLabel":"Bot Token(从 @BotFather 获取)","tg.bindWorkspace":"绑定到 workspace","tg.nameOptional":"名称(可选)","tg.namePlaceholder":"比如:SA Agent","tg.allowedUsersOptional":"允许的用户(可选,逗号分隔)","tg.allowedUsersHint":"user_id 或 @username,留空=不限制","tg.addBtn":"添加","slack.title":"Slack Bot","slack.desc":"添加启用了 Socket Mode 的 Slack 应用。Bot 通过长连 ws 接收消息——无需公网 webhook。","slack.add":"添加 Bot","slack.loading":"加载中...","slack.empty":"还没有 Slack Bot。在 api.slack.com 创建应用、启用 Socket Mode,然后点右上角添加。","slack.saveFailed":"保存失败:{error}","slack.switchFailed":"切换失败:{error}","slack.confirmDelete":"确定删除 team {name} 的 bot?","slack.deleteFailed":"删除失败:{error}","slack.label":"名称","slack.labelPlaceholder":"Bot 名称","slack.workspace":"绑定 workspace(绝对路径)","slack.accessLevel":"权限级别","slack.readonly":"只读 — 仅查看(默认)","slack.wsWrite":"Workspace — 工作区内可读写","slack.full":"完全 — 全部权限","slack.save":"保存","slack.cancel":"取消","slack.pathMissing":"(路径不存在)","slack.disable":"禁用","slack.enable":"启用","slack.edit":"编辑","slack.delete":"删除","slack.addTitle":"添加 Slack Bot","slack.botTokenLabel":"Bot Token(xoxb-…)","slack.botTokenHint":"OAuth & Permissions → Bot User OAuth Token","slack.appTokenLabel":"App Token(xapp-…)","slack.appTokenHint":"Basic Information → App-Level Tokens(需 connections:write 权限)","slack.bindWorkspace":"绑定 workspace","slack.nameOptional":"名称(可选)","slack.namePlaceholder":"比如 SA Agent","slack.addBtn":"添加","feishu.title":"飞书 Bot","feishu.desc":"添加启用了「长连接」事件订阅模式的飞书应用。Bot 通过 wss 长连接收消息——无需公网 webhook。","feishu.add":"添加 Bot","feishu.loading":"加载中...","feishu.empty":"还没有飞书 Bot。在飞书开放平台创建应用,启用长连接事件订阅,然后点右上角添加。","feishu.saveFailed":"保存失败:{error}","feishu.switchFailed":"切换失败:{error}","feishu.confirmDelete":"确定删除 app {name} 的 bot?","feishu.deleteFailed":"删除失败:{error}","feishu.label":"名称","feishu.labelPlaceholder":"Bot 名称","feishu.workspace":"绑定 workspace(绝对路径)","feishu.accessLevel":"权限级别","feishu.readonly":"只读 — 仅查看(默认)","feishu.wsWrite":"Workspace — 工作区内可读写","feishu.full":"完全 — 全部权限","feishu.save":"保存","feishu.cancel":"取消","feishu.pathMissing":"(路径不存在)","feishu.disable":"禁用","feishu.enable":"启用","feishu.edit":"编辑","feishu.delete":"删除","feishu.addTitle":"添加飞书 Bot","feishu.appIdLabel":"App ID","feishu.appSecretLabel":"App Secret","feishu.botOpenIdLabel":"Bot Open ID","feishu.botOpenIdHint":"机器人自己的 open_id,用于在群里识别 @ 提及","feishu.verificationTokenLabel":"Verification Token(可选)","feishu.encryptKeyLabel":"Encrypt Key(可选)","feishu.bindWorkspace":"绑定 workspace","feishu.nameOptional":"名称(可选)","feishu.namePlaceholder":"比如 SA Agent","feishu.addBtn":"添加","wecom.title":"企业微信机器人","wecom.desc":"添加企业微信智能机器人。Halo 通过 wss 长连接主动连出,只需机器人 ID + Secret,无需公网回调地址。","wecom.add":"添加 Bot","wecom.loading":"加载中...","wecom.empty":"还没有企业微信机器人。在企业微信管理后台创建智能机器人,接收消息方式选「长连接」,然后点击上方添加。","wecom.saveFailed":"保存失败:{error}","wecom.switchFailed":"切换失败:{error}","wecom.confirmDelete":"删除机器人 {name}?","wecom.deleteFailed":"删除失败:{error}","wecom.label":"名称","wecom.labelPlaceholder":"Bot 名称","wecom.workspace":"绑定 workspace(绝对路径)","wecom.accessLevel":"权限级别","wecom.readonly":"只读 — 仅查看(默认)","wecom.wsWrite":"Workspace — 工作区内可读写","wecom.full":"完全 — 全部权限","wecom.save":"保存","wecom.cancel":"取消","wecom.pathMissing":"(路径不存在)","wecom.disable":"禁用","wecom.enable":"启用","wecom.edit":"编辑","wecom.delete":"删除","wecom.addTitle":"添加企业微信机器人","wecom.botIdLabel":"Bot ID","wecom.secretLabel":"Secret","wecom.bindWorkspace":"绑定 workspace","wecom.nameOptional":"名称(可选)","wecom.namePlaceholder":"比如 SA Agent","wecom.addBtn":"添加","web.title":"Web 接入","web.desc":"创建 Token 给 Web 前端接入,每个 Token 绑定一个 workspace","web.add":"创建接入","web.loading":"加载中...","web.empty":"还没有 Web 接入。点右上角创建一个 Token。","web.saveFailed":"保存失败: {error}","web.switchFailed":"切换失败: {error}","web.confirmDelete":"确定删除此接入?Token 将失效。","web.deleteFailed":"删除失败: {error}","web.label":"名称","web.labelPlaceholder":"接入名称","web.workspace":"绑定 workspace(绝对路径)","web.accessLevel":"权限级别","web.readonly":"Readonly — 仅读取(默认)","web.wsWrite":"Workspace — workspace 内读写","web.full":"Full — 完整权限","web.observer":"Observer — 全局只读(看板/监控)","web.save":"保存","web.cancel":"取消","web.pathMissing":"(路径不存在)","web.copied":"已复制","web.copyToken":"复制 Token","web.disable":"停用","web.enable":"启用","web.edit":"编辑","web.delete":"删除","web.createSuccess":"创建成功","web.tokenNotice":"请保存以下 Token(只显示一次):","web.done":"完成","web.addTitle":"创建 Web 接入","web.bindWorkspace":"绑定到 workspace","web.nameOptional":"名称(可选)","web.namePlaceholder":"比如:SA Demo","web.addBtn":"创建","wx.title":"微信机器人","wx.desc":"扫码登录后,微信消息会进入绑定的 workspace 会话","wx.add":"添加机器人","wx.loading":"加载中...","wx.empty":'还没有机器人。点右上角"添加机器人"用微信扫码接入。',"wx.saveFailed":"保存失败: {error}","wx.switchFailed":"切换失败: {error}","wx.confirmDelete":'确定删除机器人 "{name}"?微信里的好友会变成无响应状态。',"wx.deleteFailed":"删除失败: {error}","wx.label":"名称","wx.labelPlaceholder":"Bot 名称","wx.workspace":"绑定 workspace(绝对路径)","wx.accessLevel":"权限级别","wx.readonly":"Readonly — 仅读 workspace 内文件 / grep / glob(默认)","wx.wsWrite":"Workspace — workspace 内读写","wx.full":"Full — 完整权限,含写入和 shell","wx.readonlyHint":"Readonly:微信用户只能让 agent 查文件/搜索,无法写入、跑命令或联网。子 agent 自动继承同档位","wx.save":"保存","wx.cancel":"取消","wx.pathMissing":"(路径不存在)","wx.disable":"停用","wx.enable":"启用","wx.rescan":"重新扫码(刷新 token)","wx.edit":"编辑","wx.delete":"删除","wx.generatingQr":"正在生成二维码...","wx.pathRequired":"workspace 必须是绝对路径","wx.qrFailed":"生成二维码失败","wx.scanPrompt":"请使用微信扫描二维码","wx.connected":"连接成功 ({id})","wx.loginFailed":"登录失败","wx.rescanBtn":"重新扫码","wx.addTitle":"添加微信机器人","wx.bindWorkspace":"绑定到 workspace","wx.bindHint":"微信消息会进入该 workspace 的会话,web 端打开能看到","wx.nameOptional":"名称(可选)","wx.namePlaceholder":"比如:工作助理","wx.readonlyFull":"Full — 和 web 端等权","wx.readonlyShareHint":"Readonly:微信用户无法写入文件、跑 shell、或联网。子 agent 自动继承同档位。给客户分享时强烈建议用 Readonly","wx.nextStep":"下一步:扫码","wx.scanTitle":"微信扫码","wx.bindTo":"绑定到","wx.restart":"重新开始","media.image":"图片","media.video":"视频","media.voice":"语音","media.file":"文件","chat.imageSent":"[已发送 {n} 张图片]","chat.sendFailed":"发送失败——服务器未确认收到这条消息,请重新发送。","chat.sendFailedBadge":"发送失败","chat.attachUnsupported":"无法发送:{names}。这种图片格式模型不认,浏览器也转不了,请转成 png 或 jpg 再发。","chat.attachNoVision":"无法附加 {name}:当前 Agent 的模型不支持图片输入,请先换一个支持视觉的 Agent。","chat.interrupted":"未收到响应——连接可能已断开,如有需要请重发。","chat.window.showEarlier":"显示更早的消息(本会话还有 {count} 轮)","chat.archive.loadOlder":"加载更早的消息","chat.archive.loading":"正在加载更早的消息……","chat.archive.noEarlier":"没有更早的消息","chat.archive.header":"已归档 \xb7 {segments} 段 \xb7 {messages} 条消息","chat.access.full":"完全","chat.access.workspace":"工作区","chat.access.readonly":"只读","chat.access.fullDesc":"不受限制","chat.access.workspaceDesc":"只能写当前工作区","chat.access.readonlyDesc":"只读,不能写","chat.agent.select":"选择本次会话使用的 Agent","chat.agent.locked":"会话开始后就不能再切换 Agent 啦,想换的话新建一个会话就行。","chat.access.title":"下一条消息的访问级别","chat.access.noSandbox":"本机没有可用的系统沙箱(bubblewrap / sandbox-exec),只能用完全档","chat.tabs.newSession":"新会话","chat.tabs.untitled":"未命名会话","chat.tabs.unread":"有新消息","chat.sessions.title":"会话","chat.sessions.new":"新建会话","chat.sessions.rename":"重命名","chat.sessions.delete":"删除会话","chat.sessions.deleteConfirm":"删除这个会话?删除后历史记录无法恢复。","chat.sessions.deleteTreeConfirm":"删除这个会话及其全部子会话?删除后历史记录无法恢复。","chat.exchange.deleteConfirm":"从对话中删除这条消息及其回复?删除后 agent 将不再看到这轮对话。","goal.banner.intake":"\uD83C\uDFAF 目标模式 \xb7 契约制定中——正在和你确认目标","goal.banner.running":"\uD83C\uDFAF 目标模式 \xb7 第 {round}/{max} 轮","goal.banner.paused":"⏸ 目标已暂停——可直接与工作会话对话,/goal resume 继续","goal.banner.halted":"⛔ 目标已中止——到目标会话查看诊断","goal.banner.done":"✅ 目标完成——查看最终报告","goal.banner.jump":"跳转到目标会话","goal.banner.worker":"工作会话 →","goal.banner.jump_worker":"跳转到工作会话","goal.banner.dismiss":"关闭","goal.inputLocked":"目标模式接管中——请在目标会话中对话,或 /goal pause 手动接管","link.fresh":"已连接","link.stale":"连接不稳定——探测中…","link.down":"连接已断开","agent.agents":"Agents","agent.selectToEdit":"选择一个 Agent 进行编辑","agent.back":"返回","agent.test":"测试","agent.edit":"编辑","agent.editHint":"打开 agent.yaml + AGENT.md 编辑器","agent.saving":"保存中…","agent.loading":"加载中...","agent.loadFailed":"agent.yaml 加载失败","agent.yamlInvalid":'agent.yaml 无法解析,请到"编辑"视图中修复',"agent.retry":"重试","agent.mdPreviewHint":'预览(只读 — 点击上方"编辑"修改)',"agent.basic":"基本信息","agent.name":"名称","agent.description":"描述","agent.priority":"优先级","agent.model":"模型","agent.provider":"提供商","agent.modelId":"模型 ID","agent.endpoint":"端点","agent.endpointSwitched":"端点已切换为 {endpoint} — {model} 不在原端点上提供","agent.endpointOffList":"{model} 未列出此端点 — 请求可能失败","agent.contextMax":"模型最大 {max}","agent.contextOverMax":"超过模型最大 {max} — 未保存","agent.maxTokens":"最大 Token","agent.maxTokensDefault":"默认: {max} (模型上限)","agent.maxTokensAuto":"留空使用模型默认值","agent.systemPrompt":"系统提示词","agent.systemPromptHint":"(无 AGENT.md 时的 fallback)","agent.systemPromptPlaceholder":"给这个 agent 的指令...","agent.context":"上下文","agent.team":"团队","agent.teamHint":"(选择可委派的成员 — 选中任意一个即获得会话/委派工具;不选则无法委派)","agent.loadingTools":"加载中...","agent.noSkills":"暂无可用 Skills。请先创建。","skill.skills":"Skills","skill.noSkills":"暂无 Skills。点击分组旁的 + 创建。","skill.section.extension":"扩展","skill.fromExtension":"来自扩展 {ext}","skill.readOnly":"只读","skill.extensionHint":"由扩展自带,卸载扩展时一并移除。如需自定义,把该目录复制到 ~/.halo/global/skills/{id}/ 或 <workspace>/.halo/skills/{id}/,复制出的版本优先生效。","settings.general":"常规","settings.params":"参数","settings.emptyParams":"暂无参数。在 YAML 视图中添加 params.x.y 条目,可在 AGENT.md / SKILL.md 中通过 {{params.x.y}} 引用。","settings.emptyGeneral":"该分类暂无配置项。切换到 YAML 视图手动添加。","settings.nav.system":"系统","settings.nav.security":"安全","settings.nav.extensions":"扩展","settings.nav.extensionSettings":"扩展配置","settings.nav.providers":"模型供应商","settings.nav.skills":"技能","settings.nav.agents":"Agent","settings.nav.orphans":"孤儿项","settings.nav.unclaimed":"无主值","settings.section.namespace":"命名空间","settings.section.declaredBy":"声明方","settings.section.declaredBy.general":"系统","settings.section.declaredBy.provider":"模型供应商","settings.section.declaredBy.skill":"技能","settings.section.declaredBy.agent":"Agent","settings.section.declaredBy.extension":"扩展","settings.section.settings":"设置","settings.section.params":"参数","settings.section.secrets":"密钥","settings.section.secretsHint":"仅服务端可见,永不注入到 Agent 提示词或 shell 命令中。{placeholder} 占位符会在读取时从进程环境变量中替换。","settings.field.reset":"重置","settings.field.resetTitleWorkspace":"移除 workspace 覆盖(回退到 global)","settings.field.resetTitleGlobal":"移除 global 值(回到默认)","settings.source.workspace":"workspace","settings.source.global":"global","settings.source.inherited":"继承自 global","settings.source.unset":"未设置","settings.orphans.title":"无主值","settings.orphans.intro":"这些值在 settings.yaml 中存在,但当前没有任何模型供应商或技能声明它们。可能是卸载了对应包,或声明被移除。保留(如果对应命名空间回归会自动接上)或删除都安全。","settings.orphans.remove":"移除","settings.orphans.confirm":"移除孤儿项 {key}?将从 settings.yaml 删除该值。","settings.empty":"未选中分类。","settings.loading":"加载中…","settings.globalOnlyHint":"只在全局生效;workspace 级覆盖会被忽略","settings.section.advanced":"高级","settings.restartNotice":"已保存。需重启 Halo 服务才会生效:{keys}","settings.restartNotice.dismiss":"关闭","settings.extensions.title":"预览扩展","settings.extensions.intro":"Canvas 预览扩展让编辑器打开内置不支持的文件类型。安装在 ~/.halo/global/extensions/,所有 workspace 共享。需要 full 权限。","settings.extensions.upload":"从 zip 安装","settings.extensions.working":"处理中…","settings.extensions.empty":"尚未安装任何扩展。","settings.extensions.optionOnly":'仅"打开方式"',"settings.extensions.canSave":"可保存","settings.extensions.bundle":"打开文件夹","settings.extensions.media":"麦克风 / 屏幕","settings.extensions.fsRead":"读取本机文件","settings.extensions.unsupportedHere":"当前设备不支持","extensions.platform.web":"浏览器","extensions.platform.desktop-mac":"Mac 客户端","extensions.platform.desktop-win":"Windows 客户端","extensions.platform.desktop-linux":"Linux 客户端","extensions.platform.sep":"、","settings.extensions.remove":"卸载","settings.extensions.confirmRemove":"卸载扩展 {name}?正在使用它的标签页会回退到内置预览。","security.changePassword.title":"修改密码","security.changePassword.desc":"修改 admin 登录密码。已登录的会话保持有效。","security.changePassword.current":"原密码","security.changePassword.new":"新密码","security.changePassword.confirm":"确认新密码","security.changePassword.rule":"至少 8 位,且包含至少一个字母和一个数字。","security.changePassword.tooShort":"密码至少需要 8 位","security.changePassword.needLetter":"密码需包含至少一个字母","security.changePassword.needDigit":"密码需包含至少一个数字","security.changePassword.sameAsOld":"新密码不能与原密码相同","security.changePassword.mismatch":"两次输入的密码不一致","security.changePassword.submit":"修改密码","security.changePassword.submitting":"修改中…","security.changePassword.success":"密码已修改,下次登录请使用新密码。","security.changePassword.connectionFailed":"连接失败","security.logout":"退出登录","security.logout.desc":"在此浏览器上退出登录并返回登录页。","nav.explorer":"文件","nav.sourceControl":"源代码管理","nav.sessions":"会话","nav.channels":"渠道","nav.settings":"设置","nav.canvas":"画布","nav.chat":"对话","nav.terminal":"终端","nav.desc.explorer":"浏览、编辑和预览文件","nav.desc.sessions":"查看所有会话记录","nav.desc.source-control":"查看改动、提交和推送","nav.desc.management":"配置 Agent 的模型、工具和提示词","nav.desc.skills":"管理技能和技能配置","nav.desc.channels":"接入飞书、微信、Telegram 等","nav.desc.evolution":"审核 Agent 的自我改进","nav.desc.cron":"定时让 Agent 跑任务","nav.desc.settings":"模型、外观、扩展等全局设置","nav.desc.quick":"网络状态、完成通知等","status.busy":"运行中…","status.idle":"空闲","status.notifyBody":"Agent 已完成回复。","status.notifyBodySession":"「{title}」已完成回复。","sc.refresh":"刷新","sc.staged":"已暂存的更改","sc.changes":"更改","sc.stageAll":"暂存所有更改","sc.unstageAll":"取消暂存所有更改","sc.commit":"提交","sc.commitPlaceholder":"消息(Ctrl+Enter 提交)","sc.push":"推送","sc.pull":"拉取","sc.clean":"没有更改","sc.initRepo":"初始化仓库","sc.initRepoDesc":"用 git 开始跟踪此文件夹。会创建 .gitignore 和一个初始提交。","sc.initializing":"初始化中…","sc.addRemoteDesc":"尚未配置远程仓库。填入你在 GitHub/GitLab 上新建的空仓库地址即可推送。","sc.remotePlaceholder":"https://github.com/user/repo.git","sc.addRemote":"添加远程仓库","sc.adding":"添加中…","sc.authError":"推送失败:需要先配置 Git 凭据。配置后再推送。","sc.openCreds":"打开凭据设置","sc.noWorkspace":"打开一个文件夹以使用源代码管理","sc.selectFile":"选择文件查看差异对比","sc.loadingDiff":"正在加载差异…","sc.diffStaged":"(已暂存)","sc.diffWorking":"(工作区)","sc.viewChanges":"更改","sc.viewGraph":"提交历史","sc.noCommits":"暂无提交","sc.noCommitFiles":"无文件变更","sc.pushed":"已推送到远程","sc.unpushed":"仅本地 — 未推送","sc.cred.title":"Git 凭据","sc.cred.tabHttps":"HTTPS","sc.cred.tabSsh":"SSH","sc.cred.httpsDesc":"用 GitHub/GitLab 用户名 + 个人访问令牌(PAT,不是账号密码)登录。凭据存在磁盘上——配一次,重启不丢。","sc.cred.configuredList":"已配置的凭据","sc.cred.noCredsYet":"还没有配置任何凭据","sc.cred.delete":"删除","sc.cred.confirmDelete":"确认删除?","sc.cred.addNew":"添加新凭据","sc.cred.host":"地址","sc.cred.username":"用户名","sc.cred.token":"个人访问令牌","sc.cred.tokenHint":"以 0600 权限保存在本地,不会再次显示。","sc.cred.tokenLink":"如何生成 PAT","sc.cred.required":"地址、用户名和令牌都必填。","sc.cred.save":"保存","sc.cred.saving":"保存中…","sc.cred.cancel":"取消","sc.cred.close":"关闭","sc.cred.sshDesc":"用 SSH 密钥推送/拉取。halo 自持一个与内置终端共享的 ssh-agent——在下面解锁密钥,推送/拉取即可生效。密码直接交给 ssh-add,halo 不会保存。","sc.cred.sshAgentOn":"ssh-agent 就绪 \xb7 已加载 {count} 个密钥","sc.cred.sshAgentOff":"ssh-agent 未运行","sc.cred.sshRefresh":"刷新","sc.cred.sshKeys":"~/.ssh 中的密钥","sc.cred.sshNoKeys":"~/.ssh 中未找到私钥","sc.cred.sshLoaded":"已加载","sc.cred.sshUnlock":"解锁","sc.cred.sshUnlocking":"解锁中…","sc.cred.sshPassphrase":"密钥密码","sc.cred.sshUnlockHint":"输入密码将此密钥加载进 agent。密码直接交给 ssh-add——不保存、不记录日志。","sc.cred.sshUnlockFailed":"无法解锁该密钥","sc.cred.sshRemote":"Remote 协议(origin)","sc.cred.sshRestartNote":"注意:加载的 SSH 密钥只在服务运行期间存在于 agent 中——halo 重启后需要重新解锁一次(类似电脑重启后要重新解锁 SSH)。HTTPS 令牌存在磁盘上,重启不丢。","common.language":"语言","common.global":"全局","common.workspace":"工作区","common.internal":"内部","common.imageUnavailable":"图片无法加载","sidebar.collapse":"收起","sidebar.expand":"展开","sidebar.resize":"拖动调整宽度","terminal.list.title":"终端","terminal.list.new":"新建终端","terminal.list.close":"关闭终端","terminal.menu.copy":"复制","terminal.menu.paste":"粘贴","terminal.menu.selectAll":"全选","terminal.menu.clear":"清屏","terminal.menu.pasteHint":"此处无法读取剪贴板,请按 {key} 粘贴。","dataPreview.range":"第 {from}–{to} 行 / 共 {total} 行","dataPreview.prev":"上一页","dataPreview.next":"下一页","dataPreview.empty":"无数据行","dataPreview.noTables":"该数据库中没有表","editor.hideNotes":"隐藏备注","editor.showNotes":"显示备注","editor.notes":"备注","editor.zoomOut":"缩小","editor.zoomIn":"放大","editor.resetZoom":"重置缩放","editor.annotate":"标注","editor.annotate.hint":"在图上画几笔,发到对话里","editor.annotate.exit":"退出标注(丢弃标记)","editor.annotate.pen":"画笔","editor.annotate.rect":"矩形","editor.annotate.arrow":"箭头","editor.annotate.red":"红色","editor.annotate.yellow":"黄色","editor.annotate.green":"绿色","editor.annotate.blue":"蓝色","editor.annotate.undo":"撤销","editor.annotate.clear":"清空","editor.annotate.send":"发送到对话","editor.annotate.failed":"标注图片生成失败,请重试。","explorer.download.title":"将「{name}」打包为 zip 下载","explorer.download.workspace":"工作区","explorer.download.estimating":"正在统计文件…","explorer.download.file":"{n} 个文件","explorer.download.files":"{n} 个文件","explorer.download.excludedDefault":"默认排除:{list}","explorer.download.otherFolders":"其他 {n} 个构建/缓存目录","explorer.download.excludedBlocked":"始终排除:{n} 项(.env 文件、密钥、Halo 会话数据)","explorer.download.excludedSkipped":"已跳过:{n} 项(无法读取、链接指向工作区外,或文件名无法放入 zip)","explorer.download.include":"包含 node_modules、.git 及其他构建/缓存目录","explorer.download.cancel":"取消","explorer.download.confirm":"下载","editor.print":"打印","editor.openAsText":"以文本打开","editor.download":"下载","editor.hideOutline":"隐藏大纲","editor.showOutline":"显示大纲","editor.outline":"大纲","editor.viewDiff":"查看未保存的差异","editor.diff":"差异","editor.splitRight":"向右拆分编辑器","editor.openWith":"打开方式","editor.openWith.builtin":"内置({id})","editor.unsupported.title":"此文件类型没有内置预览。","editor.unsupported.hub":"更多文件类型的扩展见","editor.unsupported.platform":"「{name}」仅支持:{platforms}","editor.extension.save":"保存","editor.extension.retry":"重试","editor.extension.reload":"重新加载","editor.extension.unresponsive":"扩展「{name}」未响应。","editor.extension.updated":"扩展已更新到 v{version},保存后重新加载以使用新版本。","editor.extension.uninstalled":"此扩展已卸载。保存修改后请关闭此标签页。","editor.extension.exported":"已导出到 {path}","editor.extension.exportOverwrite":"「{name}」已存在,是否覆盖?","editor.extension.exportFailed":"导出失败:{message}","editor.immersive.exit":"\xd7 退出放大 \xb7 Esc","editor.immersive.replied":"Agent 已回复 \xb7 {title}","editor.immersive.repliedMany":"{count} 个会话已回复","editor.immersive.backToChat":"回到对话","picker.title":"选择文件","picker.workspace":"工作区","picker.home":"主目录","picker.pathBox":"路径","picker.loading":"加载中…","picker.cancel":"取消","picker.empty":"此文件夹没有文件","picker.emptyAccept":"没有匹配的文件({accept})","cron.title":"定时任务","cron.new":"新建","cron.refresh":"刷新","cron.empty":"还没有定时任务。点击「新建」创建一个。","cron.loading":"加载中…","cron.error":"错误:{error}","cron.confirmDelete":"删除这个定时任务?运行历史也会一起删掉。","cron.err.wechat.noInbound":"该用户尚未给此 bot 发过消息——微信 ilink 协议要求对方先开口,bot 才能主动推送。请让 ta 给 bot 发任意一条消息后再试。","cron.next":"下一次:","cron.runNow":"立即运行","cron.edit":"编辑","cron.delete":"删除","cron.disabled":"已禁用","cron.badge.activeFrom":"{date} 起生效","cron.badge.ended":"已截止","cron.field.schedule":"调度:","cron.field.runAt":"触发时刻:","cron.field.agent":"Agent:","cron.field.timeoutSec":"超时:","cron.field.timeoutSecDefault":"3600 秒(默认)","cron.field.activeFrom":"生效起:","cron.field.activeUntil":"生效止:","cron.field.session":"会话:","cron.field.sessionDefault":"{id}(默认)","cron.field.sessionStateless":"每次运行新建会话","cron.field.folder":"目录:","cron.field.folderRoot":"(工作区根目录)","cron.run.session":"本次运行使用的会话","cron.field.workspace":"工作区:","cron.field.targets":"通知目标:","cron.field.targetsLogOnly":"— (仅记录日志)","cron.field.prompt":"提示词","cron.runHistory":"运行历史","cron.noRuns":"还没有运行记录。","cron.dispatch":"通知:","cron.loadOlder":"加载更早的运行","cron.log.title":"日志:{id}","cron.log.close":"关闭","cron.pickJob":"挑一个任务,或点「新建」创建一个。","cron.form.create":"新建定时任务","cron.form.edit":"编辑定时任务","cron.form.cancel":"取消","cron.form.save":"保存","cron.form.create.btn":"创建","cron.form.saving":"保存中…","cron.form.label":"标签(可选)","cron.form.label.placeholder":"每日报告","cron.form.workspace":"工作区路径 *","cron.form.workspace.placeholder":"/绝对路径/到/workspace","cron.form.agent":"Agent *","cron.form.mode":"触发方式","cron.form.mode.recurring":"周期性(cron 表达式)","cron.form.mode.oneShot":"一次性(指定时刻触发)","cron.form.schedule":"调度(cron 表达式)*","cron.form.schedule.placeholder":"0 9 * * *","cron.form.schedule.hint":"标准 5 段 cron。例:0 9 * * * = 每天 09:00 \xb7 */15 * * * * = 每 15 分钟 \xb7 0 9 * * 1 = 每周一 09:00","cron.form.runAt":"触发时刻 *","cron.form.runAt.hint":"任务会在选中的时区下,按这个时刻触发一次,触发后自动禁用。","cron.form.timezone":"时区","cron.form.timezone.hostDefault":"默认 — 主机时区({tz})","cron.form.timeoutSec":"最长运行时间(秒)","cron.form.timeoutSec.placeholder":"3600(默认)","cron.form.timeoutSec.hint":"运行超过该时长会被强制终止。留空 = 默认 3600 秒。范围 60–21600。","cron.form.activeFrom":"生效起(可选)","cron.form.activeUntil":"生效止(可选)","cron.form.activeClear":"清空","cron.form.active.hint":"只在这个时间段内按调度触发(按所选时区,止时刻不含);段外的触发静默跳过。想暂停到某天,就把「生效起」设成那天。「立即运行」不受限制。","cron.form.session":"会话","cron.form.session.placeholder":"留空 = 任务自己的会话(id 与任务 id 相同)","cron.form.session.hint":"选一个已有会话,任务就在其中继续对话:沿用该会话自己的 agent 和访问级别(上面的 Agent 会锁定为它)。该会话正有回合在跑时本次运行会跳过;若运行期间你恰好在里面发消息,其中一轮可能从历史中丢失。","cron.form.agent.locked":"会话 {id} 已用 {agent} 运行过。删除该会话或开启「每次运行新建会话」后才能更换 Agent。","cron.form.agent.mismatch":"此任务的会话 {id} 运行的是 Agent {agent};在改选 Agent {agent}、换一个会话或开启「每次运行新建会话」之前,每次运行都会失败。","cron.form.stateless":"会话模式","cron.form.stateless.label":"每次运行新建会话","cron.form.stateless.hint":"适合无状态任务:每次运行都从新会话开始,运行之间不保留上下文。历次运行的会话保留在「会话」标签页中。","cron.form.session.stateless":"每次运行新建会话","cron.form.folder":"目录","cron.form.folder.placeholder":"留空 = 工作区根目录","cron.form.folder.hint":"本次运行聚焦的工作区子目录:沿途的 .halo/INSTRUCTIONS.md 会并入提示词(文件和 shell 路径仍从工作区根目录起算)。每次运行都会应用。","cron.form.userPrompt":"用户提示词 *","cron.form.userPrompt.placeholder":"从 .halo/memory/ 整理今天的状态摘要","cron.form.targets":"通知目标(最终输出发到哪里)","cron.form.targets.empty":"还没有绑定的渠道。","cron.form.targets.disabled":"(已禁用)","cron.form.targets.noActiveChat":"还没有用户跟这个账号说过话 —— 在收到至少一条消息之前,cron 没法发送。","cron.form.targets.noActiveChatLabel":"(没有活跃会话)","cron.form.targets.hint":"留空表示仅记录日志(不通过渠道发送)。","cron.form.enabled":"启用(创建后立刻开始排期)","cron.form.err.workspace":"需要工作区路径","cron.form.err.agent":"需要选择 Agent","cron.form.err.schedule":"需要 cron 表达式","cron.form.err.runAt":"需要触发时刻","cron.form.err.runAtPast":"触发时刻必须在未来","cron.form.err.prompt":"需要填写提示词","cron.form.err.timeoutSec":"最长运行时间必须是 60–21600 之间的整数(秒)","cron.form.err.activeUntilPast":"生效止必须在未来","cron.form.err.activeOrder":"生效止必须晚于生效起","cron.form.err.chatId.slack":"Slack chatId 格式错误:「{chatId}」。应该是 D…(DM) / C…(频道) / G…(私有频道)。请用搜索后点击建议项填入,或从 Slack URL 复制。","cron.form.err.chatId.feishu":"飞书 chatId 格式错误:「{chatId}」。应该是 oc_…。请用搜索后点击建议项填入。","cron.form.err.chatId.telegram":"Telegram chatId 必须是数字(私聊 = user id)。「{chatId}」无效。","cron.form.err.chatId.wecom":"无效的企业微信 chatId “{chatId}”——应为 userid(单聊)或 chatid(群聊)。","cron.form.chatIdHint.telegram":"数字 chat id(私聊 = user id)","cron.form.chatIdHint.feishu":"oc_… 聊天 id","cron.form.chatIdHint.wecom":"userid(单聊)或 chatid(群聊)","cron.form.chatIdHelp":"多个 chat 用逗号分隔。留空则不推送(仅写日志)。","cron.form.searching":"搜索中…","evolution.title":"自我进化","evolution.refresh":"刷新","evolution.filter.all":"全部","evolution.filter.awaiting_review":"待审","evolution.filter.pending":"待跑","evolution.filter.running":"运行中","evolution.filter.approved":"已批准","evolution.filter.applied":"已应用","evolution.filter.skipped":"已跳过","evolution.filter.rejected":"已拒绝","evolution.filter.failed":"失败","evolution.filter.timeout":"超时","evolution.filter.archived":"已归档","evolution.list.empty":"当前筛选下没有运行记录。","evolution.list.pickRun":"选一个运行查看详情。","evolution.list.loading":"加载中…","evolution.list.noRun":"没有数据。","evolution.list.noHint":"没有提示","evolution.list.attempts":"{n} 次尝试","evolution.list.applyFailed":"应用失败","evolution.detail.workspace":"工作区:","evolution.detail.hint":"提示:","evolution.detail.failure":"失败:{reason}","evolution.detail.skipped":"Evo 主动跳过了这次 run —— 没有 patch 需要应用,无需 approve / reject。原因见下方 .skip.md。","evolution.detail.applyFailed":"已批准,但应用时未通过回归校验,主工作区未改动。补充提示后可重试。","evolution.action.approve":"批准","evolution.action.reject":"拒绝","evolution.action.confirmReject":"确定拒绝这次 run?","evolution.action.addHint":"添加备注","evolution.action.retry":"重试","evolution.action.retryWithHint":"带提示重试","evolution.action.confirmApprove":"确认批准","evolution.action.confirmRetry":"确认重试","evolution.action.add":"添加","evolution.action.cancel":"取消","evolution.action.retryNeedsHint":"重试需要带提示 —— 相同输入没有新指令,只会跑出同样的结果。","evolution.action.delete":"删除","evolution.action.confirmDelete":"删除这次 run?它的 patch、评分、日志和快照会被一并永久删除。","evolution.retry.title":"重试提示 —— 给 evo 的新指引,会替换原来的用户提示。","evolution.retry.placeholder":"例如:上次提了架构层面的改动,这次试试更小的、聚焦在用户实际模式上的 patch","evolution.retry.tooltip":"把这次 run 带新提示发回 evo","evolution.approve.title":"可选的审核提示 —— 会传给 apply agent。留空就按 patch 原样应用。","evolution.approve.placeholder":"例如:只应用 INSTRUCTIONS.md 那部分,不要碰 AGENT.md","evolution.hint.title":"给这次 run 加一条备忘。状态不变,仅作为后续查阅的笔记。","evolution.hint.placeholder":"例如:等下次 Bedrock 模型升级之后再回来看","evolution.score.title":"评分","evolution.score.lint":"lint","evolution.score.behavior":"行为","evolution.score.scope":"范围","evolution.score.avg":"平均","evolution.score.confidence":"置信度:","evolution.snapshot.title":"源会话({n} 条消息)","evolution.snapshot.firstUser":"首条用户消息:","evolution.snapshot.firstAssistant":"首条回复:","evolution.skip.title":".skip.md(evo 跳过本次 run 的理由)","evolution.patch.title":"patch.md","evolution.patch.empty":"还没有产出 patch","evolution.logs.title":"运行日志","evolution.logs.wrapper":"wrapper.log(编排层)","evolution.logs.subCli":"sub-cli.log(evo / score / dry-run 的标准输出)","time.justNow":"刚刚","time.minutes":"{n} 分钟前","time.hours":"{n} 小时前","time.days":"{n} 天前","quick.title":"快捷开关","quick.panelTitle":"状态与窗口","quick.on":"开","quick.off":"关","quick.network":"网络","quick.networkTitle":"网络连接","quick.networkSub":"只显示,不可切换","quick.notify":"通知","quick.notifyTitle":"完成时通知","quick.notifySub":"始终响提示音,窗口失焦时另弹通知","quick.pin":"置顶","quick.pinTitle":"窗口置顶","quick.pinSub":"浮在其他程序之上","quick.awake":"常亮","quick.awakeTitle":"保持屏幕常亮","quick.awakeSub":"窗口未最小化时生效;人离开也不会锁屏","capture.button":"共享一个窗口/屏幕给 AI 查看","capture.bound":"正在共享:{name}(AI 可主动截图查看)— 点击停止共享","capture.unbind":"停止共享","capture.pickTitle":"选择要共享的窗口或屏幕","capture.permissionHint":"需要「屏幕录制」权限。请在 系统设置 → 隐私与安全性 → 屏幕录制 里勾选 Halo,然后彻底退出并重新打开 Halo。","capture.openSettings":"打开系统设置","capture.loading":"正在加载来源…","capture.empty":"未找到窗口或屏幕。","capture.noPreview":"无预览(请授予屏幕录制权限)","capture.llmPrompt":"[系统:用户正在共享「{name}」窗口。当你需要查看它的当前画面来回答时,单独输出一行 <<<CAPTURE>>>,系统会自动截图并作为下一条消息发给你。不需要查看时不要输出该标记。]","capture.failNote":"[\uD83D\uDCF7 {name} — 截图失败:该窗口在后台太久被系统回收了画面,切到它再让我截一次]","capture.cameraName":"摄像头","capture.cameraButton":"开启摄像头,让 AI 主动拍照","capture.cameraBound":"摄像头已开启(AI 可主动拍照)— 点击关闭","capture.cameraPick":"选择摄像头","capture.cameraUse":"使用此摄像头","capture.cameraPermissionHint":"需要「摄像头」权限。请在 系统设置 → 隐私与安全性 → 摄像头 里勾选 Halo。","capture.cameraLlmPrompt":"[系统:用户已为你开启摄像头。当你需要查看摄像头当前画面来回答时,单独输出一行 <<<CAPTURE>>>,系统会自动拍一张照片并作为下一条消息发给你。不需要查看时不要输出该标记。]","capture.cameraFailNote":"[\uD83D\uDCF7 {name} — 拍照失败:摄像头可能被其他应用占用或权限被关闭]","capture.dismiss":"关闭","capture.webSurfaceScreen":"整个屏幕","capture.webSurfaceWindow":"共享的窗口","capture.webSurfaceTab":"浏览器标签页","capture.webLlmPrompt":"[系统:用户正在通过浏览器共享{name}。当你需要查看它的当前画面来回答时,单独输出一行 <<<CAPTURE>>>,系统会自动截图并作为下一条消息发给你。不需要查看时不要输出该标记。]","capture.webFailNote":"[\uD83D\uDCF7 {name} — 截图失败:共享可能已停止,或共享的窗口被最小化了 — 恢复后再让我截一次]","capture.bothLlmPrompt":"[系统:用户正在共享「{name}」,同时开着摄像头。当你需要查看画面来回答时,单独输出一行标记:只看屏幕输出 <<<CAPTURE:screen>>>,只看摄像头输出 <<<CAPTURE:camera>>>,两个都看输出 <<<CAPTURE>>>,系统会截图 / 拍照并作为下一条消息发给你。不需要查看时不要输出任何标记。]","capture.cameraPermissionHintWeb":"需要摄像头权限:点地址栏左侧的站点设置图标,允许摄像头后再试。","capture.screenshotButton":"截图:框选屏幕区域作为图片附件","capture.screenshotActive":"正在截图 — 点击取消","capture.screenshotHint":"拖拽框选区域 \xb7 Enter 确认 \xb7 Esc 取消","capture.screenshotConfirm":"确认(Enter)","capture.screenshotCancel":"取消(Esc)","face.button":"显示 AI 的面孔","face.on":"面孔已打开(AI 知道它开着)— 点击关闭"}},43672:(e,t,o)=>{o.d(t,{F:()=>a});let n="/api";async function s(e,t){let o=await fetch("".concat(n).concat(e),{headers:{"Content-Type":"application/json",...null==t?void 0:t.headers},...t});if(!o.ok){let e=await o.text().catch(()=>"");throw Error("API error ".concat(o.status,": ").concat(e||o.statusText))}return o.json()}let a={health:()=>fetch("".concat(n,"/health")).then(e=>e.json()),fs:{home:()=>s("/fs/home"),exists:e=>s("/fs/exists?path=".concat(encodeURIComponent(e))),browse:e=>s("/fs/browse?path=".concat(encodeURIComponent(e))),browseFiles:e=>s("/fs/browse?path=".concat(encodeURIComponent(e),"&files=1&sizes=1")),stat:e=>s("/fs/stat?path=".concat(encodeURIComponent(e))),resolveWorkspace:e=>s("/fs/workspace/resolve",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})})},files:{read:(e,t)=>s("/files?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t))),stat:(e,t)=>s("/files/stat?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t))),tree(e,t){let o=new URLSearchParams;return o.set("projectId",e),t&&o.set("path",t),s("/files/tree?".concat(o.toString()))},search(e,t,o,n){let a=new URLSearchParams;return a.set("projectId",e),a.set("q",t),o&&a.set("limit",String(o)),n&&a.set("dirsOnly","1"),s("/files/search?".concat(a.toString()))},upload(e,t,o,s,a){let r=new FormData;for(let n of(r.append("projectId",t),o&&r.append("targetDir",o),e))r.append("files",n);return new Promise((e,t)=>{let o=new XMLHttpRequest;o.open("POST","".concat(n,"/files/upload")),o.withCredentials=!0,o.upload.onprogress=e=>{s&&(e.lengthComputable?s(e.loaded,e.total):s(e.loaded,0))},o.onload=()=>{if(o.status>=200&&o.status<300)try{e(JSON.parse(o.responseText))}catch(e){t(e)}else t(Error("Upload failed: ".concat(o.status," ").concat(o.statusText)))},o.onerror=()=>t(Error("Upload failed: network error")),o.onabort=()=>t(new DOMException("Upload aborted","AbortError")),null==a||a.addEventListener("abort",()=>o.abort(),{once:!0}),o.send(r)})},save:(e,t,o)=>s("/files",{method:"PUT",body:JSON.stringify({path:e,content:t,projectId:o})}),async saveRaw(e,t,o,s,a){var r,i,c;let l,d=new URLSearchParams({path:e,projectId:o});null!=s&&d.set("expectMtime",String(s)),(null==a?void 0:a.create)&&d.set("create","1"),(null==a?void 0:a.append)&&d.set("append","1"),(null==a?void 0:a.root)&&d.set("root",a.root);try{l=await fetch("".concat(n,"/files/raw?").concat(d),{method:"PUT",headers:{"Content-Type":"application/octet-stream"},body:t})}catch(e){return{ok:!1,status:0,message:e instanceof Error?e.message:String(e)}}let u=await l.json().catch(()=>({}));return l.ok?{ok:!0,mtime:null!=(r=u.mtime)?r:0,size:null!=(i=u.size)?i:t.byteLength}:409===l.status&&"number"==typeof u.mtime?{ok:!1,status:409,mtime:u.mtime}:{ok:!1,status:l.status,message:null!=(c=u.error)?c:l.statusText}},create:(e,t)=>s("/files/new",{method:"POST",body:JSON.stringify({path:e,projectId:t})}),mkdir:(e,t)=>s("/files/mkdir",{method:"POST",body:JSON.stringify({path:e,projectId:t})}),rename:(e,t,o)=>s("/files/rename",{method:"POST",body:JSON.stringify({oldPath:e,newPath:t,projectId:o})}),remove:(e,t)=>s("/files?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t)),{method:"DELETE"}),downloadUrl:(e,t)=>"/api/files/download?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t)),viewUrl:(e,t)=>"/api/files/download?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t),"&inline=1"),downloadFolderUrl:(e,t,o)=>"/api/files/download-folder?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t)).concat(o?"&include=excluded":""),async downloadFolderEstimate(e,t,o,n){var s,r,i;let c=await fetch("".concat(a.files.downloadFolderUrl(e,t,o),"&estimate=1"),{signal:n});if(!c.ok){let e=await c.json().catch(()=>({}));return{ok:!1,status:c.status,message:null!=(s=e.error)?s:c.statusText}}let l=await c.json();if("number"!=typeof(null==l?void 0:l.files)||"number"!=typeof l.bytes)throw Error("Unexpected estimate response");return{ok:!0,files:l.files,bytes:l.bytes,excluded:null!=(r=l.excluded)?r:{},ignoredNames:null!=(i=l.ignoredNames)?i:{}}}},dataPreview:{sqliteTables:(e,t,o)=>s("/data-preview/sqlite/tables?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t)),{signal:o}),sqliteRows:(e,t,o,n,a,r)=>s("/data-preview/sqlite/rows?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t),"&table=").concat(encodeURIComponent(o),"&offset=").concat(n,"&limit=").concat(a),{signal:r}),parquet:(e,t,o,n,a)=>s("/data-preview/parquet?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t),"&offset=").concat(o,"&limit=").concat(n),{signal:a}),csv:(e,t,o,n,a)=>s("/data-preview/csv?path=".concat(encodeURIComponent(e),"&projectId=").concat(encodeURIComponent(t),"&offset=").concat(o,"&limit=").concat(n),{signal:a})},git:{status:e=>s("/git/status?projectId=".concat(encodeURIComponent(e))),ignored:e=>s("/git/ignored?projectId=".concat(encodeURIComponent(e))),diff(e,t,o,n,a){let r=new URLSearchParams;return r.set("projectId",e),r.set("path",t),r.set("staged",o?"1":"0"),n&&r.set("from",n),a&&r.set("commit",a),s("/git/diff?".concat(r.toString()))},log(e,t){let o=new URLSearchParams({projectId:e});return t&&o.set("limit",String(t)),s("/git/log?".concat(o.toString()))},commitFiles(e,t){let o=new URLSearchParams({projectId:e,hash:t});return s("/git/commit-files?".concat(o.toString()))},stage:(e,t)=>s("/git/stage",{method:"POST",body:JSON.stringify({projectId:e,paths:t})}),unstage:(e,t)=>s("/git/unstage",{method:"POST",body:JSON.stringify({projectId:e,paths:t})}),commit:(e,t)=>s("/git/commit",{method:"POST",body:JSON.stringify({projectId:e,message:t})}),push:e=>s("/git/push",{method:"POST",body:JSON.stringify({projectId:e})}),pull:e=>s("/git/pull",{method:"POST",body:JSON.stringify({projectId:e})}),init:e=>s("/git/init",{method:"POST",body:JSON.stringify({projectId:e})}),remotes:e=>s("/git/remotes?projectId=".concat(encodeURIComponent(e))),addRemote:(e,t,o)=>s("/git/remote",{method:"POST",body:JSON.stringify({projectId:e,url:t,name:o})}),getCredentials:()=>s("/git/credentials"),saveCredentials:e=>s("/git/credentials",{method:"POST",body:JSON.stringify(e)}),deleteCredential:e=>s("/git/credentials/".concat(encodeURIComponent(e)),{method:"DELETE"}),sshKeys:()=>s("/git/ssh/keys"),sshAgent:()=>s("/git/ssh/agent"),unlockSshKey:e=>s("/git/ssh/unlock",{method:"POST",body:JSON.stringify(e)}),getRemoteProtocol:e=>s("/git/remote/protocol?projectId=".concat(encodeURIComponent(e))),setRemoteProtocol:(e,t)=>s("/git/remote/protocol",{method:"POST",body:JSON.stringify({projectId:e,to:t})})},agentConfigs:{list(e){let t=e?"?projectId=".concat(encodeURIComponent(e)):"";return s("/agent-configs".concat(t))},create:e=>s("/agent-configs",{method:"POST",body:JSON.stringify(e)}),getYaml(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/agent-configs/".concat(e,"/yaml").concat(n))},saveYaml:(e,t,o)=>s("/agent-configs/".concat(e,"/yaml"),{method:"PUT",body:JSON.stringify({yaml:t,scope:null==o?void 0:o.scope,projectId:null==o?void 0:o.projectId})}),remove(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/agent-configs/".concat(e).concat(n),{method:"DELETE"})},toggle(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/agent-configs/".concat(e,"/toggle").concat(n),{method:"PATCH"})},tools:()=>s("/agent-configs/tools"),models:()=>s("/agent-configs/models"),getMdFile(e,t,o){let n=new URLSearchParams;(null==o?void 0:o.scope)&&n.set("scope",o.scope),(null==o?void 0:o.projectId)&&n.set("projectId",o.projectId);let a=n.toString()?"?".concat(n):"";return s("/agent-configs/".concat(e,"/md/").concat(t).concat(a))},saveMdFile:(e,t,o,n)=>s("/agent-configs/".concat(e,"/md/").concat(t),{method:"PUT",body:JSON.stringify({content:o,scope:null==n?void 0:n.scope,projectId:null==n?void 0:n.projectId})}),getMdAll(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/agent-configs/".concat(e,"/md-all").concat(n))},listSessions(e,t){let o=new URLSearchParams;(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId),(null==t?void 0:t.source)&&o.set("source",t.source);let n=o.toString()?"?".concat(o):"";return s("/agent-configs/".concat(e,"/sessions").concat(n))},getSession(e,t,o){let n=new URLSearchParams;(null==o?void 0:o.projectId)&&n.set("projectId",o.projectId),(null==o?void 0:o.source)&&n.set("source",o.source);let a=n.toString()?"?".concat(n):"";return s("/agent-configs/".concat(e,"/sessions/").concat(t).concat(a))},saveSession:(e,t)=>s("/agent-configs/".concat(e,"/sessions"),{method:"POST",body:JSON.stringify(t)}),deleteSession(e,t,o){let n=new URLSearchParams;(null==o?void 0:o.projectId)&&n.set("projectId",o.projectId),(null==o?void 0:o.source)&&n.set("source",o.source);let a=n.toString()?"?".concat(n):"";return s("/agent-configs/".concat(e,"/sessions/").concat(t).concat(a),{method:"DELETE"})},deleteAllSessions(e,t){let o=new URLSearchParams;(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId),(null==t?void 0:t.source)&&o.set("source",t.source),o.set("all","1");let n="?".concat(o);return s("/agent-configs/".concat(e,"/sessions").concat(n),{method:"DELETE"})}},sessionLogs:{list(e,t){let o=new URLSearchParams;e&&o.set("projectId",e),(null==t?void 0:t.includeArchived)&&o.set("includeArchived","1"),(null==t?void 0:t.parentId)!==void 0&&o.set("parentId",t.parentId),(null==t?void 0:t.rootOnly)&&o.set("rootOnly","1"),(null==t?void 0:t.cursor)!==void 0&&o.set("cursor",String(t.cursor)),(null==t?void 0:t.limit)!==void 0&&o.set("limit",String(t.limit));let n=o.toString()?"?".concat(o):"";return s("/sessions/logs".concat(n))},goal:e=>s("/sessions/goal?projectId=".concat(encodeURIComponent(e))),get(e,t){let o=new URLSearchParams;t&&o.set("projectId",t);let n=o.toString()?"?".concat(o):"";return s("/sessions/logs/".concat(e).concat(n))},delete(e,t){let o=new URLSearchParams;t&&o.set("projectId",t);let n=o.toString()?"?".concat(o):"";return s("/sessions/logs/".concat(e).concat(n),{method:"DELETE"})},archiveSegment:(e,t,o)=>s("/sessions/logs/".concat(e,"/archive/").concat(t,"?projectId=").concat(encodeURIComponent(o))),rename(e,t,o){let n=new URLSearchParams;o&&n.set("projectId",o);let a=n.toString()?"?".concat(n):"";return s("/sessions/logs/".concat(e).concat(a),{method:"PATCH",body:JSON.stringify({title:t})})}},commands:{list(e,t,o){let n=new URLSearchParams;e&&n.set("projectId",e),t&&n.set("sessionId",t),o&&n.set("agentId",o);let a=n.toString()?"?".concat(n.toString()):"";return s("/commands".concat(a))}},skills:{list(e){let t=e?"?projectId=".concat(encodeURIComponent(e)):"";return s("/skills".concat(t))},create:e=>s("/skills",{method:"POST",body:JSON.stringify(e)}),remove(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/skills/".concat(e).concat(n),{method:"DELETE"})},toggle(e,t){let o=new URLSearchParams;(null==t?void 0:t.scope)&&o.set("scope",t.scope),(null==t?void 0:t.projectId)&&o.set("projectId",t.projectId);let n=o.toString()?"?".concat(o):"";return s("/skills/".concat(e,"/toggle").concat(n),{method:"PATCH"})}},extensions:{list:()=>s("/extensions"),token:()=>s("/extensions/token"),async install(e){var t;let o=new FormData;o.append("file",e);let s=await fetch("".concat(n,"/extensions/install"),{method:"POST",body:o}),a=await s.json().catch(()=>({}));if(!s.ok)throw Error(null!=(t=a.error)?t:"API error ".concat(s.status,": ").concat(s.statusText));return a},remove:e=>s("/extensions/".concat(encodeURIComponent(e)),{method:"DELETE"})},wechat:{listAccounts:()=>s("/wechat/accounts"),startLogin:e=>s("/wechat/login/start",{method:"POST",body:JSON.stringify({sessionKey:e})}),waitLogin:e=>s("/wechat/login/wait",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/wechat/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/wechat/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"})},telegram:{listAccounts:()=>s("/telegram/accounts"),createAccount:e=>s("/telegram/accounts",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/telegram/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/telegram/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"})},web:{listAccounts:()=>s("/web/accounts"),createAccount:e=>s("/web/accounts",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/web/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/web/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"})},slack:{listAccounts:()=>s("/slack/accounts"),createAccount:e=>s("/slack/accounts",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/slack/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/slack/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"}),searchTargets:(e,t)=>s("/slack/accounts/".concat(encodeURIComponent(e),"/search?q=").concat(encodeURIComponent(t)))},feishu:{listAccounts:()=>s("/feishu/accounts"),createAccount:e=>s("/feishu/accounts",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/feishu/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/feishu/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"}),searchTargets:(e,t)=>s("/feishu/accounts/".concat(encodeURIComponent(e),"/search?q=").concat(encodeURIComponent(t)))},wecom:{listAccounts:()=>s("/wecom/accounts"),createAccount:e=>s("/wecom/accounts",{method:"POST",body:JSON.stringify(e)}),updateAccount:(e,t)=>s("/wecom/accounts/".concat(encodeURIComponent(e)),{method:"PATCH",body:JSON.stringify(t)}),deleteAccount:e=>s("/wecom/accounts/".concat(encodeURIComponent(e)),{method:"DELETE"})},settings:{get(e){let t=e?"?projectId=".concat(encodeURIComponent(e)):"";return s("/settings".concat(t))},getSchema(e){let t=e?"?projectId=".concat(encodeURIComponent(e)):"";return s("/settings/schema".concat(t))},save:(e,t,o)=>s("/settings",{method:"PUT",body:JSON.stringify({scope:e,projectId:o,settings:t})}),patch:(e,t,o,n)=>s("/settings",{method:"PATCH",body:JSON.stringify({scope:e,projectId:n,key:t,value:o})}),remove:(e,t,o)=>s("/settings",{method:"DELETE",body:JSON.stringify({scope:e,projectId:o,key:t})})},evolution:{listRuns(e){let t=new URLSearchParams;(null==e?void 0:e.archived)&&t.set("archived","1"),(null==e?void 0:e.limit)!==void 0&&t.set("limit",String(e.limit)),(null==e?void 0:e.before)!==void 0&&t.set("before",String(e.before));let o=t.toString()?"?".concat(t):"";return s("/evolution/runs".concat(o))},getRun:e=>s("/evolution/runs/".concat(encodeURIComponent(e))),approve:(e,t)=>s("/evolution/runs/".concat(encodeURIComponent(e),"/approve"),{method:"POST",body:JSON.stringify({reviewerHint:t})}),reject:e=>s("/evolution/runs/".concat(encodeURIComponent(e),"/reject"),{method:"POST",body:"{}"}),addHint:(e,t)=>s("/evolution/runs/".concat(encodeURIComponent(e),"/hint"),{method:"POST",body:JSON.stringify({hint:t})}),retry:(e,t)=>s("/evolution/runs/".concat(encodeURIComponent(e),"/retry"),{method:"POST",body:JSON.stringify({hint:t})}),delete:e=>s("/evolution/runs/".concat(encodeURIComponent(e)),{method:"DELETE"})},cron:{listJobs(e){let t=new URLSearchParams;(null==e?void 0:e.limit)!==void 0&&t.set("limit",String(e.limit)),(null==e?void 0:e.before)!==void 0&&t.set("before",String(e.before));let o=t.toString()?"?".concat(t):"";return s("/cron/jobs".concat(o))},createJob:e=>s("/cron/jobs",{method:"POST",body:JSON.stringify(e)}),updateJob:(e,t)=>s("/cron/jobs/".concat(encodeURIComponent(e)),{method:"PUT",body:JSON.stringify(t)}),deleteJob:e=>s("/cron/jobs/".concat(encodeURIComponent(e)),{method:"DELETE"}),runNow:e=>s("/cron/jobs/".concat(encodeURIComponent(e),"/run-now"),{method:"POST",body:"{}"}),listRuns(e,t){let o=new URLSearchParams;(null==t?void 0:t.limit)&&o.set("limit",String(t.limit)),(null==t?void 0:t.before)&&o.set("before",t.before);let n=o.toString()?"?".concat(o):"";return s("/cron/jobs/".concat(encodeURIComponent(e),"/runs").concat(n))},getRunLog:e=>s("/cron/runs/".concat(encodeURIComponent(e),"/log")),listChannelTargets:()=>s("/cron/channel-targets"),meta:()=>s("/cron/meta")}}},45966:(e,t,o)=>{o.d(t,{en:()=>n});let n={"channels.wechat":"WeChat","channels.observerAdminOnly":"Observer (set by admin)","tg.title":"Telegram Bot","tg.desc":"Add a bot created by BotFather. Telegram messages will go to the bound workspace.","tg.add":"Add Bot","tg.loading":"Loading...","tg.empty":"No bots yet. Create one with @BotFather on Telegram, then click Add above.","tg.saveFailed":"Save failed: {error}","tg.switchFailed":"Switch failed: {error}","tg.confirmDelete":"Delete bot @{name}?","tg.deleteFailed":"Delete failed: {error}","tg.label":"Name","tg.labelPlaceholder":"Bot name","tg.workspace":"Bind workspace (absolute path)","tg.accessLevel":"Access level","tg.readonly":"Readonly — read only (default)","tg.wsWrite":"Workspace — read-write within workspace","tg.full":"Full — full access","tg.allowedUsers":"Allowed users (comma-separated user_id or @username, empty = no limit)","tg.save":"Save","tg.cancel":"Cancel","tg.whitelist":"Whitelist","tg.pathMissing":"(path does not exist)","tg.disable":"Disable","tg.enable":"Enable","tg.edit":"Edit","tg.delete":"Delete","tg.addTitle":"Add Telegram Bot","tg.tokenLabel":"Bot Token (from @BotFather)","tg.bindWorkspace":"Bind to workspace","tg.nameOptional":"Name (optional)","tg.namePlaceholder":"e.g. SA Agent","tg.allowedUsersOptional":"Allowed users (optional, comma-separated)","tg.allowedUsersHint":"user_id or @username, empty = no limit","tg.addBtn":"Add","slack.title":"Slack Bot","slack.desc":"Add a Slack app with Socket Mode enabled. The bot listens via a long-lived ws connection — no public webhook needed.","slack.add":"Add Bot","slack.loading":"Loading...","slack.empty":"No Slack bots yet. Create an app at api.slack.com, enable Socket Mode, then click Add above.","slack.saveFailed":"Save failed: {error}","slack.switchFailed":"Switch failed: {error}","slack.confirmDelete":"Delete bot for team {name}?","slack.deleteFailed":"Delete failed: {error}","slack.label":"Name","slack.labelPlaceholder":"Bot name","slack.workspace":"Bind workspace (absolute path)","slack.accessLevel":"Access level","slack.readonly":"Readonly — read only (default)","slack.wsWrite":"Workspace — read-write within workspace","slack.full":"Full — full access","slack.save":"Save","slack.cancel":"Cancel","slack.pathMissing":"(path does not exist)","slack.disable":"Disable","slack.enable":"Enable","slack.edit":"Edit","slack.delete":"Delete","slack.addTitle":"Add Slack Bot","slack.botTokenLabel":"Bot Token (xoxb-…)","slack.botTokenHint":"OAuth & Permissions → Bot User OAuth Token","slack.appTokenLabel":"App Token (xapp-…)","slack.appTokenHint":"Basic Information → App-Level Tokens (with connections:write)","slack.bindWorkspace":"Bind to workspace","slack.nameOptional":"Name (optional)","slack.namePlaceholder":"e.g. SA Agent","slack.addBtn":"Add","feishu.title":"Feishu Bot","feishu.desc":"Add a Feishu (Lark) app with long-connect events enabled. The bot listens via a wss connection — no public webhook needed.","feishu.add":"Add Bot","feishu.loading":"Loading...","feishu.empty":"No Feishu bots yet. Create an app in the Feishu open platform, enable long-connect events, then click Add above.","feishu.saveFailed":"Save failed: {error}","feishu.switchFailed":"Switch failed: {error}","feishu.confirmDelete":"Delete bot for app {name}?","feishu.deleteFailed":"Delete failed: {error}","feishu.label":"Name","feishu.labelPlaceholder":"Bot name","feishu.workspace":"Bind workspace (absolute path)","feishu.accessLevel":"Access level","feishu.readonly":"Readonly — read only (default)","feishu.wsWrite":"Workspace — read-write within workspace","feishu.full":"Full — full access","feishu.save":"Save","feishu.cancel":"Cancel","feishu.pathMissing":"(path does not exist)","feishu.disable":"Disable","feishu.enable":"Enable","feishu.edit":"Edit","feishu.delete":"Delete","feishu.addTitle":"Add Feishu Bot","feishu.appIdLabel":"App ID","feishu.appSecretLabel":"App Secret","feishu.botOpenIdLabel":"Bot Open ID","feishu.botOpenIdHint":"The bot's own open_id, used to detect mentions in groups","feishu.verificationTokenLabel":"Verification Token (optional)","feishu.encryptKeyLabel":"Encrypt Key (optional)","feishu.bindWorkspace":"Bind to workspace","feishu.nameOptional":"Name (optional)","feishu.namePlaceholder":"e.g. SA Agent","feishu.addBtn":"Add","wecom.title":"WeCom Bot","wecom.desc":"Add a WeCom (企业微信) intelligent bot. Halo connects outbound over a wss long-connection using the bot ID + secret from the WeCom admin console — no public webhook needed.","wecom.add":"Add Bot","wecom.loading":"Loading...","wecom.empty":'No WeCom bots yet. Create an intelligent bot in the WeCom admin console, choose the long-connection ("长连接") receive mode, then click Add above.',"wecom.saveFailed":"Save failed: {error}","wecom.switchFailed":"Switch failed: {error}","wecom.confirmDelete":"Delete bot {name}?","wecom.deleteFailed":"Delete failed: {error}","wecom.label":"Name","wecom.labelPlaceholder":"Bot name","wecom.workspace":"Bind workspace (absolute path)","wecom.accessLevel":"Access level","wecom.readonly":"Readonly — read only (default)","wecom.wsWrite":"Workspace — read-write within workspace","wecom.full":"Full — full access","wecom.save":"Save","wecom.cancel":"Cancel","wecom.pathMissing":"(path does not exist)","wecom.disable":"Disable","wecom.enable":"Enable","wecom.edit":"Edit","wecom.delete":"Delete","wecom.addTitle":"Add WeCom Bot","wecom.botIdLabel":"Bot ID","wecom.secretLabel":"Secret","wecom.bindWorkspace":"Bind to workspace","wecom.nameOptional":"Name (optional)","wecom.namePlaceholder":"e.g. SA Agent","wecom.addBtn":"Add","web.title":"Web Access","web.desc":"Create tokens for web frontend access. Each token binds to one workspace.","web.add":"Create Access","web.loading":"Loading...","web.empty":"No web access yet. Click Create above to generate a token.","web.saveFailed":"Save failed: {error}","web.switchFailed":"Switch failed: {error}","web.confirmDelete":"Delete this access? The token will be invalidated.","web.deleteFailed":"Delete failed: {error}","web.label":"Name","web.labelPlaceholder":"Access name","web.workspace":"Bind workspace (absolute path)","web.accessLevel":"Access level","web.readonly":"Readonly — read only (default)","web.wsWrite":"Workspace — read-write within workspace","web.full":"Full — full access","web.observer":"Observer — global read-only (dashboards/metrics)","web.save":"Save","web.cancel":"Cancel","web.pathMissing":"(path does not exist)","web.copied":"Copied","web.copyToken":"Copy Token","web.disable":"Disable","web.enable":"Enable","web.edit":"Edit","web.delete":"Delete","web.createSuccess":"Created successfully","web.tokenNotice":"Save this token (shown only once):","web.done":"Done","web.addTitle":"Create Web Access","web.bindWorkspace":"Bind to workspace","web.nameOptional":"Name (optional)","web.namePlaceholder":"e.g. SA Demo","web.addBtn":"Create","wx.title":"WeChat Bot","wx.desc":"After scanning, WeChat messages will go to the bound workspace session.","wx.add":"Add Bot","wx.loading":"Loading...","wx.empty":'No bots yet. Click "Add Bot" above to scan with WeChat.',"wx.saveFailed":"Save failed: {error}","wx.switchFailed":"Switch failed: {error}","wx.confirmDelete":'Delete bot "{name}"? Friends will get no response.',"wx.deleteFailed":"Delete failed: {error}","wx.label":"Name","wx.labelPlaceholder":"Bot name","wx.workspace":"Bind workspace (absolute path)","wx.accessLevel":"Access level","wx.readonly":"Readonly — read workspace files / grep / glob (default)","wx.wsWrite":"Workspace — read-write within workspace","wx.full":"Full — full access, including write and shell","wx.readonlyHint":"Readonly: WeChat users can only let agent read files/search, cannot write, run commands or access network. Sub-agents inherit the same level.","wx.save":"Save","wx.cancel":"Cancel","wx.pathMissing":"(path does not exist)","wx.disable":"Disable","wx.enable":"Enable","wx.rescan":"Re-scan (refresh token)","wx.edit":"Edit","wx.delete":"Delete","wx.generatingQr":"Generating QR code...","wx.pathRequired":"Workspace must be an absolute path","wx.qrFailed":"Failed to generate QR code","wx.scanPrompt":"Scan with WeChat","wx.connected":"Connected ({id})","wx.loginFailed":"Login failed","wx.rescanBtn":"Re-scan","wx.addTitle":"Add WeChat Bot","wx.bindWorkspace":"Bind to workspace","wx.bindHint":"WeChat messages will go to this workspace session, visible in web panel","wx.nameOptional":"Name (optional)","wx.namePlaceholder":"e.g. Work Assistant","wx.readonlyFull":"Full — same as web","wx.readonlyShareHint":"Readonly: WeChat users cannot write files, run shell, or access network. Sub-agents inherit. Strongly recommended for sharing with clients.","wx.nextStep":"Next: Scan","wx.scanTitle":"Scan with WeChat","wx.bindTo":"Bind to","wx.restart":"Start over","media.image":"Image","media.video":"Video","media.voice":"Voice","media.file":"File","chat.imageSent":"[Sent {n} image(s)]","chat.sendFailed":"Send failed — the server never confirmed receipt. Please resend.","chat.sendFailedBadge":"send failed","chat.attachUnsupported":"Can't attach {names}: the model doesn't take this image format and the browser can't convert it. Convert to png or jpg first.","chat.attachNoVision":"Can't attach {name}: the current agent's model doesn't take images. Switch to an agent on a vision model first.","chat.interrupted":"No response received — the connection may have dropped. Resend if needed.","chat.window.showEarlier":"Show earlier messages ({count} more in this session)","chat.archive.loadOlder":"Load earlier messages","chat.archive.loading":"Loading earlier messages…","chat.archive.noEarlier":"No earlier messages","chat.archive.header":"Archived \xb7 {segments} segment(s) \xb7 {messages} messages","chat.access.full":"Full","chat.access.workspace":"Workspace","chat.access.readonly":"Readonly","chat.access.fullDesc":"No restrictions","chat.access.workspaceDesc":"Writes limited to this workspace","chat.access.readonlyDesc":"Read only, no writes","chat.agent.select":"Choose the agent for this conversation","chat.agent.locked":"The agent can’t be changed once the conversation has started — start a new session to use a different one.","chat.access.title":"Access level for the next message","chat.access.noSandbox":"This host has no OS sandbox (bubblewrap / sandbox-exec) — Full only","chat.tabs.newSession":"New session","chat.tabs.untitled":"Untitled session","chat.tabs.unread":"New messages","chat.sessions.title":"Sessions","chat.sessions.new":"New session","chat.sessions.rename":"Rename","chat.sessions.delete":"Delete session","chat.sessions.deleteConfirm":"Delete this session? Its history cannot be recovered.","chat.sessions.deleteTreeConfirm":"Delete this session and all its sub-sessions? History cannot be recovered.","chat.exchange.deleteConfirm":"Delete this message and its responses from the conversation? The agent will no longer see this exchange.","goal.banner.intake":"\uD83C\uDFAF Goal mode \xb7 intake — define the goal contract","goal.banner.running":"\uD83C\uDFAF Goal mode \xb7 round {round}/{max}","goal.banner.paused":"⏸ Goal paused — talk to the worker directly, /goal resume to continue","goal.banner.halted":"⛔ Goal halted — view the diagnosis in the goal session","goal.banner.done":"✅ Goal done — view the final report","goal.banner.jump":"Go to the goal session","goal.banner.worker":"Worker →","goal.banner.jump_worker":"Go to the worker session","goal.banner.dismiss":"Dismiss","goal.inputLocked":"Goal mode is driving this session — talk in the goal session, or /goal pause to take over","link.fresh":"Connected","link.stale":"Connection stale — probing…","link.down":"Disconnected","agent.agents":"Agents","agent.selectToEdit":"Select an agent to edit","agent.back":"Back","agent.test":"Test","agent.edit":"Edit","agent.editHint":"Open agent.yaml + AGENT.md in a mini workspace","agent.saving":"saving…","agent.loading":"Loading...","agent.loadFailed":"Failed to load agent.yaml","agent.yamlInvalid":"agent.yaml could not be parsed — fix it in the Edit view","agent.retry":"Retry","agent.mdPreviewHint":'preview (read-only — edit via "Edit" above)',"agent.basic":"Basic","agent.name":"name","agent.description":"description","agent.priority":"priority","agent.model":"Model","agent.provider":"provider","agent.modelId":"id","agent.endpoint":"endpoint","agent.endpointSwitched":"Endpoint switched to {endpoint} — {model} isn't served on the previous one","agent.endpointOffList":"{model} is not listed on this endpoint — requests may fail","agent.contextMax":"Model max {max}","agent.contextOverMax":"Exceeds model max {max} — not saved","agent.maxTokens":"maxTokens","agent.maxTokensDefault":"Default: {max} (model max)","agent.maxTokensAuto":"Leave blank for model default","agent.systemPrompt":"System Prompt","agent.systemPromptHint":"(fallback if no AGENT.md)","agent.systemPromptPlaceholder":"Instructions for this agent...","agent.context":"Context","agent.team":"Team","agent.teamHint":"(pick agents to delegate to — selecting any grants the session/delegation tools; none = no delegation)","agent.loadingTools":"Loading tools...","agent.noSkills":"No skills available. Create skills first.","skill.skills":"Skills","skill.noSkills":"No skills yet. Click + on a section to create one.","skill.section.extension":"Extensions","skill.fromExtension":"from extension {ext}","skill.readOnly":"read-only","skill.extensionHint":"Shipped by the extension and removed with it. To customise, copy this folder to ~/.halo/global/skills/{id}/ or <workspace>/.halo/skills/{id}/ — that copy takes precedence.","settings.general":"General","settings.params":"Params","settings.emptyParams":"No params yet. Add entries under params.x.y in YAML view — they can be referenced as {{params.x.y}} in AGENT.md / SKILL.md.","settings.emptyGeneral":"No settings in this section yet. Switch to YAML view to add manually.","settings.nav.system":"System","settings.nav.security":"Security","settings.nav.extensions":"Extensions","settings.nav.extensionSettings":"Extension settings","settings.nav.providers":"Model Providers","settings.nav.skills":"Skills","settings.nav.agents":"Agents","settings.nav.orphans":"Orphans","settings.nav.unclaimed":"Unclaimed values","settings.section.namespace":"Namespace","settings.section.declaredBy":"Declared by","settings.section.declaredBy.general":"General","settings.section.declaredBy.provider":"Provider","settings.section.declaredBy.skill":"Skill","settings.section.declaredBy.agent":"Agent","settings.section.declaredBy.extension":"Extension","settings.section.settings":"Settings","settings.section.params":"Params","settings.section.secrets":"Secrets","settings.section.secretsHint":"Server-side only. Never injected into agent prompts or shell commands. {placeholder} placeholders are pulled from the process environment at read time.","settings.field.reset":"Reset","settings.field.resetTitleWorkspace":"Remove workspace override (inherit from global)","settings.field.resetTitleGlobal":"Remove global value (revert to default)","settings.source.workspace":"workspace","settings.source.global":"global","settings.source.inherited":"inherited from global","settings.source.unset":"unset","settings.orphans.title":"Unclaimed values","settings.orphans.intro":"These values exist in settings.yaml but no provider or skill currently declares them. They might be left over from an uninstalled package — or the declaration was removed. Safe to keep (they re-attach automatically if their namespace comes back) or remove.","settings.orphans.remove":"Remove","settings.orphans.confirm":"Remove orphan {key}? This deletes the value from settings.yaml.","settings.empty":"No section selected.","settings.loading":"Loading…","settings.globalOnlyHint":"Honored only at the global layer; workspace overrides are ignored","settings.section.advanced":"Advanced","settings.restartNotice":"Saved. Restart the Halo server for this to take effect: {keys}","settings.restartNotice.dismiss":"Dismiss","settings.extensions.title":"Preview extensions","settings.extensions.intro":"Canvas preview extensions open file types the built-in editor cannot. Installed under ~/.halo/global/extensions/ and shared by every workspace. Requires full access.","settings.extensions.upload":"Install from zip","settings.extensions.working":"Working…","settings.extensions.empty":"No extensions installed.","settings.extensions.optionOnly":"open-with only","settings.extensions.canSave":"can save","settings.extensions.bundle":"opens folders","settings.extensions.media":"microphone / screen","settings.extensions.fsRead":"Reads files on this machine","settings.extensions.unsupportedHere":"not supported here","extensions.platform.web":"Browser","extensions.platform.desktop-mac":"Mac app","extensions.platform.desktop-win":"Windows app","extensions.platform.desktop-linux":"Linux app","extensions.platform.sep":", ","settings.extensions.remove":"Remove","settings.extensions.confirmRemove":"Remove extension {name}? Open tabs using it fall back to the built-in preview.","security.changePassword.title":"Change Password","security.changePassword.desc":"Change the admin login password. Existing sessions stay signed in.","security.changePassword.current":"Current password","security.changePassword.new":"New password","security.changePassword.confirm":"Confirm new password","security.changePassword.rule":"At least 8 characters, with at least one letter and one digit.","security.changePassword.tooShort":"Password must be at least 8 characters","security.changePassword.needLetter":"Password must contain at least one letter","security.changePassword.needDigit":"Password must contain at least one digit","security.changePassword.sameAsOld":"New password must differ from the current password","security.changePassword.mismatch":"Passwords don't match","security.changePassword.submit":"Change password","security.changePassword.submitting":"Changing…","security.changePassword.success":"Password changed. Use the new password next time you log in.","security.changePassword.connectionFailed":"Connection failed","security.logout":"Log out","security.logout.desc":"Sign out on this browser and return to the login page.","nav.explorer":"Explorer","nav.sourceControl":"Source Control","nav.sessions":"Sessions","nav.channels":"Channels","nav.settings":"Settings","nav.canvas":"Canvas","nav.chat":"CHAT","nav.terminal":"TERMINAL","nav.desc.explorer":"Browse, edit and preview files","nav.desc.sessions":"Browse every session's history","nav.desc.source-control":"Review changes, commit, push","nav.desc.management":"Models, tools and prompts","nav.desc.skills":"Manage skills and their settings","nav.desc.channels":"Feishu, WeChat, Telegram…","nav.desc.evolution":"Review self-improvements","nav.desc.cron":"Run agents on a schedule","nav.desc.settings":"Models, look, extensions…","nav.desc.quick":"Network, notifications…","status.busy":"Working…","status.idle":"Idle","status.notifyBody":"Agent finished responding.","status.notifyBodySession":"“{title}” finished responding.","sc.refresh":"Refresh","sc.staged":"Staged Changes","sc.changes":"Changes","sc.stageAll":"Stage All Changes","sc.unstageAll":"Unstage All Changes","sc.commit":"Commit","sc.commitPlaceholder":"Message (Ctrl+Enter to commit)","sc.push":"Push","sc.pull":"Pull","sc.clean":"No changes","sc.initRepo":"Initialize Repository","sc.initRepoDesc":"Start tracking this folder with git. Creates a .gitignore and an initial commit.","sc.initializing":"Initializing…","sc.addRemoteDesc":"No remote configured. Add the URL of an empty repository you created on GitHub/GitLab to push to.","sc.remotePlaceholder":"https://github.com/user/repo.git","sc.addRemote":"Add Remote","sc.adding":"Adding…","sc.authError":"Push failed: Git credentials needed. Configure them, then push again.","sc.openCreds":"Open credential settings","sc.noWorkspace":"Open a folder to use source control","sc.selectFile":"Select a file to view its diff","sc.loadingDiff":"Loading diff…","sc.diffStaged":"(staged)","sc.diffWorking":"(working tree)","sc.viewChanges":"Changes","sc.viewGraph":"Graph","sc.noCommits":"No commits yet","sc.noCommitFiles":"No files changed","sc.pushed":"Pushed to remote","sc.unpushed":"Local only — not pushed","sc.cred.title":"Git Credentials","sc.cred.tabHttps":"HTTPS","sc.cred.tabSsh":"SSH","sc.cred.httpsDesc":"Sign in with your GitHub/GitLab username and a Personal Access Token (not your account password). Stored on disk — configure once, survives restarts.","sc.cred.configuredList":"Configured credentials","sc.cred.noCredsYet":"No credentials configured yet","sc.cred.delete":"Delete","sc.cred.confirmDelete":"Confirm delete?","sc.cred.addNew":"Add credential","sc.cred.host":"Host","sc.cred.username":"Username","sc.cred.token":"Personal Access Token","sc.cred.tokenHint":"Stored locally with 0600 permissions, never displayed again.","sc.cred.tokenLink":"How to create a PAT","sc.cred.required":"Host, username and token are all required.","sc.cred.save":"Save","sc.cred.saving":"Saving…","sc.cred.cancel":"Cancel","sc.cred.close":"Close","sc.cred.sshDesc":"Use an SSH key for push/pull. halo runs one ssh-agent shared with its built-in terminal — unlock a key below and push/pull just works. The passphrase goes straight to ssh-add; halo never stores it.","sc.cred.sshAgentOn":"ssh-agent ready \xb7 {count} key(s) loaded","sc.cred.sshAgentOff":"ssh-agent not running","sc.cred.sshRefresh":"Refresh","sc.cred.sshKeys":"Keys in ~/.ssh","sc.cred.sshNoKeys":"No private keys found in ~/.ssh","sc.cred.sshLoaded":"Loaded","sc.cred.sshUnlock":"Unlock","sc.cred.sshUnlocking":"Unlocking…","sc.cred.sshPassphrase":"Key passphrase","sc.cred.sshUnlockHint":"Enter the passphrase to load this key into the agent. It goes straight to ssh-add — never stored or logged.","sc.cred.sshUnlockFailed":"Could not unlock the key","sc.cred.sshRemote":"Remote protocol (origin)","sc.cred.sshRestartNote":"Note: a loaded SSH key lives in the agent only while the server runs — after a halo restart, unlock it again (like re-unlocking SSH after a reboot). HTTPS tokens are stored on disk and survive restarts.","common.language":"Language","common.global":"Global","common.workspace":"Workspace","common.internal":"Internal","common.imageUnavailable":"Image unavailable","sidebar.collapse":"Collapse","sidebar.expand":"Expand","sidebar.resize":"Drag to resize","terminal.list.title":"Terminals","terminal.list.new":"New terminal","terminal.list.close":"Close terminal","terminal.menu.copy":"Copy","terminal.menu.paste":"Paste","terminal.menu.selectAll":"Select All","terminal.menu.clear":"Clear","terminal.menu.pasteHint":"Clipboard access unavailable here — press {key} to paste.","dataPreview.range":"{from}–{to} of {total} rows","dataPreview.prev":"Previous page","dataPreview.next":"Next page","dataPreview.empty":"No rows","dataPreview.noTables":"No tables in this database","editor.hideNotes":"Hide notes","editor.showNotes":"Show notes","editor.notes":"Notes","editor.zoomOut":"Zoom out","editor.zoomIn":"Zoom in","editor.resetZoom":"Reset zoom","editor.annotate":"Annotate","editor.annotate.hint":"Draw on the image and send it to chat","editor.annotate.exit":"Exit annotate (discards marks)","editor.annotate.pen":"Pen","editor.annotate.rect":"Rectangle","editor.annotate.arrow":"Arrow","editor.annotate.red":"Red","editor.annotate.yellow":"Yellow","editor.annotate.green":"Green","editor.annotate.blue":"Blue","editor.annotate.undo":"Undo","editor.annotate.clear":"Clear","editor.annotate.send":"Send to chat","editor.annotate.failed":"Couldn't render the annotated image. Please try again.","explorer.download.title":'Download "{name}" as zip',"explorer.download.workspace":"workspace","explorer.download.estimating":"Counting files…","explorer.download.file":"{n} file","explorer.download.files":"{n} files","explorer.download.excludedDefault":"Excluded by default: {list}","explorer.download.otherFolders":"{n} other build/cache folders","explorer.download.excludedBlocked":"Always excluded: {n} (.env files, secrets, Halo session data)","explorer.download.excludedSkipped":"Skipped: {n} (unreadable, links leaving the workspace, or names a zip can't hold)","explorer.download.include":"Include node_modules, .git and other build/cache folders","explorer.download.cancel":"Cancel","explorer.download.confirm":"Download","editor.print":"Print","editor.openAsText":"Open as text","editor.download":"Download","editor.hideOutline":"Hide outline","editor.showOutline":"Show outline","editor.outline":"Outline","editor.viewDiff":"View unsaved diff","editor.diff":"Diff","editor.splitRight":"Split editor right","editor.openWith":"Open with","editor.openWith.builtin":"Built-in ({id})","editor.unsupported.title":"This file type has no built-in preview.","editor.unsupported.hub":"Extensions for more file types are available at","editor.unsupported.platform":'"{name}" only runs on: {platforms}',"editor.extension.save":"Save","editor.extension.retry":"Retry","editor.extension.reload":"Reload","editor.extension.unresponsive":'Extension "{name}" did not respond.',"editor.extension.updated":"Extension updated to v{version} — save, then reload to use the new version.","editor.extension.uninstalled":"This extension has been uninstalled. Save your changes, then close the tab.","editor.extension.exported":"Exported to {path}","editor.extension.exportOverwrite":'"{name}" already exists next to this file. Overwrite it?',"editor.extension.exportFailed":"Export failed: {message}","editor.immersive.exit":"\xd7 Exit \xb7 Esc","editor.immersive.replied":"Agent replied \xb7 {title}","editor.immersive.repliedMany":"{count} sessions replied","editor.immersive.backToChat":"Back to chat","picker.title":"Choose a file","picker.workspace":"Workspace","picker.home":"Home","picker.pathBox":"Path","picker.loading":"Loading…","picker.cancel":"Cancel","picker.empty":"No files in this folder","picker.emptyAccept":"No matching files ({accept})","cron.title":"Cron Jobs","cron.new":"New","cron.refresh":"Refresh","cron.empty":'No cron jobs yet. Click "New" to create one.',"cron.loading":"Loading…","cron.error":"Error: {error}","cron.confirmDelete":"Delete this cron job? Run history is also removed.","cron.err.wechat.noInbound":"This user has never messaged the bot — WeChat ilink requires an inbound from them first before any push works. Ask them to send any message to the bot, then retry.","cron.next":"next:","cron.runNow":"Run now","cron.edit":"Edit","cron.delete":"Delete","cron.disabled":"disabled","cron.badge.activeFrom":"active from {date}","cron.badge.ended":"ended","cron.field.schedule":"Schedule:","cron.field.runAt":"Run at:","cron.field.agent":"Agent:","cron.field.timeoutSec":"Timeout:","cron.field.timeoutSecDefault":"3600s (default)","cron.field.activeFrom":"Active from:","cron.field.activeUntil":"Active until:","cron.field.session":"Session:","cron.field.sessionDefault":"{id} (default)","cron.field.sessionStateless":"new session every run","cron.field.folder":"Folder:","cron.field.folderRoot":"(workspace root)","cron.run.session":"Session this run used","cron.field.workspace":"Workspace:","cron.field.targets":"Targets:","cron.field.targetsLogOnly":"— (log only)","cron.field.prompt":"Prompt","cron.runHistory":"Run history","cron.noRuns":"No runs yet.","cron.dispatch":"Dispatch:","cron.loadOlder":"Load older runs","cron.log.title":"Log: {id}","cron.log.close":"Close","cron.pickJob":'Pick a job, or click "New" to create one.',"cron.form.create":"Create cron job","cron.form.edit":"Edit cron job","cron.form.cancel":"Cancel","cron.form.save":"Save","cron.form.create.btn":"Create","cron.form.saving":"Saving…","cron.form.label":"Label (optional)","cron.form.label.placeholder":"Daily report","cron.form.workspace":"Workspace path *","cron.form.workspace.placeholder":"/abs/path/to/workspace","cron.form.agent":"Agent *","cron.form.mode":"Trigger","cron.form.mode.recurring":"Recurring (cron)","cron.form.mode.oneShot":"One-time (specific instant)","cron.form.schedule":"Schedule (cron expression) *","cron.form.schedule.placeholder":"0 9 * * *","cron.form.schedule.hint":"Standard 5-field cron. Examples: 0 9 * * * = daily 09:00 \xb7 */15 * * * * = every 15 min \xb7 0 9 * * 1 = Mondays 09:00","cron.form.runAt":"Fire at *","cron.form.runAt.hint":"Job fires once at this instant (interpreted in the chosen timezone), then auto-disables.","cron.form.timezone":"Timezone","cron.form.timezone.hostDefault":"Default — host TZ ({tz})","cron.form.timeoutSec":"Max run time (seconds)","cron.form.timeoutSec.placeholder":"3600 (default)","cron.form.timeoutSec.hint":"Kill the run if it exceeds this. Empty = default 3600s. Range 60–21600.","cron.form.activeFrom":"Active from (optional)","cron.form.activeUntil":"Active until (optional)","cron.form.activeClear":"Clear","cron.form.active.hint":"Fires only inside this window (chosen timezone; until is exclusive). Fires outside it are skipped silently. To pause until a date, set “from”. Run now ignores it.","cron.form.session":"Session","cron.form.session.placeholder":"Empty = the job's own session (same id as the job)","cron.form.session.hint":"Pick an existing session to continue its conversation: it keeps its own agent and access level (the Agent above is locked to it). A run is skipped while a turn is running in that session; if you send a message there mid-run, one of the two turns may be lost from its history.","cron.form.agent.locked":'Session {id} already ran with {agent}. Delete it or turn on "new session every run" to change the agent.',"cron.form.agent.mismatch":'This job\'s session {id} runs agent {agent}; runs will fail until you pick agent {agent}, a new session, or "new session every run".',"cron.form.stateless":"Session mode","cron.form.stateless.label":"New session every run","cron.form.stateless.hint":"For stateless tasks: each run starts a fresh session (nothing carries over between runs). Old runs' sessions are kept in the Sessions tab.","cron.form.session.stateless":"A new session every run","cron.form.folder":"Folder","cron.form.folder.placeholder":"Empty = the workspace root","cron.form.folder.hint":"Workspace sub-folder the run focuses on: its .halo/INSTRUCTIONS.md chain joins the prompt (file and shell paths still start at the workspace root). Applied on every run.","cron.form.userPrompt":"User prompt *","cron.form.userPrompt.placeholder":"Generate today's status digest from .halo/memory/","cron.form.targets":"Channel targets (where to deliver the output)","cron.form.targets.empty":"No bound channels yet.","cron.form.targets.disabled":"(disabled)","cron.form.targets.noActiveChat":"No user has messaged this account yet — cron can't deliver until at least one inbound message","cron.form.targets.noActiveChatLabel":"(no active chat)","cron.form.targets.hint":"Leave empty to log only (no channel delivery).","cron.form.enabled":"Enabled (start scheduling immediately)","cron.form.err.workspace":"Workspace path required","cron.form.err.agent":"Agent required","cron.form.err.schedule":"Schedule required","cron.form.err.runAt":"Fire time required","cron.form.err.runAtPast":"Fire time must be in the future","cron.form.err.prompt":"Prompt required","cron.form.err.timeoutSec":"Max run time must be an integer between 60 and 21600 seconds","cron.form.err.activeUntilPast":"Active until must be in the future","cron.form.err.activeOrder":"Active until must be after active from","cron.form.err.chatId.slack":"Invalid Slack chatId “{chatId}” — expected D… (DM) / C… (channel) / G… (private channel). Search and click a suggestion, or copy it from the Slack URL.","cron.form.err.chatId.feishu":"Invalid Feishu chatId “{chatId}” — expected oc_…. Search and click a suggestion.","cron.form.err.chatId.telegram":"Telegram chatId must be numeric (DM = user id); “{chatId}” is invalid.","cron.form.err.chatId.wecom":"Invalid WeCom chatId “{chatId}” — expected a userid (single chat) or a chatid (group).","cron.form.chatIdHint.telegram":"numeric chat id (DM = user id)","cron.form.chatIdHint.feishu":"oc_… chat id","cron.form.chatIdHint.wecom":"userid (single) or chatid (group)","cron.form.chatIdHelp":"Comma-separate multiple chats. Leave empty to skip pushing (log only).","cron.form.searching":"Searching…","evolution.title":"Evolution","evolution.refresh":"Refresh","evolution.filter.all":"All","evolution.filter.awaiting_review":"Awaiting review","evolution.filter.pending":"Pending","evolution.filter.running":"Running","evolution.filter.approved":"Approved","evolution.filter.applied":"Applied","evolution.filter.skipped":"Skipped","evolution.filter.rejected":"Rejected","evolution.filter.failed":"Failed","evolution.filter.timeout":"Timeout","evolution.filter.archived":"Archived","evolution.list.empty":"No runs match this filter.","evolution.list.pickRun":"Select a run to view details.","evolution.list.loading":"Loading…","evolution.list.noRun":"No run.","evolution.list.noHint":"no hint","evolution.list.attempts":"{n} attempts","evolution.list.applyFailed":"apply failed","evolution.detail.workspace":"Workspace:","evolution.detail.hint":"Hint:","evolution.detail.failure":"Failure: {reason}","evolution.detail.skipped":"Evo skipped this run on its own — no patch to apply, no need to approve / reject. See .skip.md below for the reason.","evolution.detail.applyFailed":"Approved, but the apply failed its regression check and the main workspace was left unchanged. Add a hint and retry.","evolution.action.approve":"Approve","evolution.action.reject":"Reject","evolution.action.confirmReject":"Reject this run?","evolution.action.addHint":"Add hint","evolution.action.retry":"Retry","evolution.action.retryWithHint":"Retry with hint","evolution.action.confirmApprove":"Confirm approve","evolution.action.confirmRetry":"Confirm retry","evolution.action.add":"Add","evolution.action.cancel":"Cancel","evolution.action.retryNeedsHint":"Retry needs a hint — same input + no new direction would just reproduce the same output.","evolution.action.delete":"Delete","evolution.action.confirmDelete":"Delete this run? Its patch, score, logs, and snapshot are removed for good.","evolution.retry.title":"Retry hint — reviewer's steering for the next pass. Replaces the existing user hint, evo sees this as the new ask.","evolution.retry.placeholder":"e.g. last time you proposed an architecture change — try a smaller patch focused on the user pattern","evolution.retry.tooltip":"Send the run back to evo with a new hint","evolution.approve.title":"Optional reviewer hint — passed to the apply agent. Leave empty to approve as-is.","evolution.approve.placeholder":"e.g. only apply the INSTRUCTIONS.md change, not the AGENT.md one","evolution.hint.title":"Append a memo to the run's hint. Status doesn't change; this is just a note for later.","evolution.hint.placeholder":"e.g. revisit after the upcoming Bedrock model upgrade","evolution.score.title":"Score","evolution.score.lint":"lint","evolution.score.behavior":"behavior","evolution.score.scope":"scope","evolution.score.avg":"avg","evolution.score.confidence":"Confidence:","evolution.snapshot.title":"Source session ({n} msgs)","evolution.snapshot.firstUser":"First user:","evolution.snapshot.firstAssistant":"First reply:","evolution.skip.title":".skip.md (why evo skipped this run)","evolution.patch.title":"patch.md","evolution.patch.empty":"no patch produced yet","evolution.logs.title":"Run logs","evolution.logs.wrapper":"wrapper.log (orchestration)","evolution.logs.subCli":"sub-cli.log (evo / score / dry-run stdout+stderr)","time.justNow":"just now","time.minutes":"{n}m ago","time.hours":"{n}h ago","time.days":"{n}d ago","quick.title":"Quick toggles","quick.panelTitle":"Status & window","quick.on":"on","quick.off":"off","quick.network":"Network","quick.networkTitle":"Connection","quick.networkSub":"Status only, not a toggle","quick.notify":"Notify","quick.notifyTitle":"Notify when finished","quick.notifySub":"Always chimes; also a banner when the window is unfocused","quick.pin":"Pin","quick.pinTitle":"Keep window on top","quick.pinSub":"Float above other apps","quick.awake":"Awake","quick.awakeTitle":"Keep screen awake","quick.awakeSub":"While not minimized; the screen won't lock when you step away","capture.button":"Share a window/screen for the AI to see","capture.bound":"Sharing: {name} (AI can request a screenshot) — click to stop sharing","capture.unbind":"Stop sharing","capture.pickTitle":"Choose a window or screen to share","capture.permissionHint":"Screen Recording permission is needed. Enable Halo under System Settings → Privacy & Security → Screen Recording, then fully quit and reopen Halo.","capture.openSettings":"Open Settings","capture.loading":"Loading sources…","capture.empty":"No windows or screens found.","capture.noPreview":"No preview (grant Screen Recording)","capture.llmPrompt":'[System: the user is sharing the "{name}" window. When you need to see its current contents to answer, output a line containing exactly <<<CAPTURE>>> and the system will screenshot it and send it back to you as the next message. Do not output the marker unless you actually need to look.]',"capture.failNote":"[\uD83D\uDCF7 {name} — screenshot failed: the window sat in the background long enough for the OS to purge its frame — switch to it and ask me to capture again]","capture.cameraName":"Camera","capture.cameraButton":"Turn on the camera for the AI to take a photo","capture.cameraBound":"Camera on (AI can take a photo) — click to turn off","capture.cameraPick":"Choose camera","capture.cameraUse":"Use this camera","capture.cameraPermissionHint":"Camera permission is needed. Enable Halo under System Settings → Privacy & Security → Camera.","capture.cameraLlmPrompt":"[System: the user has turned the camera on for you. When you need to see what the camera is pointed at to answer, output a line containing exactly <<<CAPTURE>>> and the system will take a photo and send it back to you as the next message. Do not output the marker unless you actually need to look.]","capture.cameraFailNote":"[\uD83D\uDCF7 {name} — photo failed: the camera may be in use by another app, or its permission was turned off]","capture.dismiss":"Dismiss","capture.webSurfaceScreen":"Entire screen","capture.webSurfaceWindow":"Shared window","capture.webSurfaceTab":"Browser tab","capture.webLlmPrompt":"[System: the user is sharing {name} from the browser. When you need to see its current contents to answer, output a line containing exactly <<<CAPTURE>>> and the system will screenshot it and send it back to you as the next message. Do not output the marker unless you actually need to look.]","capture.webFailNote":"[\uD83D\uDCF7 {name} — screenshot failed: sharing may have stopped, or the shared window is minimized — restore it and ask me to capture again]","capture.bothLlmPrompt":'[System: the user is sharing "{name}" and has the camera on. When you need to look to answer, output one line with exactly one marker: <<<CAPTURE:screen>>> for the screen only, <<<CAPTURE:camera>>> for the camera only, or <<<CAPTURE>>> for both — the system will screenshot / take a photo and send it back to you as the next message. Do not output any marker unless you actually need to look.]',"capture.cameraPermissionHintWeb":"Camera permission is needed. Click the site settings icon at the left of the address bar, allow the camera, then try again.","capture.screenshotButton":"Screenshot: box a screen region as an image attachment","capture.screenshotActive":"Taking a screenshot — click to cancel","capture.screenshotHint":"Drag to select a region \xb7 Enter to confirm \xb7 Esc to cancel","capture.screenshotConfirm":"Confirm (Enter)","capture.screenshotCancel":"Cancel (Esc)","face.button":"Show the assistant's face","face.on":"Face on (the assistant can see it's open) — click to close"}},53804:(e,t,o)=>{o.d(t,{LanguageSelect:()=>a});var n=o(41440),s=o(19882);function a(e){let{value:t,onChange:o}=e,a=(0,s.useT)();return(0,n.jsxs)("div",{children:[(0,n.jsx)("label",{className:"text-[10px] text-[var(--muted-foreground)]",children:a("common.language")}),(0,n.jsxs)("select",{value:t,onChange:e=>o(e.target.value),className:"mt-0.5 w-full rounded border border-[var(--border)] bg-[var(--card)] px-2 py-1 text-xs",children:[(0,n.jsx)("option",{value:"en",children:"English"}),(0,n.jsx)("option",{value:"zh",children:"中文"})]})]})}},58321:(e,t,o)=>{o.d(t,{ThemeProvider:()=>u,useTheme:()=>d});var n=o(41440),s=o(54604),a=o(43672);let r=["dark","light","midnight","warm"];function i(e){return"string"==typeof e&&r.includes(e)}function c(e){"undefined"!=typeof document&&("dark"===e?delete document.documentElement.dataset.theme:document.documentElement.dataset.theme=e)}let l=(0,s.createContext)({theme:"dark",setTheme:()=>{},refreshFromServer:async()=>{}});function d(){return(0,s.useContext)(l)}function u(e){let{children:t}=e,[o,r]=(0,s.useState)("dark");(0,s.useEffect)(()=>{let e=function(){let e=localStorage.getItem("halo_theme");return i(e)?e:"dark"}();r(e),c(e)},[]);let d=(0,s.useCallback)(async()=>{try{var e,t;let o=(await a.F.settings.getSchema()).sections.find(e=>"general"===e.namespaceId),n=null==o?void 0:o.fields.find(e=>"theme"===e.key),s=null!=(t=null!=(e=null==n?void 0:n.value)?e:null==n?void 0:n.default)?t:null,l=i(s)?s:"dark";r(l),c(l),localStorage.setItem("halo_theme",l)}catch(e){}},[]);(0,s.useEffect)(()=>{d()},[d]);let u=(0,s.useCallback)(e=>{r(e),c(e),localStorage.setItem("halo_theme",e),a.F.settings.patch("global","general.theme",e).catch(()=>{})},[]);return(0,n.jsx)(l.Provider,{value:{theme:o,setTheme:u,refreshFromServer:d},children:t})}}}]);