From aa4eeba1028444ea6a9b405cb6d71373b91d32a8 Mon Sep 17 00:00:00 2001 From: Fnckerpoi <38403438+Fnckerpoi@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:33:21 +0800 Subject: [PATCH 1/2] feat: add board defaults for model, reasoning effort and preset --- README.md | 2 +- README_en.md | 2 +- lib/client.js | 14 +-- lib/host/routes.js | 6 +- lib/host/routes.js.map | 2 +- lib/host/tools.js | 9 +- lib/host/tools.js.map | 2 +- lib/shared/api.js.map | 2 +- lib/shared/protocol.js | 6 ++ lib/shared/protocol.js.map | 2 +- src/client/board/SettingsModal.tsx | 75 +++++++++++++++- src/client/board/TaskFormModal.tsx | 25 +++--- src/client/i18n/en.ts | 5 ++ src/client/i18n/zh.ts | 5 ++ src/host/routes.ts | 6 +- src/host/tools.ts | 11 +-- src/shared/api.ts | 11 ++- src/shared/protocol.ts | 12 +++ tests/default-settings.spec.ts | 140 +++++++++++++++++++++++++++++ tests/protocol.spec.ts | 45 +++++----- tests/routes.spec.ts | 30 +++++++ tests/tools.spec.ts | 28 ++++++ 22 files changed, 378 insertions(+), 62 deletions(-) create mode 100644 tests/default-settings.spec.ts diff --git a/README.md b/README.md index 5bcf00d..e6ef35c 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ agent: - **任务级 preset(0.3.3)**:新建/编辑表单「执行模式(preset)」下拉——执行会话按该 preset 组合(工具集与人设由此而来,对齐 GUI 新会话的组合方式);默认预选部署默认 preset,也可选「跟随部署默认」;preset 损坏时执行直接失败并把原因写进执行记录(不产出半组合会话);随时可改,下轮执行生效 - **Git Worktree 隔离执行(0.3.0)**:任务级开关(0.5.0 起新建任务的默认值由「看板设置」统一决定,出厂默认原目录执行),每次执行在 `<项目>/.dsh-worktrees/<任务ID>` 独立 worktree 上进行,分支 `task/<标题>+<任务ID>`(首次创建后定死,改名不改);执行会话归属项目根目录(分组、工具与文件沙箱完整可用——DSH 要求会话 cwd 即工作区根,0.3.2 修正),worktree 路径与边界纪律在开场指令中明确下达;结算自动采集提交列表 / 未提交修改警告 / 改动统计;非 git 项目或 git 不可用时自动降级原目录执行(执行记录注明降级原因,台账与执行主流程永不因 git 失败而失败);验收时详情页一键 `--no-ff` 合并到主工作区(主区脏或冲突原样报告,不自动解决)、删除 worktree(有未提交修改时拒绝)、可选删分支;支持「↻ 续跑」在现有 worktree/分支上继续执行(保留上次改动与提交) - **多仓库镜像隔离(0.6.3)**:工作区内并列多个 git 仓库时(根仓库 + 嵌套独立仓库),worktree 模式自动升级为「任务镜像」——有界扫描发现全部仓库(深度 ≤3、上限 8 个、60s 缓存;submodule 与 linked worktree 形态跳过),每仓库各自建立同名任务分支的 worktree,按相对路径挂进 `<项目>/.dsh-worktrees/<任务ID>/` 形成结构同构镜像;执行引导逐仓库给出镜像路径与分支并声明边界纪律(未镜像仓库禁改);提交证据、diff 查看(`?repo=` 按仓库)与合并(逐仓库 `--no-ff`,一仓冲突不阻断他仓,按仓库汇总)均分仓库进行;镜像清理聚合全部仓库的未提交检查后「先子后根」删除;任务与执行记录新增 `branches` / `repos` 附加字段,单仓库行为与旧数据零变化;根仓库以 gitlink(embedded repo)形式跟踪子仓的容器工作区同样完全可用——嵌套子镜像在根镜像 status 中的结构性噪音(未跟踪目录 / gitlink 漂移)已在证据采集、合并检查与镜像清理中自动豁免;新建任务表单在多仓库工作区显示「将镜像 N 个仓库」提示,纯容器工作区(根非仓库、只有并列子仓)同样可选 Worktree 隔离 -- **看板设置(0.5.0)**:顶栏「🛠 设置」——选择新建任务默认怎么执行(🌿 Worktree 隔离 / 📁 原目录执行,出厂默认后者)。保存后新建的任务都按它来;之后改设置,已建好的任务不受影响 +- **看板设置**:顶栏「🛠 设置」——统一配置新建任务的默认模型、推理强度、Agent preset、执行隔离与权限。推理强度随模型保存,切换模型时重置为跟随模型默认。任务或模板的显式选择优先;GUI 与 `taskboard_create` 创建时将默认值存入新任务,后续改设置不影响已有任务。未配置看板模型时,GUI 保留原有的上次模型选择记忆;新建表单也可主动选择跟随部署默认。配置随台账持久化并支持 JSON 导入导出。 > Worktree 隔离是协作约定而非沙箱:执行会话拥有完整工具权限,隔离依赖分支约定,不适用于运行不可信代码的场景。 - host 侧调度:关掉浏览器照常触发;错过窗口跳过不补跑 - 乐观并发(ifVersion)+ 完整归因(谁改的、哪个会话执行的) diff --git a/README_en.md b/README_en.md index fc47738..3b1acf8 100644 --- a/README_en.md +++ b/README_en.md @@ -156,7 +156,7 @@ Available in any session. Project boundary: only sessions belonging to the task' - **Per-task presets (0.3.3)**: an "execution mode (preset)" dropdown in the create/edit form — execution sessions are composed from that preset (tool sets and persona come from it, matching how the GUI composes new sessions); defaults to the deployment default preset, or pick "follow deployment default"; a broken preset fails the execution outright and records why in the execution history (no half-composed sessions); changeable anytime, effective next round - **Git worktree isolated execution (0.3.0)**: per-task toggle (since 0.5.0 the default for newly created tasks comes from Board Settings; factory default runs in place). Every execution happens on a dedicated worktree at `/.dsh-worktrees/`, branch `task/+<taskId>` (fixed after first creation; renaming doesn't rename branches). The executing session stays rooted at the project directory (grouping, tools, and the file sandbox fully available — DSH requires session cwd === workspace root, fixed in 0.3.2), and the worktree path plus boundary rules are spelled out in the opening instructions. Settlement collects commit lists / uncommitted-changes warnings / change stats automatically. Non-git projects or missing git degrade gracefully to in-place execution (the reason is recorded; the ledger and execution flow never fail because of git). At acceptance: one-click `--no-ff` merge into the main working tree (dirty tree / conflicts reported verbatim, never auto-resolved), worktree deletion (refused with uncommitted changes), optional branch deletion. "↻ Resume" continues on the existing worktree/branch (previous commits and edits kept) - **Multi-repo mirror isolation (0.6.3)**: when a workspace holds several parallel git repositories (a root repo plus nested independent ones), worktree mode upgrades into a whole-workspace task mirror — a bounded scan discovers every repo (depth ≤3, capped at 8, 60s cache; submodule / linked-worktree shapes are skipped), each repo gets its own worktree on the same task branch mounted at its relative path under `<project>/.dsh-worktrees/<taskId>/`; the session framing lists every repo's mirror path and branch and marks un-mirrored repos do-not-touch; commit evidence, diff viewing (`?repo=`) and merging (per-repo `--no-ff`, one conflict never blocking the others, per-repo summaries) all work per repo; mirror cleanup aggregates dirty checks across all repo worktrees and removes children before the root; the new `branches` / `repos` record fields are purely additive — single-repo behavior and old data are untouched; container workspaces whose root repo tracks sub-repos as gitlinks (embedded repos) are fully supported too — the structural noise nested child mirrors produce in the root mirror's status (untracked directories / gitlink drift) is exempted automatically from evidence collection, merge clean-checks, and mirror removal; the create-task form shows an "mirrors N repos" note on multi-repo workspaces, and pure-container workspaces (root not a repo, parallel sub-repos only) can pick worktree isolation too -- **Board settings (0.5.0)**: "🛠 Settings" in the toolbar — choose how new tasks execute by default (🌿 Worktree isolation / 📁 run in place; factory default is the latter). Saving applies to newly created tasks; later changes never affect existing ones +- **Board settings**: "🛠 Settings" in the toolbar configures the default model, reasoning effort, Agent preset, isolation, and permission for new tasks. Effort is saved with its model and resets to the model default when switching models. Explicit task or template choices win; both the GUI and `taskboard_create` materialize board defaults onto new tasks, so later settings changes never rewrite existing tasks. Without a board model default, the GUI retains its remembered last model; the task form can also explicitly follow deployment defaults. Settings persist with the ledger and support JSON import/export. > Worktree isolation is a collaboration convention, not a sandbox: execution sessions hold full tool permissions, isolation rests on the branch convention, and it is not suitable for running untrusted code. - Host-side scheduling: fires with the browser closed; missed windows are skipped, never replayed - Optimistic concurrency (ifVersion) + full attribution (who changed what, which session executed) diff --git a/lib/client.js b/lib/client.js index 3212887..605817a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -4,19 +4,19 @@ window.__ModuleLoader__.load({ var module = { exports: {} }; var exports = module.exports; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); -Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("react");c=s(c,1);let l=require("react/jsx-runtime"),u=require("react-dom"),d=require("react-dom/client");async function f(e,t,n){let r=e=>typeof e==`object`&&!!e&&`name`in e&&e.name===`TimeoutError`,i=e=>r(e)||n?.aborted===!0&&r(n.reason),a;try{a=await e}catch(e){throw Error(`taskboard: ${t}: ${i(e)?`request timed out`:`network request failed`}`,{cause:e})}let o;try{o=await a.text()}catch(e){throw Error(`taskboard: ${t}: ${i(e)?`response body timed out`:`response body read failed`} (HTTP ${a.status})`,{cause:e})}if(o.trim().length===0)throw Error(`taskboard: ${t}: empty response body (HTTP ${a.status})`);let s;try{s=JSON.parse(o)}catch(e){let n=(a.headers.get(`content-type`)??``).toLowerCase().includes(`json`)?`invalid JSON response`:`non-JSON response`;throw Error(`taskboard: ${t}: ${n} (HTTP ${a.status})`,{cause:e})}if(typeof s!=`object`||!s||!(`ok`in s)||typeof s.ok!=`boolean`)throw Error(`taskboard: ${t}: invalid API response (HTTP ${a.status})`);if(!s.ok){let e=s;throw!(`error`in e)||typeof e.error?.code!=`string`||typeof e.error.message!=`string`?Error(`taskboard: ${t}: invalid API error response (HTTP ${a.status})`):Error(`taskboard: ${e.error.code}: ${e.error.message}`)}if(!(`value`in s))throw Error(`taskboard: ${t}: invalid API response (HTTP ${a.status})`);if(!a.ok)throw Error(`taskboard: ${t}: HTTP ${a.status} with success body`);return s.value}const p=1e4;async function m(e){let t=AbortSignal.timeout(p);return f(fetch(e,{signal:t}),e,t)}async function h(e,t){let n=AbortSignal.timeout(p);return f(fetch(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(t),signal:n}),e,n)}async function g(e){let t=`/dsh-taskboard/assets`,n=AbortSignal.timeout(3e4);return f(fetch(t,{method:`POST`,headers:{"content-type":e.type,"x-dsh-taskboard-upload":`1`},body:e,signal:n}),t,n)}function _(){return{state:()=>m(`/dsh-taskboard/state`),workspaces:()=>m(`/dsh-taskboard/workspaces`),projectSessions:e=>m(`/dsh-taskboard/project-sessions?workspaceId=${encodeURIComponent(e)}`),create:e=>h(`/dsh-taskboard/tasks`,e),get:e=>m(`/dsh-taskboard/tasks/${encodeURIComponent(e)}`),update:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/update`,t),move:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/move`,t),archiveSessions:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/archive-sessions`,{}),reject:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/reject`,t),comment:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/comment`,{body:t}),uploadImage:g,remove:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/delete`,t),run:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/run`,t??{}),cancel:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/cancel`,{}),mergeBranch:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/merge`,{}),worktreeRemove:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/worktree-remove`,t),diagnostics:()=>m(`/dsh-taskboard/diagnostics`),clearQueue:()=>h(`/dsh-taskboard/queue/clear`,{}),worktreeCleanup:(e,t)=>h(`/dsh-taskboard/worktree-cleanup`,{workspaceId:e,taskId:t}),diff:(e,t)=>{let n=new URLSearchParams({execution:t.execution});return t.commit!==void 0&&n.set(`commit`,t.commit),t.path!==void 0&&n.set(`path`,t.path),t.repo!==void 0&&n.set(`repo`,t.repo),m(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/diff?${n.toString()}`)},importPreview:e=>h(`/dsh-taskboard/import/preview`,e),importCommit:(e,t)=>h(`/dsh-taskboard/import`,{mode:e,ledger:t}),templates:()=>m(`/dsh-taskboard/templates`),templateUpsert:e=>h(`/dsh-taskboard/templates`,e),templateDelete:e=>h(`/dsh-taskboard/templates/delete`,{id:e}),settings:()=>m(`/dsh-taskboard/settings`),updateSettings:e=>h(`/dsh-taskboard/settings/update`,e),storage:()=>m(`/dsh-taskboard/storage`),checkStorage:e=>h(`/dsh-taskboard/storage/check`,{directory:e}),migrateStorage:e=>{let t=`/dsh-taskboard/storage/migrate`,n=AbortSignal.timeout(12e4);return f(fetch(t,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({directory:e}),signal:n}),t,n)},promptCompletions:()=>m(`/dsh-taskboard/prompt-completions`),modelCatalog:()=>m(`/dsh-taskboard/model-catalog`),stream(e,t){let n=new EventSource(`/dsh-taskboard/events`),r;return n.addEventListener(`hello`,e=>{let n;try{n=JSON.parse(e.data)}catch{return}r!==void 0&&n.revision!==r&&t(),r=n.revision}),n.addEventListener(`change`,n=>{let i;try{i=JSON.parse(n.data)}catch{return}r!==void 0&&i.revision!==r+1&&t(),r=i.revision,e(i)}),n.onerror=()=>{},()=>{n.close()}}}}const v=[`backlog`,`todo`,`in_progress`,`in_review`,`done`],y=[`canceled`,`archived`];[...v,...y];const b={backlog:[`todo`,`canceled`],todo:[`in_progress`,`backlog`,`canceled`],in_progress:[`in_review`,`todo`,`canceled`],in_review:[`in_progress`,`todo`,`done`,`canceled`],done:[`archived`,`todo`],canceled:[`archived`,`todo`],archived:[]};function x(e,t){return b[e].includes(t)}function S(e){if(typeof e!=`string`)return`workspace-write`;let t=e.trim();if(t===`workspace-write`||t===`workspaceWrite`)return`workspace-write`;if(t===`read-only`||t===`readOnly`)return`read-only`;if(t===`danger-full-access`||t===`fullAccess`)return`danger-full-access`;throw Error(`permission must be 'workspace-write', 'read-only', or 'danger-full-access'`)}function ee(e){return e?.defaultIsolation??`none`}function C(e){return e?.syncExternalSessions??!1}function w(e){return e?.defaultPermission??`workspace-write`}function T(e,t=3){return e?.maxConcurrent??t}function E(e){return e?.scheduleMissedAfterMinutes??5}function D(e){return e?.queueMaxAgeMinutes??0}function O(e){return e?.dispatchIntervalMs??1e3}function te(e){let t=e.trim().split(/\s+/);if(t.length!==5)return null;let n=[[0,59],[0,23],[1,31],[1,12],[0,7]],r=[];for(let e=0;e<5;e++){let[i,a]=n[e],o=new Set;if(!k(t[e],i,a,o))return null;r.push(o)}let i=new Set;for(let e of r[4])i.add(e===7?0:e);return{minutes:r[0],hours:r[1],days:r[2],months:r[3],weekdays:i}}function k(e,t,n,r){for(let i of e.split(`,`)){let[e,a]=i.split(`/`),o=a===void 0?1:Number.parseInt(a,10);if(!Number.isInteger(o)||o<1)return!1;let s,c;if(e===void 0||e===``)return!1;if(e===`*`)s=t,c=n;else if(e.includes(`-`)){let[t,n]=e.split(`-`);if(s=Number.parseInt(t??``,10),c=Number.parseInt(n??``,10),!Number.isInteger(s)||!Number.isInteger(c))return!1}else{if(s=Number.parseInt(e,10),!Number.isInteger(s))return!1;c=a===void 0?s:n}if(s<t||c>n||s>c)return!1;for(let e=s;e<=c;e+=o)r.add(e)}return r.size>0}function A(e,t){let n=new Date(t);n.setSeconds(0,0),n.setMinutes(n.getMinutes()+1);let r=t+4*366*24*60*60*1e3,i=n.getTime();for(;i<=r;){let t=new Date(i);if(e.months.has(t.getMonth()+1)&&e.days.has(t.getDate())&&e.weekdays.has(t.getDay())&&e.hours.has(t.getHours())&&e.minutes.has(t.getMinutes()))return i;i+=6e4}return null}function j(){return{schemaVersion:1,revision:0,tasks:[]}}function M(e){let t=new Set,n=[],r=e=>{if(typeof e==`string`){let r=e.trim();r.length>0&&!t.has(r)&&(t.add(r),n.push(r))}};if(Array.isArray(e.executions))for(let t of e.executions)typeof t==`object`&&t&&r(t.sessionId);return n}function N(e){let t=e.checklist??[];return{done:t.filter(e=>e.checked).length,total:t.length}}const P={zh:{"shared.close":`关闭`,"shared.cancel":`取消`,"shared.loading":`读取中…`,"shared.blocked":`受阻`,"shared.permission":`权限`,"shared.confirmDelete":`确认删除`,"shared.current":`(当前:{name})`,"shared.entry.aria":`Agent 任务看板`,"shared.entry.label":`任务看板`,"shared.stats.title":`待办 {todo} | 进行中 {doing} | 待验收 {review}(待办|进行中|待验收)`,"shared.duplicate.suffix":`(副本)`,"status.column.backlog":`待规划`,"status.column.todo":`待办`,"status.column.in_progress":`进行中`,"status.column.in_review":`待验收`,"status.column.done":`已完成`,"status.column.canceled":`已取消`,"status.column.archived":`已归档`,"status.pill.backlog":`待规划`,"status.pill.todo":`待办`,"status.pill.in_progress":`进行中`,"status.pill.in_review":`待验收`,"status.pill.done":`完成`,"status.pill.canceled":`取消`,"status.pill.archived":`归档`,"status.move.backlog":`待规划`,"status.move.todo":`待办`,"status.move.in_progress":`进行中`,"status.move.in_review":`待验收`,"status.move.done":`完成`,"status.move.canceled":`取消`,"status.move.archived":`归档`,"urgency.urgent":`紧急`,"urgency.normal":`一般`,"urgency.relaxed":`不急`,"outcome.running":`执行中`,"outcome.succeeded":`成功`,"outcome.failed":`失败`,"outcome.cancelled":`已取消`,"board.title":`Agent 任务看板`,"board.count.tasks":`{n} 任务 · rev {rev}`,"board.action.newTask":`+ 新建任务 ▼`,"board.action.blankTask":`空白任务`,"board.action.manageTemplates":`⌗ 管理模板…`,"board.search.placeholder":`搜索标题 / ID…`,"board.filter.allProjects":`全部项目`,"board.sort.title":`列内排序`,"board.sort.default":`默认排序`,"board.sort.updated":`最近更新`,"board.sort.urgency":`按紧急度`,"board.sort.created":`创建时间`,"board.sort.byTitle":`按标题`,"board.action.backToBoard":`返回看板`,"board.action.otherTasks":`其它任务`,"board.action.settingsTitle":`看板设置:新建任务的默认执行隔离等`,"board.action.settings":`🛠 设置`,"board.action.diagTitle":`健康诊断:遗留 worktree、台账基本项`,"board.action.diag":`⚙ 诊断`,"board.action.importTitle":`从 JSON 备份文件导入台账(预览后合并或整册替换)`,"board.action.import":`⬆ 导入`,"board.action.exportTitle":`导出台账:完整 JSON 备份或任务清单 CSV`,"board.action.export":`⬇ 导出 ▼`,"board.export.jsonTitle":`完整台账备份(含执行历史与看板设置),可用于导入恢复`,"board.export.json":`完整台账(JSON)`,"board.export.csvTitle":`任务清单表格(Excel 可直接打开,中文已加 BOM)`,"board.export.csv":`任务清单(CSV)`,"board.drag.forbidden":`无法从「{from}」拖至「{to}」`,"board.empty":`无任务`,"board.secondary.empty":`无已取消 / 已归档 / 已删除任务`,"board.group.trashed":`已删除`,"board.batch.trashArchived":`批量删除`,"board.batch.trashArchivedTip":`进入选择模式,勾选后批量标记为待清除`,"board.batch.purgeTrashed":`批量清理`,"board.batch.purgeTrashedTip":`进入选择模式,勾选后物理清除,不可恢复`,"board.batch.confirm":`确认执行`,"board.batch.confirmTip":`确认对本列选中卡片执行批量操作`,"board.batch.confirmCancelTip":`取消本次批量操作`,"board.batch.cancel":`取消`,"board.batch.noneSelected":`请先勾选要操作的卡片`,"board.batch.selectAll":`全选 / 全不选`,"diag.title":`健康诊断`,"diag.subtitle":`台账基本项与 worktree 遗留清理`,"diag.revision":`台账修订号`,"diag.tasks":`任务总数`,"diag.running":`执行中`,"diag.orphans":`遗留 worktree`,"diag.orphans.heading":`遗留 worktree(台账无主但目录存在)`,"diag.orphans.none":`无遗留 — 各项目 .dsh-worktrees 目录干净`,"diag.orphans.cleanup":`清理`,"diag.orphans.hint":`提示:有未提交修改的遗留目录会被拒绝清理,请先手动处理其内容。live 任务的 worktree 请在任务详情页删除。`,"diag.gitignore.heading":`gitignore 建议`,"diag.gitignore.none":`无待办 — 各 git 项目已忽略 .dsh-worktrees 目录`,"diag.gitignore.suggestA":`建议在 .gitignore 加入一行`,"diag.gitignore.suggestB":`(不会自动修改)`,"card.drag.running":`该任务正由会话执行中({title}),不能拖动`,"card.badge.stale":`⏱ 认领超时`,"card.badge.modelTitle":`固定模型: {model}`,"card.badge.modelEffort":` · 思考强度: {effort}`,"card.badge.checklistReview":`待验收:清单未全部勾选`,"card.badge.checklist":`验收清单进度`,"card.badge.pendingPurge":`待清除`,"card.session.jumpTitle":`点击一键跳转到会话:{id}`,"card.session.missing":`该会话已被删除({id}),无法打开`,"card.session.archived":`该会话已归档({id}),已从会话列表隐藏`,"card.session.unavailable":`会话导航不可用,会话 ID:{id}`,"card.reject.placeholder":`退回原因(可选,agent 开工前会读)…`,"card.reject.confirm":`退回待办`,"card.action.doneTitle":`验收完成:移至已完成`,"card.action.done":`✓ 完成`,"card.action.rejectTitle":`退回待办,可附退回原因`,"card.action.reject":`✗ 退回`,"alert.ok":`知道了`,"diff.commit":`提交 {hash}`,"diff.file":`文件 {path}`,"diff.truncated":`⚠ 内容过长已截断`,"diff.failed":`获取失败(原因见看板顶部错误条;对象可能已随 worktree 删除丢失)`,"checklist.title":`验收清单(DoD)`,"checklist.unchecked":` · {n} 项未完成`,"checklist.allDone":` · 全部完成`,"checklist.byUser":`👤 用户`,"checklist.uncheckedItem":`未完成`,"checklist.evidence":`证据:{note}`,"report.title":`执行报告`,"report.submitted":`由执行会话提交 · {time}`,"report.changedFiles":`改动文件`,"report.checks":`自验情况`,"report.artifacts":`产物`,"report.risk":`剩余风险`,"iso.title":`执行隔离`,"iso.none":`📁 原目录执行`,"iso.worktreeTitle":`执行隔离 · Worktree`,"iso.branch":`🌿 分支`,"iso.baseline":`基线 {base} → {head}`,"iso.changed":`改动 {n} 个文件`,"iso.commit.openTitle":`点击展开该提交的 diff`,"iso.commits.more":`… 共 {n} 个提交`,"iso.nocommit":`该次执行没有产生提交(改动可能未提交,见下方警告)`,"iso.dirty.toggle":`⚠ 有 {n} 处未提交修改(合并前请让 agent 提交,或手动处理)`,"iso.dirty.expand":` ▼ 查看文件`,"iso.dirty.collapse":` ▲`,"iso.dirty.openTitle":`点击查看该文件的未提交 diff`,"iso.dirty.more":`… 共 {n} 处(完整列表见任务台账)`,"iso.hint.running":`执行中 — 结束后可合并或清理`,"iso.merge.confirm":`将分支以 --no-ff 合并到主工作区?`,"iso.merge.go":`确认合并`,"iso.merge.title":`在主工作区 git merge --no-ff 该任务分支(要求主区干净;冲突会原样报告)`,"iso.merge.button":`⇥ 合并到主工作区`,"iso.merge.failed":`合并失败:{error}`,"iso.merge.noop":`该分支没有领先主工作区的新提交,无需合并(可退回续跑或直接清理)`,"iso.merge.done":`已按仓库合并(--no-ff):{summary}`,"iso.merge.partial":`部分仓库合并失败:{summary} -{error}`,"iso.repo.root":`根仓库`,"iso.remove.wt":`🗑 删除 worktree`,"iso.remove.wtTitle":`git worktree remove(有未提交修改时拒绝)`,"iso.remove.wtb":`🗑 删 worktree + 分支`,"iso.remove.wtbTitle":`删除 worktree 并删除任务分支(有未提交修改时拒绝)`,"iso.remove.confirmWt":`删除 worktree 目录?`,"iso.remove.confirmWtb":`删除 worktree 并删除分支?`,"iso.remove.failed":`删除失败:{error}`,"iso.remove.branchFailed":`worktree 已删除,但分支删除失败:{error}`,"iso.hint.keep":`分支与 worktree 保留中 — 可退回继续修改`,"detail.session.jumpTitle":`一键跳转到对应会话:{id}`,"detail.session.jump":`🤖 跳转会话 ↗`,"detail.action.edit":`✎ 编辑`,"detail.action.duplicateTitle":`复制此任务的全部配置为一张新卡(待办列)`,"detail.action.duplicate":`⧉ 复制`,"detail.action.saveTplTitle":`把此任务的配置(含清单)保存为模板,新建任务时可用`,"detail.action.saveTplDone":`已存为模板(新建任务 ▼ 下拉可用,可在模板管理中改名)`,"detail.action.saveTpl":`⌗ 存为模板`,"detail.action.reuseTitle":`续跑:保留现有 worktree 与分支(上次的改动和提交都在原处),在其上继续执行;默认「立即执行」会重置为全新基线`,"detail.action.reuse":`↻ 续跑`,"detail.action.runTitleModel":`新会话执行({model})`,"detail.action.runTitleDefault":`新会话执行(默认模型)`,"detail.action.run":`▶ 立即执行`,"detail.action.stopConfirm":`停止该执行会话?`,"detail.action.stop":`停止`,"detail.action.stopTitle":`停止执行会话 {id}(任务回到待办)`,"detail.action.stopExec":`■ 停止执行`,"detail.chip.nextRun":`{cron} · 下次 {time}`,"detail.chip.queuedRun":`等待执行 · 原定 {time}`,"detail.chip.checklist":`清单 {done}/{total}`,"detail.chip.isolated":`Worktree 隔离`,"detail.chip.holderTitle":`点击跳转至该会话:{id}`,"detail.chip.holderStale":`认领超时 · `,"detail.chip.holderBy":`由 `,"detail.chip.holderSuffix":` 持有 ↗`,"detail.chip.trashed":`已删除待清除`,"detail.sub.line":`更新 {time} · 最近操作 {who}`,"detail.updatedBy.system":`⚙️ 系统`,"detail.updatedBy.user":`👤 用户`,"detail.field.description":`描述`,"detail.field.prompt":`执行 Prompt`,"detail.move.to":`移至→{status}`,"detail.move.confirmDoneUnchecked":`仍有 {n} 项清单未勾选,确认完成?`,"detail.move.confirmDone":`确认完成?`,"detail.move.confirmArchive":`确认归档该任务?`,"detail.move.confirmArchiveSessionWithId":`是否连同执行会话 {id} 一同归档?`,"detail.move.confirmArchiveSessionCount":`是否连同 {n} 个执行会话一同归档?`,"detail.move.archiveWithSession":`连同会话归档`,"detail.move.archiveUnsupported":`当前宿主不支持会话归档`,"detail.move.archiveResult":`卡片已归档;执行会话归档成功 {n} 个。失败会话如下(如有):`,"detail.move.archiveRetry":`归档 / 重试归档执行会话`,"detail.move.archiveCardOnly":`仅归档卡片`,"detail.move.confirm":`确认`,"detail.blocked.unmark":`✓ 解除受阻`,"detail.blocked.mark":`⛔ 标记受阻`,"detail.release.title":`释放 {id} 的认领:任务回到待办(持有会话可能仍在工作,确认它已停止后再释放)`,"detail.release.button":`🔓 释放认领`,"detail.comments.title":`评论`,"detail.comments.empty":`暂无评论 — agent 交接时会在这里汇报改动与验证结果`,"detail.comments.user":`用户`,"detail.composer.placeholder":`以用户身份留言(agent 开工前会读)…`,"detail.composer.send":`发表`,"detail.exec.title":`执行记录`,"detail.exec.prunedTitle":`更早的 {n} 条执行记录已按保留上限清理`,"detail.exec.pruned":`+{n} 已清理`,"detail.exec.trigger.manual":`手动`,"detail.exec.trigger.scheduled":`定时`,"detail.exec.openTitle":`点击打开该执行会话:{id}`,"detail.danger.delete":`🗑 删除(标记待清除)`,"detail.danger.purgeConfirm":`物理清除不可恢复`,"detail.danger.purgeGo":`确认清除`,"detail.danger.purge":`🔥 物理清除(需确认)`,"form.title.create":`新建任务`,"form.title.edit":`编辑任务`,"form.subtitle.create":`推入看板,项目内会话可认领执行`,"form.subtitle.edit":`调整任务内容与执行配置`,"form.field.title":`标题`,"form.field.titlePlaceholder":`一句话说清要做什么`,"form.field.project":`项目`,"form.field.model":`模型(默认 = 会话默认模型)`,"form.field.modelDefault":`默认模型`,"form.model.option":`{name}({provider})`,"form.field.effort":`思考强度(Reasoning Effort)`,"form.field.effortTitle":`设置模型的思考强度(如 low/medium/high);默认 = 跟随模型/提供商默认`,"form.effort.follow":`跟随模型默认`,"form.effort.low":`低 (low)`,"form.effort.medium":`中 (medium)`,"form.effort.high":`高 (high)`,"form.effort.none":`关闭思考 (none)`,"form.field.preset":`执行模式(preset)`,"form.field.presetTitle":`执行会话按该 preset 组合(决定工具集与人设);默认 = 部署默认 preset`,"form.preset.follow":`跟随部署默认`,"form.preset.defaultTag":`(部署默认)`,"form.field.urgency":`紧急度`,"form.urgency.urgent":`紧急`,"form.urgency.normal":`一般`,"form.urgency.relaxed":`不急`,"form.field.description":`描述`,"form.field.descriptionOptional":`描述(可选)`,"form.desc.placeholder":`需求细节、背景说明、验收标准…`,"form.field.prompt":`执行 Prompt(实际 Prompt = 标题+任务描述+Prompt)`,"form.field.promptOptional":`执行 Prompt(可选;实际 Prompt = 标题+任务描述+Prompt)`,"form.prompt.placeholder":`追加在「标题+任务描述」之后发给执行会话的补充指令。支持模板变量:{{lastExecution}}(上次执行结果)、{{lastComments}}(最近 3 条评论)`,"form.field.mode":`执行方式`,"form.mode.claim":`🤝 认领制`,"form.mode.claimHint":`项目内会话认领`,"form.mode.once":`⏰ 定时执行`,"form.mode.onceHint":`指定时间只跑一次`,"form.mode.periodic":`🔁 定期执行`,"form.mode.periodicHint":`按 cron 到点重复开跑`,"form.field.runAt":`触发时间`,"form.mode.scheduled":`⏰ 定时执行`,"form.mode.scheduledHint":`到点自动开跑`,"form.field.cron":`Cron 表达式`,"form.cron.placeholder":`分 时 日 月 周`,"form.cron.daily":`每天 09:00`,"form.cron.hourly":`每小时`,"form.cron.every10min":`每 10 分钟`,"form.cron.weekly":`每周一 09:00`,"form.cron.next":`下次 {time}`,"form.field.periodicCompletion":`定期任务完成后`,"form.periodicCompletion.rearm":`完成后移回待办`,"form.periodicCompletion.rearmHint":`宿主自动复位本卡,下一次 cron 继续执行`,"form.periodicCompletion.spawn":`完成后新建待办(默认)`,"form.periodicCompletion.spawnHint":`本轮保留待验收,宿主新建下一轮待办卡`,"form.field.isolation":`执行隔离`,"form.field.reuseSession":`复用会话`,"form.session.new":`新建会话`,"form.session.fresh":`每次新建会话`,"form.session.reuse":`首次新建会话,后续复用`,"form.session.unavailable":`会话列表读取失败,请重试打开表单`,"form.session.missing":`所选会话不在当前项目中,请重新选择`,"form.iso.locked":`任务已有执行记录,隔离方式已锁定`,"form.iso.lockedShort":`已锁定(执行开始后不可更改)`,"form.iso.nonGit":`当前项目非 git 仓库`,"form.iso.worktree":`🌿 Worktree 隔离`,"form.iso.worktreeTitle":`每次执行在独立 worktree 分支上进行`,"form.iso.worktreeHint":`独立分支 task/标题+ID,互不污染`,"form.iso.none":`📁 原目录执行`,"form.iso.noneTitle":`直接在项目目录执行(不使用 git)`,"form.iso.noneHintNonGit":`当前项目非 git 仓库,将在原目录执行`,"form.iso.noneHint":`不使用 git,直接在项目目录工作`,"form.iso.nonGitNote":`当前项目非 git 仓库,将在原目录执行(任务仍按默认配置创建,运行时自动降级)`,"form.iso.mirrorNote":`多仓库工作区:Worktree 隔离将自动整区镜像 {n} 个仓库——每仓库独立任务分支,验收按仓库合并`,"form.field.checklist":`验收清单(DoD)`,"form.field.checklistOptional":`验收清单(DoD,可选)`,"form.check.itemPlaceholder":`验收项 {n}(完成标准)`,"form.check.removeTitle":`删除该验收项`,"form.check.add":`+ 添加验收项`,"form.check.checkedTitle":`勾选状态随保存保留(当前勾选人:{who})`,"form.check.notCheckedYet":`未勾选`,"form.check.hintCreate":`共 {n} 项,执行会话按清单干活并逐项勾选,未完成项验收时高亮`,"form.check.hintEdit":`已勾选 {checked}/{total}(保存将整体覆盖清单,勾选状态保留)`,"form.hint.needTitle":`请填写标题`,"form.hint.needProject":`请选择项目`,"form.hint.cronBad":`Cron 表达式无效(分 时 日 月 周)`,"form.hint.runAtBad":`请选择未来的触发时间`,"form.hint.nextRun":`下次运行 {time}`,"form.hint.onceAt":`将于 {time} 触发一次`,"form.hint.saveVersion":`保存后版本 v{v} → v{next}`,"form.hint.createClaim":`创建后项目内会话可认领执行`,"form.action.runBlockedTitle":`任务正在执行中,不能重复发起`,"form.action.runBusyTitle":`正在提交…`,"form.action.runTitle":`保存后立即发起执行(新会话)`,"form.action.run":`⚡ 立即执行`,"form.action.save":`保存修改`,"form.action.create":`创建任务`,"tpl.aria":`管理模板`,"tpl.title":`任务模板`,"tpl.subtitle":`新建任务 ▼ 下拉的模板:改名 / 删除 / 直接使用;任务详情页「存为模板」可新增`,"tpl.empty":`暂无模板 — 在任务详情页点「存为模板」把常用配置沉淀下来`,"tpl.name.aria":`模板名 {name}`,"tpl.builtin":`内置`,"tpl.custom":`自建`,"tpl.meta.checklist":` · 清单 {n} 项`,"tpl.rename.title":`保存改名`,"tpl.rename.button":`改名`,"tpl.use.title":`用此模板打开新建表单`,"tpl.use.button":`用此新建`,"tpl.delete.title":`删除该模板`,"tpl.renamed":`模板已改名`,"tpl.foot.hint":`模板随台账一同保存在当前数据目录,升级不丢`,"imp.aria":`导入台账`,"imp.title":`导入台账`,"imp.subtitle":`选择导出的 JSON 备份文件:先预览、再合并或整册替换`,"imp.parseError":`文件不是合法 JSON`,"imp.note":`⬇ JSON 导出可恢复台账;图片附件需同时备份设置页所示数据目录下的 dsh-taskboard-assets 文件夹。导入文件的 schemaVersion 必须与当前版本一致。`,"imp.previewing":`预览中…`,"imp.stat.create":`新增`,"imp.stat.overwrite":`覆盖(同 id)`,"imp.stat.invalid":`无效(跳过)`,"imp.sec.create":`新增任务`,"imp.sec.overwrite":`覆盖任务(整卡替换,含执行历史与评论)`,"imp.sec.invalid":`无效条目(不会导入)`,"imp.noId":`(无 id)`,"imp.mode.merge":`⊕ 合并`,"imp.mode.mergeHint":`新增 + 按 id 覆盖,其余不动`,"imp.mode.replace":`💣 整册替换`,"imp.mode.replaceHint":`清空当前台账,以导入文件为准(先自动备份)`,"imp.result.replace":`整册替换完成:导入 {n} 张(原 {total} 张已整册备份)`,"imp.result.merge":`合并完成:新增 {n} 张、覆盖 {m} 张`,"imp.foot.replaceConfirm":`⚠ 再次点击确认执行整册替换(不可撤销,已自动备份)`,"imp.foot.replaceNeedConfirm":`整册替换需要二次确认`,"imp.foot.mergeHint":`合并只写入预览中列出的任务`,"imp.action.run":`执行导入`,"imp.action.confirmReplace":`确认整册替换`,"set.aria":`看板设置`,"set.title":`看板设置`,"set.subtitle":`新建任务、调度、会话同步与本地数据存储`,"set.iso.heading":`默认执行隔离`,"set.iso.noneHint":`不使用 git,直接在项目目录工作(出厂默认)`,"set.iso.worktreeHint":`每次执行在独立 worktree 分支上进行(task/标题+ID),互不污染;多仓库工作区自动整区镜像(每仓库独立分支)`,"set.iso.current":`当前保存的默认:{current}。仅影响之后新建的任务;已有任务保持创建时的选择,非 git 项目运行时仍自动降级原目录。`,"set.sync.heading":`自动同步工作区会话`,"set.sync.off.name":`🚫 关闭同步`,"set.sync.off.title":`仅管理在任务看板中创建和触发的任务`,"set.sync.off.hint":`仅管理看板任务(出厂默认)`,"set.sync.on.name":`🔄 自动纳入会话`,"set.sync.on.title":`各工作区直接新建的会话也会在看板展示,运行中进入「进行中」,完成后自动进入「待验收」`,"set.sync.on.hint":`跟踪运行并在完成后进入待验收`,"set.sync.stateOn":`已开启:工作区直接新建并执行的会话将自动在看板生成任务卡片,并在完成后流转至「待验收」列。`,"set.sync.stateOff":`已关闭:仅在看板内部创建与触发执行的任务会出现在看板上。`,"set.foot.dirty":`有未保存的修改`,"set.foot.clean":`与看板当前设置一致`,"set.action.save":`保存设置`,"set.storage.heading":`数据存储位置`,"set.storage.hint":`任务台账、模板和图片附件统一存放在此目录。修改路径会先完整复制并校验,再切换到新位置。`,"set.storage.loading":`正在读取当前路径…`,"set.storage.current":`当前路径:{path}`,"set.storage.assets":`图片附件:{count} 个,{size} MiB`,"set.storage.default":`恢复默认路径`,"set.storage.check":`检查路径`,"set.storage.migrate":`迁移数据`,"set.storage.migrating":`迁移中…`,"set.storage.confirm":`确认迁移全部任务台账、模板和图片附件? +Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("react");c=s(c,1);let l=require("react/jsx-runtime"),u=require("react-dom"),d=require("react-dom/client");async function f(e,t,n){let r=e=>typeof e==`object`&&!!e&&`name`in e&&e.name===`TimeoutError`,i=e=>r(e)||n?.aborted===!0&&r(n.reason),a;try{a=await e}catch(e){throw Error(`taskboard: ${t}: ${i(e)?`request timed out`:`network request failed`}`,{cause:e})}let o;try{o=await a.text()}catch(e){throw Error(`taskboard: ${t}: ${i(e)?`response body timed out`:`response body read failed`} (HTTP ${a.status})`,{cause:e})}if(o.trim().length===0)throw Error(`taskboard: ${t}: empty response body (HTTP ${a.status})`);let s;try{s=JSON.parse(o)}catch(e){let n=(a.headers.get(`content-type`)??``).toLowerCase().includes(`json`)?`invalid JSON response`:`non-JSON response`;throw Error(`taskboard: ${t}: ${n} (HTTP ${a.status})`,{cause:e})}if(typeof s!=`object`||!s||!(`ok`in s)||typeof s.ok!=`boolean`)throw Error(`taskboard: ${t}: invalid API response (HTTP ${a.status})`);if(!s.ok){let e=s;throw!(`error`in e)||typeof e.error?.code!=`string`||typeof e.error.message!=`string`?Error(`taskboard: ${t}: invalid API error response (HTTP ${a.status})`):Error(`taskboard: ${e.error.code}: ${e.error.message}`)}if(!(`value`in s))throw Error(`taskboard: ${t}: invalid API response (HTTP ${a.status})`);if(!a.ok)throw Error(`taskboard: ${t}: HTTP ${a.status} with success body`);return s.value}const p=1e4;async function m(e){let t=AbortSignal.timeout(p);return f(fetch(e,{signal:t}),e,t)}async function h(e,t){let n=AbortSignal.timeout(p);return f(fetch(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(t),signal:n}),e,n)}async function g(e){let t=`/dsh-taskboard/assets`,n=AbortSignal.timeout(3e4);return f(fetch(t,{method:`POST`,headers:{"content-type":e.type,"x-dsh-taskboard-upload":`1`},body:e,signal:n}),t,n)}function _(){return{state:()=>m(`/dsh-taskboard/state`),workspaces:()=>m(`/dsh-taskboard/workspaces`),projectSessions:e=>m(`/dsh-taskboard/project-sessions?workspaceId=${encodeURIComponent(e)}`),create:e=>h(`/dsh-taskboard/tasks`,e),get:e=>m(`/dsh-taskboard/tasks/${encodeURIComponent(e)}`),update:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/update`,t),move:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/move`,t),archiveSessions:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/archive-sessions`,{}),reject:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/reject`,t),comment:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/comment`,{body:t}),uploadImage:g,remove:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/delete`,t),run:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/run`,t??{}),cancel:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/cancel`,{}),mergeBranch:e=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/merge`,{}),worktreeRemove:(e,t)=>h(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/worktree-remove`,t),diagnostics:()=>m(`/dsh-taskboard/diagnostics`),clearQueue:()=>h(`/dsh-taskboard/queue/clear`,{}),worktreeCleanup:(e,t)=>h(`/dsh-taskboard/worktree-cleanup`,{workspaceId:e,taskId:t}),diff:(e,t)=>{let n=new URLSearchParams({execution:t.execution});return t.commit!==void 0&&n.set(`commit`,t.commit),t.path!==void 0&&n.set(`path`,t.path),t.repo!==void 0&&n.set(`repo`,t.repo),m(`/dsh-taskboard/tasks/${encodeURIComponent(e)}/diff?${n.toString()}`)},importPreview:e=>h(`/dsh-taskboard/import/preview`,e),importCommit:(e,t)=>h(`/dsh-taskboard/import`,{mode:e,ledger:t}),templates:()=>m(`/dsh-taskboard/templates`),templateUpsert:e=>h(`/dsh-taskboard/templates`,e),templateDelete:e=>h(`/dsh-taskboard/templates/delete`,{id:e}),settings:()=>m(`/dsh-taskboard/settings`),updateSettings:e=>h(`/dsh-taskboard/settings/update`,e),storage:()=>m(`/dsh-taskboard/storage`),checkStorage:e=>h(`/dsh-taskboard/storage/check`,{directory:e}),migrateStorage:e=>{let t=`/dsh-taskboard/storage/migrate`,n=AbortSignal.timeout(12e4);return f(fetch(t,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({directory:e}),signal:n}),t,n)},promptCompletions:()=>m(`/dsh-taskboard/prompt-completions`),modelCatalog:()=>m(`/dsh-taskboard/model-catalog`),stream(e,t){let n=new EventSource(`/dsh-taskboard/events`),r;return n.addEventListener(`hello`,e=>{let n;try{n=JSON.parse(e.data)}catch{return}r!==void 0&&n.revision!==r&&t(),r=n.revision}),n.addEventListener(`change`,n=>{let i;try{i=JSON.parse(n.data)}catch{return}r!==void 0&&i.revision!==r+1&&t(),r=i.revision,e(i)}),n.onerror=()=>{},()=>{n.close()}}}}const v=[`backlog`,`todo`,`in_progress`,`in_review`,`done`],y=[`canceled`,`archived`];[...v,...y];const b={backlog:[`todo`,`canceled`],todo:[`in_progress`,`backlog`,`canceled`],in_progress:[`in_review`,`todo`,`canceled`],in_review:[`in_progress`,`todo`,`done`,`canceled`],done:[`archived`,`todo`],canceled:[`archived`,`todo`],archived:[]};function x(e,t){return b[e].includes(t)}function S(e){if(typeof e!=`string`)return`workspace-write`;let t=e.trim();if(t===`workspace-write`||t===`workspaceWrite`)return`workspace-write`;if(t===`read-only`||t===`readOnly`)return`read-only`;if(t===`danger-full-access`||t===`fullAccess`)return`danger-full-access`;throw Error(`permission must be 'workspace-write', 'read-only', or 'danger-full-access'`)}function ee(e){return e?.defaultIsolation??`none`}function te(e){return e?.syncExternalSessions??!1}function C(e){return e?.defaultPermission??`workspace-write`}function w(e,t=3){return e?.maxConcurrent??t}function T(e){return e?.scheduleMissedAfterMinutes??5}function E(e){return e?.queueMaxAgeMinutes??0}function D(e){return e?.dispatchIntervalMs??1e3}function O(e){let t=e.trim().split(/\s+/);if(t.length!==5)return null;let n=[[0,59],[0,23],[1,31],[1,12],[0,7]],r=[];for(let e=0;e<5;e++){let[i,a]=n[e],o=new Set;if(!k(t[e],i,a,o))return null;r.push(o)}let i=new Set;for(let e of r[4])i.add(e===7?0:e);return{minutes:r[0],hours:r[1],days:r[2],months:r[3],weekdays:i}}function k(e,t,n,r){for(let i of e.split(`,`)){let[e,a]=i.split(`/`),o=a===void 0?1:Number.parseInt(a,10);if(!Number.isInteger(o)||o<1)return!1;let s,c;if(e===void 0||e===``)return!1;if(e===`*`)s=t,c=n;else if(e.includes(`-`)){let[t,n]=e.split(`-`);if(s=Number.parseInt(t??``,10),c=Number.parseInt(n??``,10),!Number.isInteger(s)||!Number.isInteger(c))return!1}else{if(s=Number.parseInt(e,10),!Number.isInteger(s))return!1;c=a===void 0?s:n}if(s<t||c>n||s>c)return!1;for(let e=s;e<=c;e+=o)r.add(e)}return r.size>0}function A(e,t){let n=new Date(t);n.setSeconds(0,0),n.setMinutes(n.getMinutes()+1);let r=t+4*366*24*60*60*1e3,i=n.getTime();for(;i<=r;){let t=new Date(i);if(e.months.has(t.getMonth()+1)&&e.days.has(t.getDate())&&e.weekdays.has(t.getDay())&&e.hours.has(t.getHours())&&e.minutes.has(t.getMinutes()))return i;i+=6e4}return null}function j(){return{schemaVersion:1,revision:0,tasks:[]}}function M(e){let t=new Set,n=[],r=e=>{if(typeof e==`string`){let r=e.trim();r.length>0&&!t.has(r)&&(t.add(r),n.push(r))}};if(Array.isArray(e.executions))for(let t of e.executions)typeof t==`object`&&t&&r(t.sessionId);return n}function ne(e){let t=e.checklist??[];return{done:t.filter(e=>e.checked).length,total:t.length}}const N={zh:{"shared.close":`关闭`,"shared.cancel":`取消`,"shared.loading":`读取中…`,"shared.blocked":`受阻`,"shared.permission":`权限`,"shared.confirmDelete":`确认删除`,"shared.current":`(当前:{name})`,"shared.entry.aria":`Agent 任务看板`,"shared.entry.label":`任务看板`,"shared.stats.title":`待办 {todo} | 进行中 {doing} | 待验收 {review}(待办|进行中|待验收)`,"shared.duplicate.suffix":`(副本)`,"status.column.backlog":`待规划`,"status.column.todo":`待办`,"status.column.in_progress":`进行中`,"status.column.in_review":`待验收`,"status.column.done":`已完成`,"status.column.canceled":`已取消`,"status.column.archived":`已归档`,"status.pill.backlog":`待规划`,"status.pill.todo":`待办`,"status.pill.in_progress":`进行中`,"status.pill.in_review":`待验收`,"status.pill.done":`完成`,"status.pill.canceled":`取消`,"status.pill.archived":`归档`,"status.move.backlog":`待规划`,"status.move.todo":`待办`,"status.move.in_progress":`进行中`,"status.move.in_review":`待验收`,"status.move.done":`完成`,"status.move.canceled":`取消`,"status.move.archived":`归档`,"urgency.urgent":`紧急`,"urgency.normal":`一般`,"urgency.relaxed":`不急`,"outcome.running":`执行中`,"outcome.succeeded":`成功`,"outcome.failed":`失败`,"outcome.cancelled":`已取消`,"board.title":`Agent 任务看板`,"board.count.tasks":`{n} 任务 · rev {rev}`,"board.action.newTask":`+ 新建任务 ▼`,"board.action.blankTask":`空白任务`,"board.action.manageTemplates":`⌗ 管理模板…`,"board.search.placeholder":`搜索标题 / ID…`,"board.filter.allProjects":`全部项目`,"board.sort.title":`列内排序`,"board.sort.default":`默认排序`,"board.sort.updated":`最近更新`,"board.sort.urgency":`按紧急度`,"board.sort.created":`创建时间`,"board.sort.byTitle":`按标题`,"board.action.backToBoard":`返回看板`,"board.action.otherTasks":`其它任务`,"board.action.settingsTitle":`看板设置:新建任务的默认执行隔离等`,"board.action.settings":`🛠 设置`,"board.action.diagTitle":`健康诊断:遗留 worktree、台账基本项`,"board.action.diag":`⚙ 诊断`,"board.action.importTitle":`从 JSON 备份文件导入台账(预览后合并或整册替换)`,"board.action.import":`⬆ 导入`,"board.action.exportTitle":`导出台账:完整 JSON 备份或任务清单 CSV`,"board.action.export":`⬇ 导出 ▼`,"board.export.jsonTitle":`完整台账备份(含执行历史与看板设置),可用于导入恢复`,"board.export.json":`完整台账(JSON)`,"board.export.csvTitle":`任务清单表格(Excel 可直接打开,中文已加 BOM)`,"board.export.csv":`任务清单(CSV)`,"board.drag.forbidden":`无法从「{from}」拖至「{to}」`,"board.empty":`无任务`,"board.secondary.empty":`无已取消 / 已归档 / 已删除任务`,"board.group.trashed":`已删除`,"board.batch.trashArchived":`批量删除`,"board.batch.trashArchivedTip":`进入选择模式,勾选后批量标记为待清除`,"board.batch.purgeTrashed":`批量清理`,"board.batch.purgeTrashedTip":`进入选择模式,勾选后物理清除,不可恢复`,"board.batch.confirm":`确认执行`,"board.batch.confirmTip":`确认对本列选中卡片执行批量操作`,"board.batch.confirmCancelTip":`取消本次批量操作`,"board.batch.cancel":`取消`,"board.batch.noneSelected":`请先勾选要操作的卡片`,"board.batch.selectAll":`全选 / 全不选`,"diag.title":`健康诊断`,"diag.subtitle":`台账基本项与 worktree 遗留清理`,"diag.revision":`台账修订号`,"diag.tasks":`任务总数`,"diag.running":`执行中`,"diag.orphans":`遗留 worktree`,"diag.orphans.heading":`遗留 worktree(台账无主但目录存在)`,"diag.orphans.none":`无遗留 — 各项目 .dsh-worktrees 目录干净`,"diag.orphans.cleanup":`清理`,"diag.orphans.hint":`提示:有未提交修改的遗留目录会被拒绝清理,请先手动处理其内容。live 任务的 worktree 请在任务详情页删除。`,"diag.gitignore.heading":`gitignore 建议`,"diag.gitignore.none":`无待办 — 各 git 项目已忽略 .dsh-worktrees 目录`,"diag.gitignore.suggestA":`建议在 .gitignore 加入一行`,"diag.gitignore.suggestB":`(不会自动修改)`,"card.drag.running":`该任务正由会话执行中({title}),不能拖动`,"card.badge.stale":`⏱ 认领超时`,"card.badge.modelTitle":`固定模型: {model}`,"card.badge.modelEffort":` · 思考强度: {effort}`,"card.badge.checklistReview":`待验收:清单未全部勾选`,"card.badge.checklist":`验收清单进度`,"card.badge.pendingPurge":`待清除`,"card.session.jumpTitle":`点击一键跳转到会话:{id}`,"card.session.missing":`该会话已被删除({id}),无法打开`,"card.session.archived":`该会话已归档({id}),已从会话列表隐藏`,"card.session.unavailable":`会话导航不可用,会话 ID:{id}`,"card.reject.placeholder":`退回原因(可选,agent 开工前会读)…`,"card.reject.confirm":`退回待办`,"card.action.doneTitle":`验收完成:移至已完成`,"card.action.done":`✓ 完成`,"card.action.rejectTitle":`退回待办,可附退回原因`,"card.action.reject":`✗ 退回`,"alert.ok":`知道了`,"diff.commit":`提交 {hash}`,"diff.file":`文件 {path}`,"diff.truncated":`⚠ 内容过长已截断`,"diff.failed":`获取失败(原因见看板顶部错误条;对象可能已随 worktree 删除丢失)`,"checklist.title":`验收清单(DoD)`,"checklist.unchecked":` · {n} 项未完成`,"checklist.allDone":` · 全部完成`,"checklist.byUser":`👤 用户`,"checklist.uncheckedItem":`未完成`,"checklist.evidence":`证据:{note}`,"report.title":`执行报告`,"report.submitted":`由执行会话提交 · {time}`,"report.changedFiles":`改动文件`,"report.checks":`自验情况`,"report.artifacts":`产物`,"report.risk":`剩余风险`,"iso.title":`执行隔离`,"iso.none":`📁 原目录执行`,"iso.worktreeTitle":`执行隔离 · Worktree`,"iso.branch":`🌿 分支`,"iso.baseline":`基线 {base} → {head}`,"iso.changed":`改动 {n} 个文件`,"iso.commit.openTitle":`点击展开该提交的 diff`,"iso.commits.more":`… 共 {n} 个提交`,"iso.nocommit":`该次执行没有产生提交(改动可能未提交,见下方警告)`,"iso.dirty.toggle":`⚠ 有 {n} 处未提交修改(合并前请让 agent 提交,或手动处理)`,"iso.dirty.expand":` ▼ 查看文件`,"iso.dirty.collapse":` ▲`,"iso.dirty.openTitle":`点击查看该文件的未提交 diff`,"iso.dirty.more":`… 共 {n} 处(完整列表见任务台账)`,"iso.hint.running":`执行中 — 结束后可合并或清理`,"iso.merge.confirm":`将分支以 --no-ff 合并到主工作区?`,"iso.merge.go":`确认合并`,"iso.merge.title":`在主工作区 git merge --no-ff 该任务分支(要求主区干净;冲突会原样报告)`,"iso.merge.button":`⇥ 合并到主工作区`,"iso.merge.failed":`合并失败:{error}`,"iso.merge.noop":`该分支没有领先主工作区的新提交,无需合并(可退回续跑或直接清理)`,"iso.merge.done":`已按仓库合并(--no-ff):{summary}`,"iso.merge.partial":`部分仓库合并失败:{summary} +{error}`,"iso.repo.root":`根仓库`,"iso.remove.wt":`🗑 删除 worktree`,"iso.remove.wtTitle":`git worktree remove(有未提交修改时拒绝)`,"iso.remove.wtb":`🗑 删 worktree + 分支`,"iso.remove.wtbTitle":`删除 worktree 并删除任务分支(有未提交修改时拒绝)`,"iso.remove.confirmWt":`删除 worktree 目录?`,"iso.remove.confirmWtb":`删除 worktree 并删除分支?`,"iso.remove.failed":`删除失败:{error}`,"iso.remove.branchFailed":`worktree 已删除,但分支删除失败:{error}`,"iso.hint.keep":`分支与 worktree 保留中 — 可退回继续修改`,"detail.session.jumpTitle":`一键跳转到对应会话:{id}`,"detail.session.jump":`🤖 跳转会话 ↗`,"detail.action.edit":`✎ 编辑`,"detail.action.duplicateTitle":`复制此任务的全部配置为一张新卡(待办列)`,"detail.action.duplicate":`⧉ 复制`,"detail.action.saveTplTitle":`把此任务的配置(含清单)保存为模板,新建任务时可用`,"detail.action.saveTplDone":`已存为模板(新建任务 ▼ 下拉可用,可在模板管理中改名)`,"detail.action.saveTpl":`⌗ 存为模板`,"detail.action.reuseTitle":`续跑:保留现有 worktree 与分支(上次的改动和提交都在原处),在其上继续执行;默认「立即执行」会重置为全新基线`,"detail.action.reuse":`↻ 续跑`,"detail.action.runTitleModel":`新会话执行({model})`,"detail.action.runTitleDefault":`新会话执行(默认模型)`,"detail.action.run":`▶ 立即执行`,"detail.action.stopConfirm":`停止该执行会话?`,"detail.action.stop":`停止`,"detail.action.stopTitle":`停止执行会话 {id}(任务回到待办)`,"detail.action.stopExec":`■ 停止执行`,"detail.chip.nextRun":`{cron} · 下次 {time}`,"detail.chip.queuedRun":`等待执行 · 原定 {time}`,"detail.chip.checklist":`清单 {done}/{total}`,"detail.chip.isolated":`Worktree 隔离`,"detail.chip.holderTitle":`点击跳转至该会话:{id}`,"detail.chip.holderStale":`认领超时 · `,"detail.chip.holderBy":`由 `,"detail.chip.holderSuffix":` 持有 ↗`,"detail.chip.trashed":`已删除待清除`,"detail.sub.line":`更新 {time} · 最近操作 {who}`,"detail.updatedBy.system":`⚙️ 系统`,"detail.updatedBy.user":`👤 用户`,"detail.field.description":`描述`,"detail.field.prompt":`执行 Prompt`,"detail.move.to":`移至→{status}`,"detail.move.confirmDoneUnchecked":`仍有 {n} 项清单未勾选,确认完成?`,"detail.move.confirmDone":`确认完成?`,"detail.move.confirmArchive":`确认归档该任务?`,"detail.move.confirmArchiveSessionWithId":`是否连同执行会话 {id} 一同归档?`,"detail.move.confirmArchiveSessionCount":`是否连同 {n} 个执行会话一同归档?`,"detail.move.archiveWithSession":`连同会话归档`,"detail.move.archiveUnsupported":`当前宿主不支持会话归档`,"detail.move.archiveResult":`卡片已归档;执行会话归档成功 {n} 个。失败会话如下(如有):`,"detail.move.archiveRetry":`归档 / 重试归档执行会话`,"detail.move.archiveCardOnly":`仅归档卡片`,"detail.move.confirm":`确认`,"detail.blocked.unmark":`✓ 解除受阻`,"detail.blocked.mark":`⛔ 标记受阻`,"detail.release.title":`释放 {id} 的认领:任务回到待办(持有会话可能仍在工作,确认它已停止后再释放)`,"detail.release.button":`🔓 释放认领`,"detail.comments.title":`评论`,"detail.comments.empty":`暂无评论 — agent 交接时会在这里汇报改动与验证结果`,"detail.comments.user":`用户`,"detail.composer.placeholder":`以用户身份留言(agent 开工前会读)…`,"detail.composer.send":`发表`,"detail.exec.title":`执行记录`,"detail.exec.prunedTitle":`更早的 {n} 条执行记录已按保留上限清理`,"detail.exec.pruned":`+{n} 已清理`,"detail.exec.trigger.manual":`手动`,"detail.exec.trigger.scheduled":`定时`,"detail.exec.openTitle":`点击打开该执行会话:{id}`,"detail.danger.delete":`🗑 删除(标记待清除)`,"detail.danger.purgeConfirm":`物理清除不可恢复`,"detail.danger.purgeGo":`确认清除`,"detail.danger.purge":`🔥 物理清除(需确认)`,"form.title.create":`新建任务`,"form.title.edit":`编辑任务`,"form.subtitle.create":`推入看板,项目内会话可认领执行`,"form.subtitle.edit":`调整任务内容与执行配置`,"form.field.title":`标题`,"form.field.titlePlaceholder":`一句话说清要做什么`,"form.field.project":`项目`,"form.field.model":`模型(默认 = 会话默认模型)`,"form.field.modelDefault":`默认模型`,"form.model.option":`{name}({provider})`,"form.field.effort":`思考强度(Reasoning Effort)`,"form.field.effortTitle":`设置模型的思考强度(如 low/medium/high);默认 = 跟随模型/提供商默认`,"form.effort.follow":`跟随模型默认`,"form.effort.low":`低 (low)`,"form.effort.medium":`中 (medium)`,"form.effort.high":`高 (high)`,"form.effort.none":`关闭思考 (none)`,"form.field.preset":`执行模式(preset)`,"form.field.presetTitle":`执行会话按该 preset 组合(决定工具集与人设);默认 = 部署默认 preset`,"form.preset.follow":`跟随部署默认`,"form.preset.defaultTag":`(部署默认)`,"form.field.urgency":`紧急度`,"form.urgency.urgent":`紧急`,"form.urgency.normal":`一般`,"form.urgency.relaxed":`不急`,"form.field.description":`描述`,"form.field.descriptionOptional":`描述(可选)`,"form.desc.placeholder":`需求细节、背景说明、验收标准…`,"form.field.prompt":`执行 Prompt(实际 Prompt = 标题+任务描述+Prompt)`,"form.field.promptOptional":`执行 Prompt(可选;实际 Prompt = 标题+任务描述+Prompt)`,"form.prompt.placeholder":`追加在「标题+任务描述」之后发给执行会话的补充指令。支持模板变量:{{lastExecution}}(上次执行结果)、{{lastComments}}(最近 3 条评论)`,"form.field.mode":`执行方式`,"form.mode.claim":`🤝 认领制`,"form.mode.claimHint":`项目内会话认领`,"form.mode.once":`⏰ 定时执行`,"form.mode.onceHint":`指定时间只跑一次`,"form.mode.periodic":`🔁 定期执行`,"form.mode.periodicHint":`按 cron 到点重复开跑`,"form.field.runAt":`触发时间`,"form.mode.scheduled":`⏰ 定时执行`,"form.mode.scheduledHint":`到点自动开跑`,"form.field.cron":`Cron 表达式`,"form.cron.placeholder":`分 时 日 月 周`,"form.cron.daily":`每天 09:00`,"form.cron.hourly":`每小时`,"form.cron.every10min":`每 10 分钟`,"form.cron.weekly":`每周一 09:00`,"form.cron.next":`下次 {time}`,"form.field.periodicCompletion":`定期任务完成后`,"form.periodicCompletion.rearm":`完成后移回待办`,"form.periodicCompletion.rearmHint":`宿主自动复位本卡,下一次 cron 继续执行`,"form.periodicCompletion.spawn":`完成后新建待办(默认)`,"form.periodicCompletion.spawnHint":`本轮保留待验收,宿主新建下一轮待办卡`,"form.field.isolation":`执行隔离`,"form.field.reuseSession":`复用会话`,"form.session.new":`新建会话`,"form.session.fresh":`每次新建会话`,"form.session.reuse":`首次新建会话,后续复用`,"form.session.unavailable":`会话列表读取失败,请重试打开表单`,"form.session.missing":`所选会话不在当前项目中,请重新选择`,"form.iso.locked":`任务已有执行记录,隔离方式已锁定`,"form.iso.lockedShort":`已锁定(执行开始后不可更改)`,"form.iso.nonGit":`当前项目非 git 仓库`,"form.iso.worktree":`🌿 Worktree 隔离`,"form.iso.worktreeTitle":`每次执行在独立 worktree 分支上进行`,"form.iso.worktreeHint":`独立分支 task/标题+ID,互不污染`,"form.iso.none":`📁 原目录执行`,"form.iso.noneTitle":`直接在项目目录执行(不使用 git)`,"form.iso.noneHintNonGit":`当前项目非 git 仓库,将在原目录执行`,"form.iso.noneHint":`不使用 git,直接在项目目录工作`,"form.iso.nonGitNote":`当前项目非 git 仓库,将在原目录执行(任务仍按默认配置创建,运行时自动降级)`,"form.iso.mirrorNote":`多仓库工作区:Worktree 隔离将自动整区镜像 {n} 个仓库——每仓库独立任务分支,验收按仓库合并`,"form.field.checklist":`验收清单(DoD)`,"form.field.checklistOptional":`验收清单(DoD,可选)`,"form.check.itemPlaceholder":`验收项 {n}(完成标准)`,"form.check.removeTitle":`删除该验收项`,"form.check.add":`+ 添加验收项`,"form.check.checkedTitle":`勾选状态随保存保留(当前勾选人:{who})`,"form.check.notCheckedYet":`未勾选`,"form.check.hintCreate":`共 {n} 项,执行会话按清单干活并逐项勾选,未完成项验收时高亮`,"form.check.hintEdit":`已勾选 {checked}/{total}(保存将整体覆盖清单,勾选状态保留)`,"form.hint.needTitle":`请填写标题`,"form.hint.needProject":`请选择项目`,"form.hint.cronBad":`Cron 表达式无效(分 时 日 月 周)`,"form.hint.runAtBad":`请选择未来的触发时间`,"form.hint.nextRun":`下次运行 {time}`,"form.hint.onceAt":`将于 {time} 触发一次`,"form.hint.saveVersion":`保存后版本 v{v} → v{next}`,"form.hint.createClaim":`创建后项目内会话可认领执行`,"form.action.runBlockedTitle":`任务正在执行中,不能重复发起`,"form.action.runBusyTitle":`正在提交…`,"form.action.runTitle":`保存后立即发起执行(新会话)`,"form.action.run":`⚡ 立即执行`,"form.action.save":`保存修改`,"form.action.create":`创建任务`,"tpl.aria":`管理模板`,"tpl.title":`任务模板`,"tpl.subtitle":`新建任务 ▼ 下拉的模板:改名 / 删除 / 直接使用;任务详情页「存为模板」可新增`,"tpl.empty":`暂无模板 — 在任务详情页点「存为模板」把常用配置沉淀下来`,"tpl.name.aria":`模板名 {name}`,"tpl.builtin":`内置`,"tpl.custom":`自建`,"tpl.meta.checklist":` · 清单 {n} 项`,"tpl.rename.title":`保存改名`,"tpl.rename.button":`改名`,"tpl.use.title":`用此模板打开新建表单`,"tpl.use.button":`用此新建`,"tpl.delete.title":`删除该模板`,"tpl.renamed":`模板已改名`,"tpl.foot.hint":`模板随台账一同保存在当前数据目录,升级不丢`,"imp.aria":`导入台账`,"imp.title":`导入台账`,"imp.subtitle":`选择导出的 JSON 备份文件:先预览、再合并或整册替换`,"imp.parseError":`文件不是合法 JSON`,"imp.note":`⬇ JSON 导出可恢复台账;图片附件需同时备份设置页所示数据目录下的 dsh-taskboard-assets 文件夹。导入文件的 schemaVersion 必须与当前版本一致。`,"imp.previewing":`预览中…`,"imp.stat.create":`新增`,"imp.stat.overwrite":`覆盖(同 id)`,"imp.stat.invalid":`无效(跳过)`,"imp.sec.create":`新增任务`,"imp.sec.overwrite":`覆盖任务(整卡替换,含执行历史与评论)`,"imp.sec.invalid":`无效条目(不会导入)`,"imp.noId":`(无 id)`,"imp.mode.merge":`⊕ 合并`,"imp.mode.mergeHint":`新增 + 按 id 覆盖,其余不动`,"imp.mode.replace":`💣 整册替换`,"imp.mode.replaceHint":`清空当前台账,以导入文件为准(先自动备份)`,"imp.result.replace":`整册替换完成:导入 {n} 张(原 {total} 张已整册备份)`,"imp.result.merge":`合并完成:新增 {n} 张、覆盖 {m} 张`,"imp.foot.replaceConfirm":`⚠ 再次点击确认执行整册替换(不可撤销,已自动备份)`,"imp.foot.replaceNeedConfirm":`整册替换需要二次确认`,"imp.foot.mergeHint":`合并只写入预览中列出的任务`,"imp.action.run":`执行导入`,"imp.action.confirmReplace":`确认整册替换`,"set.aria":`看板设置`,"set.title":`看板设置`,"set.subtitle":`新建任务、调度、会话同步与本地数据存储`,"set.agent.heading":`默认模型与 Agent preset`,"set.agent.hint":`仅用于新建任务:任务或模板的显式选择优先;修改设置不会改写已有任务。推理强度随所选模型保存,切换模型时重置。`,"set.agent.model":`默认模型`,"set.agent.effort":`默认推理强度`,"set.agent.preset":`默认 Agent preset`,"set.iso.heading":`默认执行隔离`,"set.iso.noneHint":`不使用 git,直接在项目目录工作(出厂默认)`,"set.iso.worktreeHint":`每次执行在独立 worktree 分支上进行(task/标题+ID),互不污染;多仓库工作区自动整区镜像(每仓库独立分支)`,"set.iso.current":`当前保存的默认:{current}。仅影响之后新建的任务;已有任务保持创建时的选择,非 git 项目运行时仍自动降级原目录。`,"set.sync.heading":`自动同步工作区会话`,"set.sync.off.name":`🚫 关闭同步`,"set.sync.off.title":`仅管理在任务看板中创建和触发的任务`,"set.sync.off.hint":`仅管理看板任务(出厂默认)`,"set.sync.on.name":`🔄 自动纳入会话`,"set.sync.on.title":`各工作区直接新建的会话也会在看板展示,运行中进入「进行中」,完成后自动进入「待验收」`,"set.sync.on.hint":`跟踪运行并在完成后进入待验收`,"set.sync.stateOn":`已开启:工作区直接新建并执行的会话将自动在看板生成任务卡片,并在完成后流转至「待验收」列。`,"set.sync.stateOff":`已关闭:仅在看板内部创建与触发执行的任务会出现在看板上。`,"set.foot.dirty":`有未保存的修改`,"set.foot.clean":`与看板当前设置一致`,"set.action.save":`保存设置`,"set.storage.heading":`数据存储位置`,"set.storage.hint":`任务台账、模板和图片附件统一存放在此目录。修改路径会先完整复制并校验,再切换到新位置。`,"set.storage.loading":`正在读取当前路径…`,"set.storage.current":`当前路径:{path}`,"set.storage.assets":`图片附件:{count} 个,{size} MiB`,"set.storage.default":`恢复默认路径`,"set.storage.check":`检查路径`,"set.storage.migrate":`迁移数据`,"set.storage.migrating":`迁移中…`,"set.storage.confirm":`确认迁移全部任务台账、模板和图片附件? 原路径:{from} 新路径:{to} 迁移期间写操作会短暂排队。`,"set.storage.migrated":`迁移成功,数据已迁移到 {path}`,"set.storage.warnings":`迁移完成,但部分旧数据未能清理:{warnings}`,"form.field.permission":`执行权限`,"form.perm.write":`可写入工作区`,"form.perm.readOnly":`仅可查看`,"form.perm.fullAccess":`完全权限`,"form.perm.defaultTag":`(默认)`,"set.perm.heading":`默认执行权限`,"set.perm.writeName":`📁 可写入工作区(出厂默认)`,"set.perm.writeHint":`可读写工作区及临时目录,写操作无需二次确认`,"set.perm.readOnlyName":`🔒 仅可查看`,"set.perm.readOnlyHint":`仅允许只读查看与检索,禁止修改文件或破坏性命令`,"set.perm.fullName":`⚡ 完全权限`,"set.perm.fullHint":`完全无限制权限,可访问全盘及执行系统外部命令`,"set.perm.current":`当前保存的默认:{current}。新建任务时预设的执行权限。`,"set.schedule.heading":`定时调度`,"set.schedule.hint":`立即生效于后续调度和执行。离线错过窗口只用于主机未运行时的历史触发;排队保质期为 0 时,已入队任务会跨重启保留。`,"set.schedule.concurrent":`最大并发执行数(1–100)`,"set.schedule.missedAfter":`离线错过超时后不补任务(分钟,1–1440)`,"board.queue.chip":`排队{n}`,"queue.panel.title":`执行队列`,"queue.depth":`排队数`,"queue.wait":`最久等待`,"queue.clear":`清空队列`,"queue.confirm":`再次确认清空`,"queue.cleared":`已清空 {n} 个排队任务。`,"set.schedule.queueMaxAge":`排队保质期(分钟,0=永不丢弃,最大 10080)`,"set.schedule.dispatchInterval":`定时会话启动间隔(毫秒,默认 1000;0=不节流,最大 60000)`,"set.schedule.invalid":`请输入范围内的整数后再保存。`,"card.badge.permReadOnly":`🔒 仅查看`,"card.badge.permReadOnlyTitle":`权限:仅可查看`,"card.badge.permFull":`⚡ 全权限`,"card.badge.permFullTitle":`权限:完全权限`,"detail.chip.permReadOnly":`仅可查看`,"detail.chip.permFull":`完全权限`,"detail.chip.permWrite":`可写入工作区`,"tpl.meta.permReadOnly":`仅查看`,"tpl.meta.permFull":`全权限`,"md.imageAlt":`图片 {n}`,"md.imageTitle":`点击查看大图 ({alt})`,"md.lightboxAlt":`大图预览`,"md.closePreview":`关闭预览`,"image.add":`插入图片`,"image.uploading":`正在上传…`,"image.hint":`支持选择、粘贴或拖入 PNG/JPEG/GIF/WebP,单张不超过 5 MB`,"image.defaultAlt":`图片`,"slash.aria":`快捷命令与技能`,"slash.title":`快捷命令与技能补全`,"slash.hint":`↑↓ 选择 · Enter / Tab 确认 · Esc 关闭`,"slash.badge.command":`⚡ 命令`,"slash.badge.skill":`🧩 技能`,"slash.tipA":`💡 输入`,"slash.tipB":`可快速补全 Slash 命令与 Agent 技能`,"slash.cmd.goal.desc":`自主完成长期目标任务,持续深度推进`,"slash.cmd.goal.hint":`<目标描述>`,"slash.cmd.schedule.desc":`设置一次性定时或周期性 Cron 调度`,"slash.cmd.schedule.hint":`<时间/表达式>`,"slash.cmd.plan.desc":`在行动前制定分步实施计划并由用户确认`,"slash.cmd.browser.desc":`启动网页浏览器交互与实时页面检索`,"slash.cmd.grill-me.desc":`通过多轮单题访谈深入对齐需求与设计意图`,"slash.cmd.teamwork-preview.desc":`多智能体协作与团队工作流预览`,"slash.cmd.learn.desc":`沉淀解决经验与新规则到知识库`,"slash.cmd.review.desc":`多维度代码审查(正确性、架构与安全)`,"slash.cmd.security.desc":`安全加固与代码漏洞扫描`,"slash.cmd.permission.desc":`切换当前会话权限级别 (read-only / workspace-write / full-access)`,"slash.cmd.permission.hint":`<preset>`,"slash.skill.frontend-ui-engineering":`构建生产级、可访问的高品质前端界面与组件`,"slash.skill.api-and-interface-design":`设计稳定契约、清晰边界的 REST / RPC 接口`,"slash.skill.test-driven-development":`测试驱动开发(TDD),编写单元与集成测试`,"slash.skill.debugging-and-error-recovery":`系统化定位 Bug 根因并恢复错误`,"slash.skill.performance-optimization":`前后端性能调优、减少渲染开销与查询优化`,"slash.skill.ci-cd-and-automation":`自动化构建、CI/CD 流水线与质量门禁`,"slash.skill.code-review-and-quality":`多轴向代码审查与重构指导`,"slash.skill.code-simplification":`精简复杂逻辑,提升可读性与可维护性`,"slash.skill.context-engineering":`优化上下文结构与提示词工程`,"slash.skill.doubt-driven-development":`以怀疑驱动的对抗式审查,确保核心逻辑正确`,"slash.skill.git-workflow-and-versioning":`Git 工作流、分支管理、语义化版本与变更日志`,"slash.skill.idea-refine":`通过发散与收敛思维细化方案与假设检验`,"slash.skill.incremental-implementation":`小步快跑、增量交付多文件变更`,"slash.skill.interview-me":`深度访谈挖掘真实意图`,"slash.skill.memory-leak-debugging":`排查诊断 JavaScript/Node.js 内存泄漏`,"slash.skill.observability-and-instrumentation":`添加日志、指标打点与链路追踪`,"slash.skill.planning-and-task-breakdown":`将复杂需求拆解为有序可执行任务`,"slash.skill.security-and-hardening":`防御安全漏洞、输入过滤与鉴权加固`,"slash.skill.shipping-and-launch":`生产发布前检查清单与回滚策略`,"slash.skill.source-driven-development":`基于权威官方文档与源码进行设计实现`,"slash.skill.spec-driven-development":`在编码前制定清晰的技术规范`,"slash.skill.using-agent-skills":`发现并动态调用智能体各项专业技能`,"sys.execFailed":`[系统] 执行失败:{error};任务已退回待办。`,"sys.endedWithComment":`[系统] 执行会话已结束并留有评论,但未移至待验收;系统自动移入待验收。`,"sys.endedNoHandoff":`[系统] 执行会话已结束,但未按协议交接(无评论、未移至待验收);系统自动移入待验收,请审查后退回或验收。`,"sys.sessionError":`[系统] 会话执行异常:{error};任务已退回待办。`,"sys.sessionDone":`[系统] 会话执行完毕,已自动进入待验收。`,"sys.cronDead":`[系统] 定时表达式 {cron} 在 4 年内没有可触发时间,已停用定时;请修正 cron 后重新开启。`,"sys.periodicHandoff":`[系统] 定期任务本轮执行完毕,定时已由新待办卡 {nextTaskId} 承接,请审查本卡后验收。`,"sys.spawnedFrom":`[系统] 定期任务上一轮执行完毕(执行 {executionId}),本卡承接定时继续下一轮;上一轮成果见 {sourceId} 的待验收。`,"sys.runAtMissed":`[系统] 定时执行错过触发时间(主机当时未运行),本次不再补跑;可手动执行或修改定时。`,"sys.mergeSingle":`[系统] 分支 {branch} 已合并到主工作区(--no-ff)。`,"sys.mergeMulti":`[系统] 分支已按仓库合并(--no-ff):{summary}`},en:{"shared.close":`Close`,"shared.cancel":`Cancel`,"shared.loading":`Loading…`,"shared.blocked":`Blocked`,"shared.permission":`Permission`,"shared.confirmDelete":`Delete`,"shared.current":` (current: {name})`,"shared.entry.aria":`Agent task board`,"shared.entry.label":`Task board`,"shared.stats.title":`To do {todo} | In progress {doing} | In review {review} (todo | in progress | in review)`,"shared.duplicate.suffix":` (copy)`,"status.column.backlog":`Backlog`,"status.column.todo":`To do`,"status.column.in_progress":`In progress`,"status.column.in_review":`In review`,"status.column.done":`Done`,"status.column.canceled":`Canceled`,"status.column.archived":`Archived`,"status.pill.backlog":`Planned`,"status.pill.todo":`To do`,"status.pill.in_progress":`In progress`,"status.pill.in_review":`In review`,"status.pill.done":`Done`,"status.pill.canceled":`Canceled`,"status.pill.archived":`Archived`,"status.move.backlog":`Backlog`,"status.move.todo":`To do`,"status.move.in_progress":`In progress`,"status.move.in_review":`In review`,"status.move.done":`Done`,"status.move.canceled":`Canceled`,"status.move.archived":`Archived`,"urgency.urgent":`Urgent`,"urgency.normal":`Normal`,"urgency.relaxed":`Relaxed`,"outcome.running":`Running`,"outcome.succeeded":`Succeeded`,"outcome.failed":`Failed`,"outcome.cancelled":`Canceled`,"board.title":`Agent Task Board`,"board.count.tasks":`{n} tasks · rev {rev}`,"board.action.newTask":`+ New Task ▼`,"board.action.blankTask":`Blank task`,"board.action.manageTemplates":`⌗ Manage templates…`,"board.search.placeholder":`Search title / ID…`,"board.filter.allProjects":`All projects`,"board.sort.title":`Sort within columns`,"board.sort.default":`Default order`,"board.sort.updated":`Recently updated`,"board.sort.urgency":`By urgency`,"board.sort.created":`Creation time`,"board.sort.byTitle":`By title`,"board.action.backToBoard":`Back to board`,"board.action.otherTasks":`Other tasks`,"board.action.settingsTitle":`Board settings: default execution isolation for new tasks, etc.`,"board.action.settings":`🛠 Settings`,"board.action.diagTitle":`Health diagnostics: orphan worktrees, ledger basics`,"board.action.diag":`⚙ Diagnostics`,"board.action.importTitle":`Import the ledger from a JSON backup (preview, then merge or replace)`,"board.action.import":`⬆ Import`,"board.action.exportTitle":`Export the ledger: full JSON backup or task-list CSV`,"board.action.export":`⬇ Export ▼`,"board.export.jsonTitle":`Full ledger backup (execution history and board settings included); import it to restore`,"board.export.json":`Full ledger (JSON)`,"board.export.csvTitle":`Task-list spreadsheet (opens directly in Excel; BOM added for CJK text)`,"board.export.csv":`Task list (CSV)`,"board.drag.forbidden":`Cannot drag from "{from}" to "{to}"`,"board.empty":`No tasks`,"board.secondary.empty":`No canceled / archived / deleted tasks`,"board.group.trashed":`Deleted`,"board.batch.trashArchived":`Batch delete`,"board.batch.trashArchivedTip":`Enter selection mode, then batch-mark picked cards for purge`,"board.batch.purgeTrashed":`Batch purge`,"board.batch.purgeTrashedTip":`Enter selection mode, then physically purge picked cards; irreversible`,"board.batch.confirm":`Confirm`,"board.batch.confirmTip":`Confirm the batch operation on the selected cards`,"board.batch.confirmCancelTip":`Cancel this batch operation`,"board.batch.cancel":`Cancel`,"board.batch.noneSelected":`Select cards first`,"board.batch.selectAll":`Select all / none`,"diag.title":`Health diagnostics`,"diag.subtitle":`Ledger basics and orphan-worktree cleanup`,"diag.revision":`Ledger revision`,"diag.tasks":`Total tasks`,"diag.running":`Running`,"diag.orphans":`Orphan worktrees`,"diag.orphans.heading":`Orphan worktrees (no owning task, directory still present)`,"diag.orphans.none":`None — every project’s .dsh-worktrees directory is clean`,"diag.orphans.cleanup":`Clean up`,"diag.orphans.hint":`Note: orphan directories with uncommitted changes are refused; handle their contents manually first. Remove a live task’s worktree from its task detail pane.`,"diag.gitignore.heading":`gitignore suggestions`,"diag.gitignore.none":`Nothing to do — every git project already ignores .dsh-worktrees`,"diag.gitignore.suggestA":`suggests adding one line to .gitignore:`,"diag.gitignore.suggestB":`(no automatic edits)`,"card.drag.running":`This task is being executed by a session ({title}) and cannot be dragged`,"card.badge.stale":`⏱ Claim stale`,"card.badge.modelTitle":`Pinned model: {model}`,"card.badge.modelEffort":` · reasoning effort: {effort}`,"card.badge.checklistReview":`In review: checklist has unchecked items`,"card.badge.checklist":`Acceptance checklist progress`,"card.badge.pendingPurge":`Pending purge`,"card.session.jumpTitle":`Click to jump to the session: {id}`,"card.session.missing":`The session has been deleted ({id}) and cannot be opened`,"card.session.archived":`The session is archived ({id}) and hidden from the session list`,"card.session.unavailable":`Session navigation unavailable; session id: {id}`,"card.reject.placeholder":`Reason for sending back (optional; the agent reads it before starting)…`,"card.reject.confirm":`Send back to todo`,"card.action.doneTitle":`Accept and complete: move to Done`,"card.action.done":`✓ Done`,"card.action.rejectTitle":`Send back to todo, optionally with a reason`,"card.action.reject":`✗ Send back`,"alert.ok":`Got it`,"diff.commit":`Commit {hash}`,"diff.file":`File {path}`,"diff.truncated":`⚠ Content truncated (too long)`,"diff.failed":`Failed to fetch (see the error bar at the top of the board; the object may be gone with a removed worktree)`,"checklist.title":`Acceptance checklist (DoD)`,"checklist.unchecked":` · {n} unchecked`,"checklist.allDone":` · all done`,"checklist.byUser":`👤 User`,"checklist.uncheckedItem":`Unchecked`,"checklist.evidence":`Evidence: {note}`,"report.title":`Execution report`,"report.submitted":`Submitted by the execution session · {time}`,"report.changedFiles":`Changed files`,"report.checks":`Self-verification`,"report.artifacts":`Artifacts`,"report.risk":`Remaining risks`,"iso.title":`Execution isolation`,"iso.none":`📁 Run in the original directory`,"iso.worktreeTitle":`Execution isolation · Worktree`,"iso.branch":`🌿 Branch`,"iso.baseline":`Baseline {base} → {head}`,"iso.changed":`{n} files changed`,"iso.commit.openTitle":`Click to expand this commit’s diff`,"iso.commits.more":`… {n} commits in total`,"iso.nocommit":`This execution produced no commits (changes may be uncommitted — see the warning below)`,"iso.dirty.toggle":`⚠ {n} uncommitted changes (have the agent commit before merging, or handle them manually)`,"iso.dirty.expand":` ▼ view files`,"iso.dirty.collapse":` ▲`,"iso.dirty.openTitle":`Click to view the uncommitted diff of this file`,"iso.dirty.more":`… {n} in total (full list in the task ledger)`,"iso.hint.running":`Running — merge or clean up after it ends`,"iso.merge.confirm":`Merge the branch into the main worktree with --no-ff?`,"iso.merge.go":`Merge`,"iso.merge.title":`git merge --no-ff the task branch into the main worktree (requires a clean main worktree; conflicts are reported as-is)`,"iso.merge.button":`⇥ Merge into main worktree`,"iso.merge.failed":`Merge failed: {error}`,"iso.merge.noop":`The branch has no commits ahead of the main worktree — nothing to merge (send it back to continue running, or clean it up)`,"iso.merge.done":`Merged per repo (--no-ff): {summary}`,"iso.merge.partial":`Some repos failed to merge: {summary} -{error}`,"iso.repo.root":`Root repo`,"iso.remove.wt":`🗑 Remove worktree`,"iso.remove.wtTitle":`git worktree remove (refused when uncommitted changes exist)`,"iso.remove.wtb":`🗑 Remove worktree + branch`,"iso.remove.wtbTitle":`Remove the worktree and delete the task branch (refused when uncommitted changes exist)`,"iso.remove.confirmWt":`Remove the worktree directory?`,"iso.remove.confirmWtb":`Remove the worktree and delete the branch?`,"iso.remove.failed":`Removal failed: {error}`,"iso.remove.branchFailed":`Worktree removed, but branch deletion failed: {error}`,"iso.hint.keep":`Branch and worktree kept — send back to continue editing`,"detail.session.jumpTitle":`Jump to the corresponding session: {id}`,"detail.session.jump":`🤖 Jump to session ↗`,"detail.action.edit":`✎ Edit`,"detail.action.duplicateTitle":`Duplicate this task’s full configuration as a new card (todo column)`,"detail.action.duplicate":`⧉ Duplicate`,"detail.action.saveTplTitle":`Save this task’s configuration (checklist included) as a template for new tasks`,"detail.action.saveTplDone":`Saved as a template (available in the New Task ▼ menu; rename it in template management)`,"detail.action.saveTpl":`⌗ Save as template`,"detail.action.reuseTitle":`Reuse-run: keep the existing worktree and branch (last run’s changes and commits stay in place) and continue on top of them; the default "Run" resets to a fresh baseline`,"detail.action.reuse":`↻ Reuse-run`,"detail.action.runTitleModel":`Run in a new session ({model})`,"detail.action.runTitleDefault":`Run in a new session (default model)`,"detail.action.run":`▶ Run`,"detail.action.stopConfirm":`Stop this execution session?`,"detail.action.stop":`Stop`,"detail.action.stopTitle":`Stop execution session {id} (the task returns to todo)`,"detail.action.stopExec":`■ Stop execution`,"detail.chip.nextRun":`{cron} · next {time}`,"detail.chip.queuedRun":`Queued · due {time}`,"detail.chip.checklist":`Checklist {done}/{total}`,"detail.chip.isolated":`Worktree isolation`,"detail.chip.holderTitle":`Click to jump to the session: {id}`,"detail.chip.holderStale":`Claim stale · `,"detail.chip.holderBy":`Held by `,"detail.chip.holderSuffix":` ↗`,"detail.chip.trashed":`Deleted, pending purge`,"detail.sub.line":`Updated {time} · last change by {who}`,"detail.updatedBy.system":`⚙️ System`,"detail.updatedBy.user":`👤 User`,"detail.field.description":`Description`,"detail.field.prompt":`Execution prompt`,"detail.move.to":`Move to → {status}`,"detail.move.confirmDoneUnchecked":`{n} checklist items are still unchecked — confirm done?`,"detail.move.confirmDone":`Confirm done?`,"detail.move.confirmArchive":`Confirm archive this task?`,"detail.move.confirmArchiveSessionWithId":`Archive execution session {id} together?`,"detail.move.confirmArchiveSessionCount":`Archive {n} execution sessions together?`,"detail.move.archiveWithSession":`With session`,"detail.move.archiveUnsupported":`Session archiving is unavailable on this host`,"detail.move.archiveResult":`Card archived; {n} execution sessions archived. Failed sessions, if any:`,"detail.move.archiveRetry":`Archive / retry execution sessions`,"detail.move.archiveCardOnly":`Card only`,"detail.move.confirm":`Confirm`,"detail.blocked.unmark":`✓ Unblock`,"detail.blocked.mark":`⛔ Mark blocked`,"detail.release.title":`Release {id}’s claim: the task returns to todo (the holding session may still be working — make sure it has stopped first)`,"detail.release.button":`🔓 Release claim`,"detail.comments.title":`Comments`,"detail.comments.empty":`No comments yet — the agent reports changes and verification here at handoff`,"detail.comments.user":`User`,"detail.composer.placeholder":`Leave a comment as the user (the agent reads it before starting)…`,"detail.composer.send":`Post`,"detail.exec.title":`Executions`,"detail.exec.prunedTitle":`{n} older execution records were pruned at the retention cap`,"detail.exec.pruned":`+{n} pruned`,"detail.exec.trigger.manual":`Manual`,"detail.exec.trigger.scheduled":`Scheduled`,"detail.exec.openTitle":`Click to open the execution session: {id}`,"detail.danger.delete":`🗑 Delete (mark for purge)`,"detail.danger.purgeConfirm":`Physical purge is irreversible`,"detail.danger.purgeGo":`Purge`,"detail.danger.purge":`🔥 Purge physically (confirm required)`,"form.title.create":`New task`,"form.title.edit":`Edit task`,"form.subtitle.create":`Push onto the board; sessions in the project can claim and run it`,"form.subtitle.edit":`Adjust the task content and execution configuration`,"form.field.title":`Title`,"form.field.titlePlaceholder":`One line: what should be done`,"form.field.project":`Project`,"form.field.model":`Model (default = session default model)`,"form.field.modelDefault":`Default model`,"form.model.option":`{name} ({provider})`,"form.field.effort":`Reasoning effort`,"form.field.effortTitle":`Set the model’s reasoning effort (e.g. low/medium/high); default = follow the model/provider default`,"form.effort.follow":`Follow model default`,"form.effort.low":`Low (low)`,"form.effort.medium":`Medium (medium)`,"form.effort.high":`High (high)`,"form.effort.none":`Off (none)`,"form.field.preset":`Execution preset`,"form.field.presetTitle":`The execution session is composed from this preset (tool set and persona); default = the deployment default preset`,"form.preset.follow":`Follow deployment default`,"form.preset.defaultTag":` (deployment default)`,"form.field.urgency":`Urgency`,"form.urgency.urgent":`Urgent`,"form.urgency.normal":`Normal`,"form.urgency.relaxed":`Relaxed`,"form.field.description":`Description`,"form.field.descriptionOptional":`Description (optional)`,"form.desc.placeholder":`Requirement details, background, acceptance criteria…`,"form.field.prompt":`Execution prompt (the actual prompt = title + description + prompt)`,"form.field.promptOptional":`Execution prompt (optional; the actual prompt = title + description + prompt)`,"form.prompt.placeholder":`Extra instructions appended after "title + task description" and sent to the execution session. Template variables: {{lastExecution}} (last execution result), {{lastComments}} (latest 3 comments)`,"form.field.mode":`Execution mode`,"form.mode.claim":`🤝 Claim-based`,"form.mode.claimHint":`Sessions in the project claim it`,"form.mode.once":`⏰ One-shot`,"form.mode.onceHint":`Runs once at a set time`,"form.mode.periodic":`🔁 Periodic`,"form.mode.periodicHint":`Repeats on a cron schedule`,"form.field.runAt":`Trigger time`,"form.mode.scheduled":`⏰ Scheduled`,"form.mode.scheduledHint":`Starts automatically on schedule`,"form.field.cron":`Cron expression`,"form.cron.placeholder":`min hour day month weekday`,"form.cron.daily":`Daily 09:00`,"form.cron.hourly":`Hourly`,"form.cron.every10min":`Every 10 minutes`,"form.cron.weekly":`Mondays 09:00`,"form.cron.next":`Next {time}`,"form.field.periodicCompletion":`After periodic completion`,"form.periodicCompletion.rearm":`Return to todo`,"form.periodicCompletion.rearmHint":`The host re-arms this card for the next cron window`,"form.periodicCompletion.spawn":`Create a new todo (default)`,"form.periodicCompletion.spawnHint":`Keeps this round in review and creates the next todo card`,"form.field.isolation":`Execution isolation`,"form.field.reuseSession":`Reuse session`,"form.session.new":`New session`,"form.session.fresh":`New session every time`,"form.session.reuse":`Create first, then reuse`,"form.session.unavailable":`Could not load sessions; reopen this form to retry`,"form.session.missing":`Selected session is unavailable in this project`,"form.iso.locked":`The task has execution history; isolation is locked`,"form.iso.lockedShort":`Locked (cannot change once execution has started)`,"form.iso.nonGit":`This project is not a git repository`,"form.iso.worktree":`🌿 Worktree isolation`,"form.iso.worktreeTitle":`Each execution runs on its own worktree branch`,"form.iso.worktreeHint":`Isolated branch task/title+ID, no cross-contamination`,"form.iso.none":`📁 Run in the project directory`,"form.iso.noneTitle":`Run directly in the project directory (no git)`,"form.iso.noneHintNonGit":`Not a git repository; will run in the project directory`,"form.iso.noneHint":`No git; works directly in the project directory`,"form.iso.nonGitNote":`This project is not a git repository; the task will run in its directory (still created with the default configuration; the runtime degrades automatically)`,"form.iso.mirrorNote":`Multi-repo workspace: worktree isolation automatically mirrors all {n} repos — one task branch each, merged per repo at acceptance`,"form.field.checklist":`Acceptance checklist (DoD)`,"form.field.checklistOptional":`Acceptance checklist (DoD, optional)`,"form.check.itemPlaceholder":`Checklist item {n} (definition of done)`,"form.check.removeTitle":`Remove this checklist item`,"form.check.add":`+ Add checklist item`,"form.check.checkedTitle":`Checked state is preserved on save (currently checked by: {who})`,"form.check.notCheckedYet":`not checked yet`,"form.check.hintCreate":`{n} items; the execution session works through them and checks each off; unfinished items are highlighted at review`,"form.check.hintEdit":`{checked}/{total} checked (saving replaces the whole list; checked state is preserved)`,"form.hint.needTitle":`Enter a title`,"form.hint.needProject":`Select a project`,"form.hint.cronBad":`Invalid cron expression (min hour day month weekday)`,"form.hint.runAtBad":`Pick a future trigger time`,"form.hint.nextRun":`Next run {time}`,"form.hint.onceAt":`Fires once at {time}`,"form.hint.saveVersion":`On save: v{v} → v{next}`,"form.hint.createClaim":`After creation, sessions in the project can claim and run it`,"form.action.runBlockedTitle":`The task is running; cannot start another`,"form.action.runBusyTitle":`Submitting…`,"form.action.runTitle":`Save, then immediately start an execution (new session)`,"form.action.run":`⚡ Run now`,"form.action.save":`Save changes`,"form.action.create":`Create task`,"tpl.aria":`Manage templates`,"tpl.title":`Task templates`,"tpl.subtitle":`Templates from the New Task ▼ menu: rename / delete / use directly; "Save as template" in the task detail pane adds new ones`,"tpl.empty":`No templates yet — click "Save as template" in a task detail pane to capture a common configuration`,"tpl.name.aria":`Template name {name}`,"tpl.builtin":`Built-in`,"tpl.custom":`Custom`,"tpl.meta.checklist":` · {n} checklist items`,"tpl.rename.title":`Save rename`,"tpl.rename.button":`Rename`,"tpl.use.title":`Open the new-task form with this template`,"tpl.use.button":`Use`,"tpl.delete.title":`Delete this template`,"tpl.renamed":`Template renamed`,"tpl.foot.hint":`Templates are stored with the ledger in the active data directory and survive upgrades`,"imp.aria":`Import ledger`,"imp.title":`Import ledger`,"imp.subtitle":`Pick an exported JSON backup: preview first, then merge or replace everything`,"imp.parseError":`The file is not valid JSON`,"imp.note":`The ⬇ JSON export restores the ledger. Back up the dsh-taskboard-assets folder in the data directory shown in Settings as well. The file’s schemaVersion must match the current version.`,"imp.previewing":`Previewing…`,"imp.stat.create":`New`,"imp.stat.overwrite":`Overwrite (same id)`,"imp.stat.invalid":`Invalid (skipped)`,"imp.sec.create":`New tasks`,"imp.sec.overwrite":`Overwritten tasks (whole-card replacement, execution history and comments included)`,"imp.sec.invalid":`Invalid entries (not imported)`,"imp.noId":`(no id)`,"imp.mode.merge":`⊕ Merge`,"imp.mode.mergeHint":`Adds new + overwrites by id; everything else untouched`,"imp.mode.replace":`💣 Replace all`,"imp.mode.replaceHint":`Clears the current ledger and adopts the imported file (automatic backup first)`,"imp.result.replace":`Replace complete: imported {n} tasks ({total} former tasks backed up)`,"imp.result.merge":`Merge complete: {n} added, {m} overwritten`,"imp.foot.replaceConfirm":`⚠ Click again to confirm the full replacement (irreversible; a backup has been taken)`,"imp.foot.replaceNeedConfirm":`Full replacement requires a second confirmation`,"imp.foot.mergeHint":`Merge writes only the tasks listed in the preview`,"imp.action.run":`Import`,"imp.action.confirmReplace":`Confirm replace`,"set.aria":`Board settings`,"set.title":`Board settings`,"set.subtitle":`New-task defaults, scheduling, session sync, and local data storage`,"set.iso.heading":`Default execution isolation`,"set.iso.noneHint":`No git; works directly in the project directory (factory default)`,"set.iso.worktreeHint":`Each execution runs on its own worktree branch (task/title+ID), isolated from the others; multi-repo workspaces are mirrored whole (one branch per repo)`,"set.iso.current":`Currently saved default: {current}. Affects only tasks created hereafter; existing tasks keep their creation-time choice, and non-git projects still degrade to the project directory at run time.`,"set.sync.heading":`Auto-sync workspace sessions`,"set.sync.off.name":`🚫 Sync off`,"set.sync.off.title":`Manage only tasks created and triggered in the task board`,"set.sync.off.hint":`Board tasks only (factory default)`,"set.sync.on.name":`🔄 Auto-include sessions`,"set.sync.on.title":`Sessions created directly in workspaces also appear on the board: In progress while running, In review automatically when done`,"set.sync.on.hint":`Tracks runs and enters review on completion`,"set.sync.stateOn":`On: sessions created and run directly in workspaces automatically become board cards and flow to the "In review" column when done.`,"set.sync.stateOff":`Off: only tasks created and triggered inside the board appear on the board.`,"set.foot.dirty":`Unsaved changes`,"set.foot.clean":`Matches the current board settings`,"set.action.save":`Save settings`,"set.storage.heading":`Data storage location`,"set.storage.hint":`The task ledger, templates, and image attachments live together in this directory. Changing it copies and verifies everything before switching.`,"set.storage.loading":`Loading the current path…`,"set.storage.current":`Current path: {path}`,"set.storage.assets":`Image attachments: {count}, {size} MiB`,"set.storage.default":`Restore default path`,"set.storage.check":`Check path`,"set.storage.migrate":`Migrate data`,"set.storage.migrating":`Migrating…`,"set.storage.confirm":`Migrate the complete task ledger, templates, and image attachments? +{error}`,"iso.repo.root":`Root repo`,"iso.remove.wt":`🗑 Remove worktree`,"iso.remove.wtTitle":`git worktree remove (refused when uncommitted changes exist)`,"iso.remove.wtb":`🗑 Remove worktree + branch`,"iso.remove.wtbTitle":`Remove the worktree and delete the task branch (refused when uncommitted changes exist)`,"iso.remove.confirmWt":`Remove the worktree directory?`,"iso.remove.confirmWtb":`Remove the worktree and delete the branch?`,"iso.remove.failed":`Removal failed: {error}`,"iso.remove.branchFailed":`Worktree removed, but branch deletion failed: {error}`,"iso.hint.keep":`Branch and worktree kept — send back to continue editing`,"detail.session.jumpTitle":`Jump to the corresponding session: {id}`,"detail.session.jump":`🤖 Jump to session ↗`,"detail.action.edit":`✎ Edit`,"detail.action.duplicateTitle":`Duplicate this task’s full configuration as a new card (todo column)`,"detail.action.duplicate":`⧉ Duplicate`,"detail.action.saveTplTitle":`Save this task’s configuration (checklist included) as a template for new tasks`,"detail.action.saveTplDone":`Saved as a template (available in the New Task ▼ menu; rename it in template management)`,"detail.action.saveTpl":`⌗ Save as template`,"detail.action.reuseTitle":`Reuse-run: keep the existing worktree and branch (last run’s changes and commits stay in place) and continue on top of them; the default "Run" resets to a fresh baseline`,"detail.action.reuse":`↻ Reuse-run`,"detail.action.runTitleModel":`Run in a new session ({model})`,"detail.action.runTitleDefault":`Run in a new session (default model)`,"detail.action.run":`▶ Run`,"detail.action.stopConfirm":`Stop this execution session?`,"detail.action.stop":`Stop`,"detail.action.stopTitle":`Stop execution session {id} (the task returns to todo)`,"detail.action.stopExec":`■ Stop execution`,"detail.chip.nextRun":`{cron} · next {time}`,"detail.chip.queuedRun":`Queued · due {time}`,"detail.chip.checklist":`Checklist {done}/{total}`,"detail.chip.isolated":`Worktree isolation`,"detail.chip.holderTitle":`Click to jump to the session: {id}`,"detail.chip.holderStale":`Claim stale · `,"detail.chip.holderBy":`Held by `,"detail.chip.holderSuffix":` ↗`,"detail.chip.trashed":`Deleted, pending purge`,"detail.sub.line":`Updated {time} · last change by {who}`,"detail.updatedBy.system":`⚙️ System`,"detail.updatedBy.user":`👤 User`,"detail.field.description":`Description`,"detail.field.prompt":`Execution prompt`,"detail.move.to":`Move to → {status}`,"detail.move.confirmDoneUnchecked":`{n} checklist items are still unchecked — confirm done?`,"detail.move.confirmDone":`Confirm done?`,"detail.move.confirmArchive":`Confirm archive this task?`,"detail.move.confirmArchiveSessionWithId":`Archive execution session {id} together?`,"detail.move.confirmArchiveSessionCount":`Archive {n} execution sessions together?`,"detail.move.archiveWithSession":`With session`,"detail.move.archiveUnsupported":`Session archiving is unavailable on this host`,"detail.move.archiveResult":`Card archived; {n} execution sessions archived. Failed sessions, if any:`,"detail.move.archiveRetry":`Archive / retry execution sessions`,"detail.move.archiveCardOnly":`Card only`,"detail.move.confirm":`Confirm`,"detail.blocked.unmark":`✓ Unblock`,"detail.blocked.mark":`⛔ Mark blocked`,"detail.release.title":`Release {id}’s claim: the task returns to todo (the holding session may still be working — make sure it has stopped first)`,"detail.release.button":`🔓 Release claim`,"detail.comments.title":`Comments`,"detail.comments.empty":`No comments yet — the agent reports changes and verification here at handoff`,"detail.comments.user":`User`,"detail.composer.placeholder":`Leave a comment as the user (the agent reads it before starting)…`,"detail.composer.send":`Post`,"detail.exec.title":`Executions`,"detail.exec.prunedTitle":`{n} older execution records were pruned at the retention cap`,"detail.exec.pruned":`+{n} pruned`,"detail.exec.trigger.manual":`Manual`,"detail.exec.trigger.scheduled":`Scheduled`,"detail.exec.openTitle":`Click to open the execution session: {id}`,"detail.danger.delete":`🗑 Delete (mark for purge)`,"detail.danger.purgeConfirm":`Physical purge is irreversible`,"detail.danger.purgeGo":`Purge`,"detail.danger.purge":`🔥 Purge physically (confirm required)`,"form.title.create":`New task`,"form.title.edit":`Edit task`,"form.subtitle.create":`Push onto the board; sessions in the project can claim and run it`,"form.subtitle.edit":`Adjust the task content and execution configuration`,"form.field.title":`Title`,"form.field.titlePlaceholder":`One line: what should be done`,"form.field.project":`Project`,"form.field.model":`Model (default = session default model)`,"form.field.modelDefault":`Default model`,"form.model.option":`{name} ({provider})`,"form.field.effort":`Reasoning effort`,"form.field.effortTitle":`Set the model’s reasoning effort (e.g. low/medium/high); default = follow the model/provider default`,"form.effort.follow":`Follow model default`,"form.effort.low":`Low (low)`,"form.effort.medium":`Medium (medium)`,"form.effort.high":`High (high)`,"form.effort.none":`Off (none)`,"form.field.preset":`Execution preset`,"form.field.presetTitle":`The execution session is composed from this preset (tool set and persona); default = the deployment default preset`,"form.preset.follow":`Follow deployment default`,"form.preset.defaultTag":` (deployment default)`,"form.field.urgency":`Urgency`,"form.urgency.urgent":`Urgent`,"form.urgency.normal":`Normal`,"form.urgency.relaxed":`Relaxed`,"form.field.description":`Description`,"form.field.descriptionOptional":`Description (optional)`,"form.desc.placeholder":`Requirement details, background, acceptance criteria…`,"form.field.prompt":`Execution prompt (the actual prompt = title + description + prompt)`,"form.field.promptOptional":`Execution prompt (optional; the actual prompt = title + description + prompt)`,"form.prompt.placeholder":`Extra instructions appended after "title + task description" and sent to the execution session. Template variables: {{lastExecution}} (last execution result), {{lastComments}} (latest 3 comments)`,"form.field.mode":`Execution mode`,"form.mode.claim":`🤝 Claim-based`,"form.mode.claimHint":`Sessions in the project claim it`,"form.mode.once":`⏰ One-shot`,"form.mode.onceHint":`Runs once at a set time`,"form.mode.periodic":`🔁 Periodic`,"form.mode.periodicHint":`Repeats on a cron schedule`,"form.field.runAt":`Trigger time`,"form.mode.scheduled":`⏰ Scheduled`,"form.mode.scheduledHint":`Starts automatically on schedule`,"form.field.cron":`Cron expression`,"form.cron.placeholder":`min hour day month weekday`,"form.cron.daily":`Daily 09:00`,"form.cron.hourly":`Hourly`,"form.cron.every10min":`Every 10 minutes`,"form.cron.weekly":`Mondays 09:00`,"form.cron.next":`Next {time}`,"form.field.periodicCompletion":`After periodic completion`,"form.periodicCompletion.rearm":`Return to todo`,"form.periodicCompletion.rearmHint":`The host re-arms this card for the next cron window`,"form.periodicCompletion.spawn":`Create a new todo (default)`,"form.periodicCompletion.spawnHint":`Keeps this round in review and creates the next todo card`,"form.field.isolation":`Execution isolation`,"form.field.reuseSession":`Reuse session`,"form.session.new":`New session`,"form.session.fresh":`New session every time`,"form.session.reuse":`Create first, then reuse`,"form.session.unavailable":`Could not load sessions; reopen this form to retry`,"form.session.missing":`Selected session is unavailable in this project`,"form.iso.locked":`The task has execution history; isolation is locked`,"form.iso.lockedShort":`Locked (cannot change once execution has started)`,"form.iso.nonGit":`This project is not a git repository`,"form.iso.worktree":`🌿 Worktree isolation`,"form.iso.worktreeTitle":`Each execution runs on its own worktree branch`,"form.iso.worktreeHint":`Isolated branch task/title+ID, no cross-contamination`,"form.iso.none":`📁 Run in the project directory`,"form.iso.noneTitle":`Run directly in the project directory (no git)`,"form.iso.noneHintNonGit":`Not a git repository; will run in the project directory`,"form.iso.noneHint":`No git; works directly in the project directory`,"form.iso.nonGitNote":`This project is not a git repository; the task will run in its directory (still created with the default configuration; the runtime degrades automatically)`,"form.iso.mirrorNote":`Multi-repo workspace: worktree isolation automatically mirrors all {n} repos — one task branch each, merged per repo at acceptance`,"form.field.checklist":`Acceptance checklist (DoD)`,"form.field.checklistOptional":`Acceptance checklist (DoD, optional)`,"form.check.itemPlaceholder":`Checklist item {n} (definition of done)`,"form.check.removeTitle":`Remove this checklist item`,"form.check.add":`+ Add checklist item`,"form.check.checkedTitle":`Checked state is preserved on save (currently checked by: {who})`,"form.check.notCheckedYet":`not checked yet`,"form.check.hintCreate":`{n} items; the execution session works through them and checks each off; unfinished items are highlighted at review`,"form.check.hintEdit":`{checked}/{total} checked (saving replaces the whole list; checked state is preserved)`,"form.hint.needTitle":`Enter a title`,"form.hint.needProject":`Select a project`,"form.hint.cronBad":`Invalid cron expression (min hour day month weekday)`,"form.hint.runAtBad":`Pick a future trigger time`,"form.hint.nextRun":`Next run {time}`,"form.hint.onceAt":`Fires once at {time}`,"form.hint.saveVersion":`On save: v{v} → v{next}`,"form.hint.createClaim":`After creation, sessions in the project can claim and run it`,"form.action.runBlockedTitle":`The task is running; cannot start another`,"form.action.runBusyTitle":`Submitting…`,"form.action.runTitle":`Save, then immediately start an execution (new session)`,"form.action.run":`⚡ Run now`,"form.action.save":`Save changes`,"form.action.create":`Create task`,"tpl.aria":`Manage templates`,"tpl.title":`Task templates`,"tpl.subtitle":`Templates from the New Task ▼ menu: rename / delete / use directly; "Save as template" in the task detail pane adds new ones`,"tpl.empty":`No templates yet — click "Save as template" in a task detail pane to capture a common configuration`,"tpl.name.aria":`Template name {name}`,"tpl.builtin":`Built-in`,"tpl.custom":`Custom`,"tpl.meta.checklist":` · {n} checklist items`,"tpl.rename.title":`Save rename`,"tpl.rename.button":`Rename`,"tpl.use.title":`Open the new-task form with this template`,"tpl.use.button":`Use`,"tpl.delete.title":`Delete this template`,"tpl.renamed":`Template renamed`,"tpl.foot.hint":`Templates are stored with the ledger in the active data directory and survive upgrades`,"imp.aria":`Import ledger`,"imp.title":`Import ledger`,"imp.subtitle":`Pick an exported JSON backup: preview first, then merge or replace everything`,"imp.parseError":`The file is not valid JSON`,"imp.note":`The ⬇ JSON export restores the ledger. Back up the dsh-taskboard-assets folder in the data directory shown in Settings as well. The file’s schemaVersion must match the current version.`,"imp.previewing":`Previewing…`,"imp.stat.create":`New`,"imp.stat.overwrite":`Overwrite (same id)`,"imp.stat.invalid":`Invalid (skipped)`,"imp.sec.create":`New tasks`,"imp.sec.overwrite":`Overwritten tasks (whole-card replacement, execution history and comments included)`,"imp.sec.invalid":`Invalid entries (not imported)`,"imp.noId":`(no id)`,"imp.mode.merge":`⊕ Merge`,"imp.mode.mergeHint":`Adds new + overwrites by id; everything else untouched`,"imp.mode.replace":`💣 Replace all`,"imp.mode.replaceHint":`Clears the current ledger and adopts the imported file (automatic backup first)`,"imp.result.replace":`Replace complete: imported {n} tasks ({total} former tasks backed up)`,"imp.result.merge":`Merge complete: {n} added, {m} overwritten`,"imp.foot.replaceConfirm":`⚠ Click again to confirm the full replacement (irreversible; a backup has been taken)`,"imp.foot.replaceNeedConfirm":`Full replacement requires a second confirmation`,"imp.foot.mergeHint":`Merge writes only the tasks listed in the preview`,"imp.action.run":`Import`,"imp.action.confirmReplace":`Confirm replace`,"set.aria":`Board settings`,"set.title":`Board settings`,"set.subtitle":`New-task defaults, scheduling, session sync, and local data storage`,"set.agent.heading":`Default model and Agent preset`,"set.agent.hint":`New tasks only: explicit task or template choices take priority. Existing tasks are unchanged. Reasoning effort is saved with the model and resets when switching models.`,"set.agent.model":`Default model`,"set.agent.effort":`Default reasoning effort`,"set.agent.preset":`Default Agent preset`,"set.iso.heading":`Default execution isolation`,"set.iso.noneHint":`No git; works directly in the project directory (factory default)`,"set.iso.worktreeHint":`Each execution runs on its own worktree branch (task/title+ID), isolated from the others; multi-repo workspaces are mirrored whole (one branch per repo)`,"set.iso.current":`Currently saved default: {current}. Affects only tasks created hereafter; existing tasks keep their creation-time choice, and non-git projects still degrade to the project directory at run time.`,"set.sync.heading":`Auto-sync workspace sessions`,"set.sync.off.name":`🚫 Sync off`,"set.sync.off.title":`Manage only tasks created and triggered in the task board`,"set.sync.off.hint":`Board tasks only (factory default)`,"set.sync.on.name":`🔄 Auto-include sessions`,"set.sync.on.title":`Sessions created directly in workspaces also appear on the board: In progress while running, In review automatically when done`,"set.sync.on.hint":`Tracks runs and enters review on completion`,"set.sync.stateOn":`On: sessions created and run directly in workspaces automatically become board cards and flow to the "In review" column when done.`,"set.sync.stateOff":`Off: only tasks created and triggered inside the board appear on the board.`,"set.foot.dirty":`Unsaved changes`,"set.foot.clean":`Matches the current board settings`,"set.action.save":`Save settings`,"set.storage.heading":`Data storage location`,"set.storage.hint":`The task ledger, templates, and image attachments live together in this directory. Changing it copies and verifies everything before switching.`,"set.storage.loading":`Loading the current path…`,"set.storage.current":`Current path: {path}`,"set.storage.assets":`Image attachments: {count}, {size} MiB`,"set.storage.default":`Restore default path`,"set.storage.check":`Check path`,"set.storage.migrate":`Migrate data`,"set.storage.migrating":`Migrating…`,"set.storage.confirm":`Migrate the complete task ledger, templates, and image attachments? From: {from} To: {to} -Writes queue briefly during migration.`,"set.storage.migrated":`Migration succeeded — data now lives in {path}`,"set.storage.warnings":`Migration finished, but some old files could not be removed: {warnings}`,"form.field.permission":`Execution permission`,"form.perm.write":`Workspace write`,"form.perm.readOnly":`Read-only`,"form.perm.fullAccess":`Full access`,"form.perm.defaultTag":` (default)`,"set.perm.heading":`Default execution permission`,"set.perm.writeName":`📁 Workspace write (factory default)`,"set.perm.writeHint":`Read-write access to the workspace and temp directories; writes need no extra confirmation`,"set.perm.readOnlyName":`🔒 Read-only`,"set.perm.readOnlyHint":`Read-only viewing and search only; no file changes or destructive commands`,"set.perm.fullName":`⚡ Full access`,"set.perm.fullHint":`Unrestricted: whole-disk access and external system commands`,"set.perm.current":`Currently saved default: {current}. The execution permission preset applied to new tasks.`,"set.schedule.heading":`Scheduled execution`,"set.schedule.hint":`Takes effect for subsequent scheduler and execution checks. The offline window only classifies historical triggers while the host was down; a queue shelf life of 0 retains queued work across restarts.`,"set.schedule.concurrent":`Maximum concurrent executions (1–100)`,"set.schedule.missedAfter":`Offline missed window, minutes (1–1440)`,"board.queue.chip":`Queued {n}`,"queue.panel.title":`Execution queue`,"queue.depth":`Queued`,"queue.wait":`Longest wait`,"queue.clear":`Clear queue`,"queue.confirm":`Confirm clear`,"queue.cleared":`Cleared {n} queued task(s).`,"set.schedule.queueMaxAge":`Queue shelf life, minutes (0 = retain indefinitely; max 10080)`,"set.schedule.dispatchInterval":`Scheduled-session start interval, ms (default 1000; 0 = no throttle; max 60000)`,"set.schedule.invalid":`Enter whole numbers within the allowed ranges before saving.`,"card.badge.permReadOnly":`🔒 Read-only`,"card.badge.permReadOnlyTitle":`Permission: read-only`,"card.badge.permFull":`⚡ Full access`,"card.badge.permFullTitle":`Permission: full access`,"detail.chip.permReadOnly":`Read-only`,"detail.chip.permFull":`Full access`,"detail.chip.permWrite":`Workspace write`,"tpl.meta.permReadOnly":`read-only`,"tpl.meta.permFull":`full access`,"md.imageAlt":`Image {n}`,"md.imageTitle":`Click to view full size ({alt})`,"md.lightboxAlt":`Full-size preview`,"md.closePreview":`Close preview`,"image.add":`Insert image`,"image.uploading":`Uploading…`,"image.hint":`Choose, paste, or drop PNG/JPEG/GIF/WebP images up to 5 MB each`,"image.defaultAlt":`Image`,"slash.aria":`Quick commands and skills`,"slash.title":`Quick command and skill completion`,"slash.hint":`↑↓ navigate · Enter / Tab pick · Esc close`,"slash.badge.command":`⚡ command`,"slash.badge.skill":`🧩 skill`,"slash.tipA":`💡 Type`,"slash.tipB":`to autocomplete slash commands and agent skills`,"slash.cmd.goal.desc":`Autonomously drive long-horizon goals to completion`,"slash.cmd.goal.hint":`<goal description>`,"slash.cmd.schedule.desc":`Set one-off or recurring cron schedules`,"slash.cmd.schedule.hint":`<time or expression>`,"slash.cmd.plan.desc":`Draft a step-by-step plan and get user confirmation before acting`,"slash.cmd.browser.desc":`Launch a browser for page interaction and live search`,"slash.cmd.grill-me.desc":`Align requirements and design intent through one-question-at-a-time interviews`,"slash.cmd.teamwork-preview.desc":`Multi-agent collaboration and team workflow preview`,"slash.cmd.learn.desc":`Capture solutions and new rules into the knowledge base`,"slash.cmd.review.desc":`Multi-axis code review (correctness, architecture, security)`,"slash.cmd.security.desc":`Security hardening and vulnerability scanning`,"slash.cmd.permission.desc":`Switch the session permission level (read-only / workspace-write / full-access)`,"slash.cmd.permission.hint":`<preset>`,"slash.skill.frontend-ui-engineering":`Build production-grade, accessible frontend interfaces and components`,"slash.skill.api-and-interface-design":`Design stable, well-bounded REST / RPC interfaces`,"slash.skill.test-driven-development":`Test-driven development (TDD): unit and integration tests`,"slash.skill.debugging-and-error-recovery":`Systematically locate bug root causes and recover`,"slash.skill.performance-optimization":`Frontend/backend performance tuning and query optimization`,"slash.skill.ci-cd-and-automation":`Build automation, CI/CD pipelines and quality gates`,"slash.skill.code-review-and-quality":`Multi-axis code review and refactoring guidance`,"slash.skill.code-simplification":`Simplify complex logic for readability and maintainability`,"slash.skill.context-engineering":`Optimize context structure and prompt engineering`,"slash.skill.doubt-driven-development":`Adversarial, doubt-driven review of core logic`,"slash.skill.git-workflow-and-versioning":`Git workflow, branching, semantic versioning and changelogs`,"slash.skill.idea-refine":`Refine ideas and test hypotheses via divergent-convergent thinking`,"slash.skill.incremental-implementation":`Deliver multi-file changes in small, incremental steps`,"slash.skill.interview-me":`Deep interviews to surface true intent`,"slash.skill.memory-leak-debugging":`Diagnose JavaScript/Node.js memory leaks`,"slash.skill.observability-and-instrumentation":`Add logging, metrics and distributed tracing`,"slash.skill.planning-and-task-breakdown":`Break complex requirements into ordered, executable tasks`,"slash.skill.security-and-hardening":`Harden against vulnerabilities; input filtering and auth hardening`,"slash.skill.shipping-and-launch":`Pre-launch checklists and rollback strategies`,"slash.skill.source-driven-development":`Design from authoritative docs and source code`,"slash.skill.spec-driven-development":`Write clear technical specs before coding`,"slash.skill.using-agent-skills":`Discover and invoke agent skills dynamically`,"sys.execFailed":`[System] Execution failed: {error}; the task was returned to todo.`,"sys.endedWithComment":`[System] The execution session ended with comments but was not moved to in review; the system moved it to in review automatically.`,"sys.endedNoHandoff":`[System] The execution session ended without a protocol handoff (no comments, not moved to in review); the system moved it to in review automatically — review it, then send back or accept.`,"sys.sessionError":`[System] Session execution error: {error}; the task was returned to todo.`,"sys.sessionDone":`[System] The session finished; automatically moved to in review.`,"sys.cronDead":`[System] The cron expression {cron} has no trigger time within 4 years; scheduling disabled — fix the cron and re-enable.`,"sys.periodicHandoff":`[System] Periodic run finished; the schedule now continues on the new todo card {nextTaskId}. Review this card, then accept.`,"sys.spawnedFrom":`[System] The previous periodic run finished (execution {executionId}); this card carries the schedule onward. See {sourceId} for the finished round.`,"sys.runAtMissed":`[System] The one-shot trigger time was missed (host was down); it will not re-fire. Run manually or reschedule.`,"sys.mergeSingle":`[System] Branch {branch} merged into the main worktree (--no-ff).`,"sys.mergeMulti":`[System] Branches merged per repo (--no-ff): {summary}`}};let F,I,L={active:ne(),revision:0};const R=new Set;let z,B;function V(){B!==void 0&&(clearInterval(B),B=void 0);try{z?.disconnect()}catch{}z=void 0}function H(e){return e===`zh`||e===`en`}function ne(){try{if(typeof document<`u`){let e=document.documentElement.lang;if(typeof e==`string`&&e.length>0){let t=e.toLowerCase();if(t.startsWith(`zh`))return`zh`;if(t.startsWith(`en`))return`en`}}}catch{}try{if(typeof navigator<`u`)return(navigator.language??`en`).toLowerCase().startsWith(`zh`)?`zh`:`en`}catch{}return`en`}function re(e){let t=e;if(!(t==null||typeof t.getSnapshot!=`function`||typeof t.subscribe!=`function`))return t}function ie(e){V();try{typeof MutationObserver<`u`&&typeof document<`u`&&(z=new MutationObserver(()=>{if(F!==void 0)return;let e=ne();e!==L.active&&ae({active:e,revision:L.revision+1})}),z.observe(document.documentElement,{attributes:!0,attributeFilter:[`lang`]}))}catch{}if(e===void 0)return;let t=0;B=setInterval(()=>{t+=1;let n;try{n=re(e())}catch{n=void 0}if(n!==void 0){oe(n);return}t>=8&&V()},250)}function ae(e){if(!(e.active===L.active&&e.revision===L.revision)){L=e;for(let e of R)e()}}function oe(e,t){let n=re(e);if(n===void 0){ae({active:ne(),revision:0}),ie(t);return}V(),F=n;let r=()=>{try{let e=n.getSnapshot();ae({active:H(e.active)?e.active:ne(),revision:e.revision})}catch{}};r(),I=n.subscribe(r)}function se(){V();try{I?.()}catch{}I=void 0,F=void 0,ae({active:ne(),revision:0})}const U={getSnapshot(){return L},subscribe(e){return R.add(e),()=>{R.delete(e)}}},ce=/\{(\w+)\}/g,W=(e,t)=>{let n=P[L.active][e]??P.en[e]??e;return t===void 0?n:n.replace(ce,(e,n)=>{let r=t[n];return r===void 0?e:String(r)})};function G(){return(0,c.useSyncExternalStore)(U.subscribe,U.getSnapshot),W}const le=`dsh-taskboard-view-v1`;function ue(){try{let e=localStorage.getItem(le);if(e===null)return{urgencies:[],sortBy:`default`};let t=JSON.parse(e),n=t.sortBy===`updated`||t.sortBy===`urgency`||t.sortBy===`created`||t.sortBy===`title`?t.sortBy:`default`;return{workspaceId:typeof t.workspaceId==`string`?t.workspaceId:void 0,urgencies:Array.isArray(t.urgencies)?t.urgencies.filter(e=>e===`urgent`||e===`normal`||e===`relaxed`):[],sortBy:n}}catch{return{urgencies:[],sortBy:`default`}}}function de(){let e=ue();return{boardOpen:!1,ledger:j(),workspaces:[],filters:{workspaceId:e.workspaceId,urgencies:e.urgencies},search:``,sortBy:e.sortBy,composerOpen:!1,secondaryOpen:!1,diagOpen:!1,queueOpen:!1,templates:[],tplManagerOpen:!1,importOpen:!1,settingsOpen:!1}}var fe=class{client;state=de();subscribers=new Set;disposed=!1;closeRequester;disposeStream;refreshInFlight;seenRevision;sessionJumper;sessionTitles;catalogFaces={};constructor(e){this.client=e}getSnapshot(){return this.state}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}emit(){if(!this.disposed)for(let e of this.subscribers)e()}setState(e){this.state={...this.state,...e},this.emit()}start(){this.refresh(),this.disposeStream=this.client.stream(e=>{this.seenRevision=e.revision,this.refresh()},()=>{this.refresh()})}async refresh(){return this.refreshInFlight===void 0&&(this.refreshInFlight=(async()=>{try{for(let e=0;e<3;e++){let[e,t]=await Promise.allSettled([Promise.resolve().then(()=>this.client.state()),Promise.resolve().then(()=>this.client.workspaces())]),n={},r=[],i=!1;if(e.status===`fulfilled`){let t=e.value,r;this.state.selectedId!==void 0&&(r=t.tasks.find(e=>e.id===this.state.selectedId)),Object.assign(n,{archiveSessionsSupported:t.capabilities?.archiveSessions===!0,ledger:t,queue:t.queue,selectedId:r===void 0?void 0:this.state.selectedId})}else r.push(`state: ${e.reason instanceof Error?e.reason.message:String(e.reason)}`);if(t.status===`fulfilled`){let e=t.value;n.workspaces=e;let r=this.state.filters.workspaceId;r!==void 0&&!e.some(e=>e.id===r)&&(n.filters={...this.state.filters,workspaceId:void 0},i=!0)}else r.push(`workspaces: ${t.reason instanceof Error?t.reason.message:String(t.reason)}`);if(n.error=r.length>0?r.join(`; `):void 0,this.setState(n),i&&this.persistView(),e.status!==`fulfilled`||this.seenRevision===void 0||e.value.revision>=this.seenRevision)break}}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}finally{this.refreshInFlight=void 0}})()),this.refreshInFlight}dispose(){this.disposed=!0,this.disposeStream?.(),this.subscribers.clear()}openBoard(){this.setState({boardOpen:!0})}closeBoard(){if(this.closeRequester!==void 0){this.closeRequester();return}this.setState({boardOpen:!1})}installCloseRequester(e){this.closeRequester=e}toggleBoard(){this.setState({boardOpen:!this.state.boardOpen})}setWorkspaceFilter(e){this.setState({filters:{...this.state.filters,workspaceId:e}}),this.persistView()}toggleUrgency(e){let t=new Set(this.state.filters.urgencies);t.has(e)?t.delete(e):t.add(e),this.setState({filters:{...this.state.filters,urgencies:[...t]}}),this.persistView()}setSearch(e){this.setState({search:e})}setSortBy(e){this.setState({sortBy:e}),this.persistView()}persistView(){try{localStorage.setItem(le,JSON.stringify({workspaceId:this.state.filters.workspaceId,urgencies:this.state.filters.urgencies,sortBy:this.state.sortBy}))}catch{}}select(e){this.setState({selectedId:e})}setComposer(e){this.setState({composerOpen:e,editingId:void 0,templatePrefill:void 0})}newFromTemplate(e){this.setState({composerOpen:!0,editingId:void 0,templatePrefill:e})}openEditor(e){this.setState({composerOpen:!0,editingId:e,templatePrefill:void 0})}closeForm(){this.setState({composerOpen:!1,editingId:void 0,templatePrefill:void 0})}toggleSecondary(){this.setState({secondaryOpen:!this.state.secondaryOpen})}gitAvailable(e){return e===void 0||this.state.workspaces.find(t=>t.id===e)?.gitAvailable===!0}repoCount(e){return e===void 0?1:this.state.workspaces.find(t=>t.id===e)?.repoCount??1}installSessionJumper(e){this.sessionJumper=e}installSessionTitles(e){this.sessionTitles=e}installModelCatalog(e){this.catalogFaces.models=e}installPresetRoster(e){this.catalogFaces.presets=e}get modelCatalog(){return this.catalogFaces.models}get presetCatalog(){return this.catalogFaces.presets}async fetchProjectSessions(e){let t=(await this.client.projectSessions(e)).sessions,n=this.sessionTitles?.()??{};return t.map(e=>({...e,title:n[e.id]?.displayTitle??n[e.id]?.title}))}async fetchModelCatalog(){if(this.catalogFaces.models!==void 0)try{let e=await this.catalogFaces.models();if(e!==void 0&&e.length>0)return e}catch{}try{return(await this.client.modelCatalog()).models??[]}catch{return[]}}async fetchPresetCatalog(){if(this.catalogFaces.presets!==void 0)try{let e=await this.catalogFaces.presets();if(e!==void 0&&e.presets!==void 0&&e.presets.length>0)return e}catch{}try{let e=await this.client.modelCatalog();return{presets:e.presets??[],...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{return{presets:[]}}}async openSession(e){if(this.sessionJumper===void 0)return`unavailable`;let t;try{t=await this.sessionJumper(e)}catch{return`unavailable`}return t===`opened`&&this.closeBoard(),t}async create(e){try{let t=await this.client.create(e);return this.setState({composerOpen:!1,error:void 0}),await this.refresh(),t.id}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async update(e,t,n){try{return await this.client.update(e,{ifVersion:t,...n}),this.setState({composerOpen:!1,editingId:void 0,error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async retryArchiveSessions(e){try{if(this.client.archiveSessions===void 0)return;let t=await this.client.archiveSessions(e);this.setState({sessionArchive:{taskId:e,result:t}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async move(e,t,n,r){try{let i=await this.client.move(e,{ifVersion:t,status:n,...r?.archiveSessions===void 0?{}:{archiveSessions:r.archiveSessions}});i.sessionArchive!==void 0&&this.setState({sessionArchive:{taskId:e,result:i.sessionArchive}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async reject(e,t,n){let r=n!==void 0&&n.trim().length>0?n.trim():void 0;try{return await this.client.reject(e,r===void 0?{ifVersion:t}:{ifVersion:t,body:r}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async toggleBlocked(e){try{await this.client.update(e.id,{ifVersion:e.version,blocked:!e.blocked}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async toggleChecklistItem(e,t){let n=(e.checklist??[]).map(e=>e.id===t?e.checked?{id:e.id,text:e.text,checked:!1}:{id:e.id,text:e.text,checked:!0,checkedBy:`user`,checkedAt:Date.now(),...e.note===void 0?{}:{note:e.note}}:e);try{await this.client.update(e.id,{ifVersion:e.version,checklist:n}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async fetchDiff(e,t){try{return await this.client.diff(e,t)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async comment(e,t){try{return await this.client.comment(e,t),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async uploadImage(e){try{return await this.client.uploadImage(e)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async run(e,t=!1){try{await this.client.run(e,t?{reuse:!0}:{}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async cancel(e){try{await this.client.cancel(e),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async mergeBranch(e){try{let t=await this.client.mergeBranch(e);return await this.refresh(),t.noop===!0?{ok:!0,noop:!0}:t.results===void 0?{ok:!0}:{ok:!0,results:t.results}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async removeWorktree(e,t){try{let n=await this.client.worktreeRemove(e,{deleteBranch:t});return await this.refresh(),n.branchError===void 0?{ok:!0}:{ok:!0,branchError:n.branchError}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}openDiagnostics(){this.setState({diagOpen:!0}),this.client.diagnostics().then(e=>this.setState({diagnostics:e})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeDiagnostics(){this.setState({diagOpen:!1})}openQueuePanel(){this.setState({queueOpen:!0}),this.refresh()}closeQueuePanel(){this.setState({queueOpen:!1})}async clearQueue(){try{let e=await this.client.clearQueue();return await this.refresh(),e}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}openSettings(){this.setState({settingsOpen:!0}),this.client.storage().then(e=>this.setState({storage:e,error:void 0})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeSettings(){this.setState({settingsOpen:!1,storageNotice:void 0})}async updateSettings(e){try{return await this.client.updateSettings(e),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async checkStorage(e){try{let t=await this.client.checkStorage(e);return this.setState({storage:t,error:void 0}),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async migrateStorage(e){try{let t=await this.client.migrateStorage(e);return this.setState({storage:t,storageNotice:{path:t.currentDirectory,warnings:t.warnings},error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}dismissStorageNotice(){this.state.storageNotice!==void 0&&this.setState({storageNotice:void 0})}async cleanupOrphan(e,t){try{await this.client.worktreeCleanup(e,t);let n=await this.client.diagnostics();this.setState({diagnostics:n,error:void 0})}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async remove(e,t,n){try{await this.client.remove(e,n?{purge:!0}:{ifVersion:t}),n&&this.setState({selectedId:void 0}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async duplicate(e){try{await this.client.create({title:e.title.slice(0,196)+W(`shared.duplicate.suffix`),workspaceId:e.workspaceId,urgency:e.urgency,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0&&e.execution.runAt>Date.now()?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async loadTemplates(){try{let e=await this.client.templates();return this.setState({templates:e.templates,error:void 0}),e.templates}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),[]}}prepareTemplateMenu(){this.state.templates.length===0&&this.loadTemplates()}openTemplateManager(){this.setState({tplManagerOpen:!0}),this.loadTemplates()}closeTemplateManager(){this.setState({tplManagerOpen:!1})}async upsertTemplate(e){try{return await this.client.templateUpsert(e),await this.loadTemplates(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async deleteTemplate(e){try{await this.client.templateDelete(e),await this.loadTemplates()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async saveAsTemplate(e){return this.upsertTemplate({name:e.title.slice(0,60),task:{title:e.title,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,urgency:e.urgency,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}})}async fetchPromptCompletions(){try{return await this.client.promptCompletions()}catch{return}}openImport(){this.setState({importOpen:!0})}closeImport(){this.setState({importOpen:!1})}async importPreview(e){try{return(await this.client.importPreview(e)).plan}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async importCommit(e,t){try{let n=await this.client.importCommit(e,t);return await this.refresh(),n}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}exportJson(){let e=new Date,t=e=>String(e).padStart(2,`0`),n=`dsh-taskboard-${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}.json`,r=JSON.stringify(this.state.ledger,null,2);this.download(n,r,`application/json`)}exportCsv(){let e=e=>{let t=String(e??``);return/^[=+\-@\t\r]/.test(t)&&(t=`'${t}`),/[",\n\r]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t},t=[`id`,`title`,`status`,`urgency`,`blocked`,`project`,`claimedBy`,`mode`,`cron`,`nextRunAt`,`model`,`createdAt`,`updatedAt`,`comments`,`executions`],n=this.state.ledger.tasks.map(t=>[t.id,t.title,t.status,t.urgency,t.blocked?`yes`:`no`,t.workspaceId,t.claimedBy??``,t.execution.mode,t.execution.cron??``,t.execution.nextRunAt===void 0?``:new Date(t.execution.nextRunAt).toISOString(),t.model===void 0?``:`${t.model.provider}/${t.model.model}`,new Date(t.createdAt).toISOString(),new Date(t.updatedAt).toISOString(),t.comments.length,t.executions.length].map(e).join(`,`)),r=new Date,i=e=>String(e).padStart(2,`0`),a=`dsh-taskboard-${r.getFullYear()}${i(r.getMonth()+1)}${i(r.getDate())}.csv`;this.download(a,`\uFEFF${[t.join(`,`),...n].join(`\r +Writes queue briefly during migration.`,"set.storage.migrated":`Migration succeeded — data now lives in {path}`,"set.storage.warnings":`Migration finished, but some old files could not be removed: {warnings}`,"form.field.permission":`Execution permission`,"form.perm.write":`Workspace write`,"form.perm.readOnly":`Read-only`,"form.perm.fullAccess":`Full access`,"form.perm.defaultTag":` (default)`,"set.perm.heading":`Default execution permission`,"set.perm.writeName":`📁 Workspace write (factory default)`,"set.perm.writeHint":`Read-write access to the workspace and temp directories; writes need no extra confirmation`,"set.perm.readOnlyName":`🔒 Read-only`,"set.perm.readOnlyHint":`Read-only viewing and search only; no file changes or destructive commands`,"set.perm.fullName":`⚡ Full access`,"set.perm.fullHint":`Unrestricted: whole-disk access and external system commands`,"set.perm.current":`Currently saved default: {current}. The execution permission preset applied to new tasks.`,"set.schedule.heading":`Scheduled execution`,"set.schedule.hint":`Takes effect for subsequent scheduler and execution checks. The offline window only classifies historical triggers while the host was down; a queue shelf life of 0 retains queued work across restarts.`,"set.schedule.concurrent":`Maximum concurrent executions (1–100)`,"set.schedule.missedAfter":`Offline missed window, minutes (1–1440)`,"board.queue.chip":`Queued {n}`,"queue.panel.title":`Execution queue`,"queue.depth":`Queued`,"queue.wait":`Longest wait`,"queue.clear":`Clear queue`,"queue.confirm":`Confirm clear`,"queue.cleared":`Cleared {n} queued task(s).`,"set.schedule.queueMaxAge":`Queue shelf life, minutes (0 = retain indefinitely; max 10080)`,"set.schedule.dispatchInterval":`Scheduled-session start interval, ms (default 1000; 0 = no throttle; max 60000)`,"set.schedule.invalid":`Enter whole numbers within the allowed ranges before saving.`,"card.badge.permReadOnly":`🔒 Read-only`,"card.badge.permReadOnlyTitle":`Permission: read-only`,"card.badge.permFull":`⚡ Full access`,"card.badge.permFullTitle":`Permission: full access`,"detail.chip.permReadOnly":`Read-only`,"detail.chip.permFull":`Full access`,"detail.chip.permWrite":`Workspace write`,"tpl.meta.permReadOnly":`read-only`,"tpl.meta.permFull":`full access`,"md.imageAlt":`Image {n}`,"md.imageTitle":`Click to view full size ({alt})`,"md.lightboxAlt":`Full-size preview`,"md.closePreview":`Close preview`,"image.add":`Insert image`,"image.uploading":`Uploading…`,"image.hint":`Choose, paste, or drop PNG/JPEG/GIF/WebP images up to 5 MB each`,"image.defaultAlt":`Image`,"slash.aria":`Quick commands and skills`,"slash.title":`Quick command and skill completion`,"slash.hint":`↑↓ navigate · Enter / Tab pick · Esc close`,"slash.badge.command":`⚡ command`,"slash.badge.skill":`🧩 skill`,"slash.tipA":`💡 Type`,"slash.tipB":`to autocomplete slash commands and agent skills`,"slash.cmd.goal.desc":`Autonomously drive long-horizon goals to completion`,"slash.cmd.goal.hint":`<goal description>`,"slash.cmd.schedule.desc":`Set one-off or recurring cron schedules`,"slash.cmd.schedule.hint":`<time or expression>`,"slash.cmd.plan.desc":`Draft a step-by-step plan and get user confirmation before acting`,"slash.cmd.browser.desc":`Launch a browser for page interaction and live search`,"slash.cmd.grill-me.desc":`Align requirements and design intent through one-question-at-a-time interviews`,"slash.cmd.teamwork-preview.desc":`Multi-agent collaboration and team workflow preview`,"slash.cmd.learn.desc":`Capture solutions and new rules into the knowledge base`,"slash.cmd.review.desc":`Multi-axis code review (correctness, architecture, security)`,"slash.cmd.security.desc":`Security hardening and vulnerability scanning`,"slash.cmd.permission.desc":`Switch the session permission level (read-only / workspace-write / full-access)`,"slash.cmd.permission.hint":`<preset>`,"slash.skill.frontend-ui-engineering":`Build production-grade, accessible frontend interfaces and components`,"slash.skill.api-and-interface-design":`Design stable, well-bounded REST / RPC interfaces`,"slash.skill.test-driven-development":`Test-driven development (TDD): unit and integration tests`,"slash.skill.debugging-and-error-recovery":`Systematically locate bug root causes and recover`,"slash.skill.performance-optimization":`Frontend/backend performance tuning and query optimization`,"slash.skill.ci-cd-and-automation":`Build automation, CI/CD pipelines and quality gates`,"slash.skill.code-review-and-quality":`Multi-axis code review and refactoring guidance`,"slash.skill.code-simplification":`Simplify complex logic for readability and maintainability`,"slash.skill.context-engineering":`Optimize context structure and prompt engineering`,"slash.skill.doubt-driven-development":`Adversarial, doubt-driven review of core logic`,"slash.skill.git-workflow-and-versioning":`Git workflow, branching, semantic versioning and changelogs`,"slash.skill.idea-refine":`Refine ideas and test hypotheses via divergent-convergent thinking`,"slash.skill.incremental-implementation":`Deliver multi-file changes in small, incremental steps`,"slash.skill.interview-me":`Deep interviews to surface true intent`,"slash.skill.memory-leak-debugging":`Diagnose JavaScript/Node.js memory leaks`,"slash.skill.observability-and-instrumentation":`Add logging, metrics and distributed tracing`,"slash.skill.planning-and-task-breakdown":`Break complex requirements into ordered, executable tasks`,"slash.skill.security-and-hardening":`Harden against vulnerabilities; input filtering and auth hardening`,"slash.skill.shipping-and-launch":`Pre-launch checklists and rollback strategies`,"slash.skill.source-driven-development":`Design from authoritative docs and source code`,"slash.skill.spec-driven-development":`Write clear technical specs before coding`,"slash.skill.using-agent-skills":`Discover and invoke agent skills dynamically`,"sys.execFailed":`[System] Execution failed: {error}; the task was returned to todo.`,"sys.endedWithComment":`[System] The execution session ended with comments but was not moved to in review; the system moved it to in review automatically.`,"sys.endedNoHandoff":`[System] The execution session ended without a protocol handoff (no comments, not moved to in review); the system moved it to in review automatically — review it, then send back or accept.`,"sys.sessionError":`[System] Session execution error: {error}; the task was returned to todo.`,"sys.sessionDone":`[System] The session finished; automatically moved to in review.`,"sys.cronDead":`[System] The cron expression {cron} has no trigger time within 4 years; scheduling disabled — fix the cron and re-enable.`,"sys.periodicHandoff":`[System] Periodic run finished; the schedule now continues on the new todo card {nextTaskId}. Review this card, then accept.`,"sys.spawnedFrom":`[System] The previous periodic run finished (execution {executionId}); this card carries the schedule onward. See {sourceId} for the finished round.`,"sys.runAtMissed":`[System] The one-shot trigger time was missed (host was down); it will not re-fire. Run manually or reschedule.`,"sys.mergeSingle":`[System] Branch {branch} merged into the main worktree (--no-ff).`,"sys.mergeMulti":`[System] Branches merged per repo (--no-ff): {summary}`}};let P,F,I={active:B(),revision:0};const L=new Set;let R,re;function z(){re!==void 0&&(clearInterval(re),re=void 0);try{R?.disconnect()}catch{}R=void 0}function ie(e){return e===`zh`||e===`en`}function B(){try{if(typeof document<`u`){let e=document.documentElement.lang;if(typeof e==`string`&&e.length>0){let t=e.toLowerCase();if(t.startsWith(`zh`))return`zh`;if(t.startsWith(`en`))return`en`}}}catch{}try{if(typeof navigator<`u`)return(navigator.language??`en`).toLowerCase().startsWith(`zh`)?`zh`:`en`}catch{}return`en`}function ae(e){let t=e;if(!(t==null||typeof t.getSnapshot!=`function`||typeof t.subscribe!=`function`))return t}function oe(e){z();try{typeof MutationObserver<`u`&&typeof document<`u`&&(R=new MutationObserver(()=>{if(P!==void 0)return;let e=B();e!==I.active&&se({active:e,revision:I.revision+1})}),R.observe(document.documentElement,{attributes:!0,attributeFilter:[`lang`]}))}catch{}if(e===void 0)return;let t=0;re=setInterval(()=>{t+=1;let n;try{n=ae(e())}catch{n=void 0}if(n!==void 0){V(n);return}t>=8&&z()},250)}function se(e){if(!(e.active===I.active&&e.revision===I.revision)){I=e;for(let e of L)e()}}function V(e,t){let n=ae(e);if(n===void 0){se({active:B(),revision:0}),oe(t);return}z(),P=n;let r=()=>{try{let e=n.getSnapshot();se({active:ie(e.active)?e.active:B(),revision:e.revision})}catch{}};r(),F=n.subscribe(r)}function ce(){z();try{F?.()}catch{}F=void 0,P=void 0,se({active:B(),revision:0})}const H={getSnapshot(){return I},subscribe(e){return L.add(e),()=>{L.delete(e)}}},le=/\{(\w+)\}/g,U=(e,t)=>{let n=N[I.active][e]??N.en[e]??e;return t===void 0?n:n.replace(le,(e,n)=>{let r=t[n];return r===void 0?e:String(r)})};function W(){return(0,c.useSyncExternalStore)(H.subscribe,H.getSnapshot),U}const ue=`dsh-taskboard-view-v1`;function G(){try{let e=localStorage.getItem(ue);if(e===null)return{urgencies:[],sortBy:`default`};let t=JSON.parse(e),n=t.sortBy===`updated`||t.sortBy===`urgency`||t.sortBy===`created`||t.sortBy===`title`?t.sortBy:`default`;return{workspaceId:typeof t.workspaceId==`string`?t.workspaceId:void 0,urgencies:Array.isArray(t.urgencies)?t.urgencies.filter(e=>e===`urgent`||e===`normal`||e===`relaxed`):[],sortBy:n}}catch{return{urgencies:[],sortBy:`default`}}}function de(){let e=G();return{boardOpen:!1,ledger:j(),workspaces:[],filters:{workspaceId:e.workspaceId,urgencies:e.urgencies},search:``,sortBy:e.sortBy,composerOpen:!1,secondaryOpen:!1,diagOpen:!1,queueOpen:!1,templates:[],tplManagerOpen:!1,importOpen:!1,settingsOpen:!1}}var fe=class{client;state=de();subscribers=new Set;disposed=!1;closeRequester;disposeStream;refreshInFlight;seenRevision;sessionJumper;sessionTitles;catalogFaces={};constructor(e){this.client=e}getSnapshot(){return this.state}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}emit(){if(!this.disposed)for(let e of this.subscribers)e()}setState(e){this.state={...this.state,...e},this.emit()}start(){this.refresh(),this.disposeStream=this.client.stream(e=>{this.seenRevision=e.revision,this.refresh()},()=>{this.refresh()})}async refresh(){return this.refreshInFlight===void 0&&(this.refreshInFlight=(async()=>{try{for(let e=0;e<3;e++){let[e,t]=await Promise.allSettled([Promise.resolve().then(()=>this.client.state()),Promise.resolve().then(()=>this.client.workspaces())]),n={},r=[],i=!1;if(e.status===`fulfilled`){let t=e.value,r;this.state.selectedId!==void 0&&(r=t.tasks.find(e=>e.id===this.state.selectedId)),Object.assign(n,{archiveSessionsSupported:t.capabilities?.archiveSessions===!0,ledger:t,queue:t.queue,selectedId:r===void 0?void 0:this.state.selectedId})}else r.push(`state: ${e.reason instanceof Error?e.reason.message:String(e.reason)}`);if(t.status===`fulfilled`){let e=t.value;n.workspaces=e;let r=this.state.filters.workspaceId;r!==void 0&&!e.some(e=>e.id===r)&&(n.filters={...this.state.filters,workspaceId:void 0},i=!0)}else r.push(`workspaces: ${t.reason instanceof Error?t.reason.message:String(t.reason)}`);if(n.error=r.length>0?r.join(`; `):void 0,this.setState(n),i&&this.persistView(),e.status!==`fulfilled`||this.seenRevision===void 0||e.value.revision>=this.seenRevision)break}}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}finally{this.refreshInFlight=void 0}})()),this.refreshInFlight}dispose(){this.disposed=!0,this.disposeStream?.(),this.subscribers.clear()}openBoard(){this.setState({boardOpen:!0})}closeBoard(){if(this.closeRequester!==void 0){this.closeRequester();return}this.setState({boardOpen:!1})}installCloseRequester(e){this.closeRequester=e}toggleBoard(){this.setState({boardOpen:!this.state.boardOpen})}setWorkspaceFilter(e){this.setState({filters:{...this.state.filters,workspaceId:e}}),this.persistView()}toggleUrgency(e){let t=new Set(this.state.filters.urgencies);t.has(e)?t.delete(e):t.add(e),this.setState({filters:{...this.state.filters,urgencies:[...t]}}),this.persistView()}setSearch(e){this.setState({search:e})}setSortBy(e){this.setState({sortBy:e}),this.persistView()}persistView(){try{localStorage.setItem(ue,JSON.stringify({workspaceId:this.state.filters.workspaceId,urgencies:this.state.filters.urgencies,sortBy:this.state.sortBy}))}catch{}}select(e){this.setState({selectedId:e})}setComposer(e){this.setState({composerOpen:e,editingId:void 0,templatePrefill:void 0})}newFromTemplate(e){this.setState({composerOpen:!0,editingId:void 0,templatePrefill:e})}openEditor(e){this.setState({composerOpen:!0,editingId:e,templatePrefill:void 0})}closeForm(){this.setState({composerOpen:!1,editingId:void 0,templatePrefill:void 0})}toggleSecondary(){this.setState({secondaryOpen:!this.state.secondaryOpen})}gitAvailable(e){return e===void 0||this.state.workspaces.find(t=>t.id===e)?.gitAvailable===!0}repoCount(e){return e===void 0?1:this.state.workspaces.find(t=>t.id===e)?.repoCount??1}installSessionJumper(e){this.sessionJumper=e}installSessionTitles(e){this.sessionTitles=e}installModelCatalog(e){this.catalogFaces.models=e}installPresetRoster(e){this.catalogFaces.presets=e}get modelCatalog(){return this.catalogFaces.models}get presetCatalog(){return this.catalogFaces.presets}async fetchProjectSessions(e){let t=(await this.client.projectSessions(e)).sessions,n=this.sessionTitles?.()??{};return t.map(e=>({...e,title:n[e.id]?.displayTitle??n[e.id]?.title}))}async fetchModelCatalog(){if(this.catalogFaces.models!==void 0)try{let e=await this.catalogFaces.models();if(e!==void 0&&e.length>0)return e}catch{}try{return(await this.client.modelCatalog()).models??[]}catch{return[]}}async fetchPresetCatalog(){if(this.catalogFaces.presets!==void 0)try{let e=await this.catalogFaces.presets();if(e!==void 0&&e.presets!==void 0&&e.presets.length>0)return e}catch{}try{let e=await this.client.modelCatalog();return{presets:e.presets??[],...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{return{presets:[]}}}async openSession(e){if(this.sessionJumper===void 0)return`unavailable`;let t;try{t=await this.sessionJumper(e)}catch{return`unavailable`}return t===`opened`&&this.closeBoard(),t}async create(e){try{let t=await this.client.create(e);return this.setState({composerOpen:!1,error:void 0}),await this.refresh(),t.id}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async update(e,t,n){try{return await this.client.update(e,{ifVersion:t,...n}),this.setState({composerOpen:!1,editingId:void 0,error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async retryArchiveSessions(e){try{if(this.client.archiveSessions===void 0)return;let t=await this.client.archiveSessions(e);this.setState({sessionArchive:{taskId:e,result:t}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async move(e,t,n,r){try{let i=await this.client.move(e,{ifVersion:t,status:n,...r?.archiveSessions===void 0?{}:{archiveSessions:r.archiveSessions}});i.sessionArchive!==void 0&&this.setState({sessionArchive:{taskId:e,result:i.sessionArchive}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async reject(e,t,n){let r=n!==void 0&&n.trim().length>0?n.trim():void 0;try{return await this.client.reject(e,r===void 0?{ifVersion:t}:{ifVersion:t,body:r}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async toggleBlocked(e){try{await this.client.update(e.id,{ifVersion:e.version,blocked:!e.blocked}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async toggleChecklistItem(e,t){let n=(e.checklist??[]).map(e=>e.id===t?e.checked?{id:e.id,text:e.text,checked:!1}:{id:e.id,text:e.text,checked:!0,checkedBy:`user`,checkedAt:Date.now(),...e.note===void 0?{}:{note:e.note}}:e);try{await this.client.update(e.id,{ifVersion:e.version,checklist:n}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async fetchDiff(e,t){try{return await this.client.diff(e,t)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async comment(e,t){try{return await this.client.comment(e,t),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async uploadImage(e){try{return await this.client.uploadImage(e)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async run(e,t=!1){try{await this.client.run(e,t?{reuse:!0}:{}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async cancel(e){try{await this.client.cancel(e),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async mergeBranch(e){try{let t=await this.client.mergeBranch(e);return await this.refresh(),t.noop===!0?{ok:!0,noop:!0}:t.results===void 0?{ok:!0}:{ok:!0,results:t.results}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async removeWorktree(e,t){try{let n=await this.client.worktreeRemove(e,{deleteBranch:t});return await this.refresh(),n.branchError===void 0?{ok:!0}:{ok:!0,branchError:n.branchError}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}openDiagnostics(){this.setState({diagOpen:!0}),this.client.diagnostics().then(e=>this.setState({diagnostics:e})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeDiagnostics(){this.setState({diagOpen:!1})}openQueuePanel(){this.setState({queueOpen:!0}),this.refresh()}closeQueuePanel(){this.setState({queueOpen:!1})}async clearQueue(){try{let e=await this.client.clearQueue();return await this.refresh(),e}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}openSettings(){this.setState({settingsOpen:!0}),this.client.storage().then(e=>this.setState({storage:e,error:void 0})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeSettings(){this.setState({settingsOpen:!1,storageNotice:void 0})}async updateSettings(e){try{return await this.client.updateSettings(e),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async checkStorage(e){try{let t=await this.client.checkStorage(e);return this.setState({storage:t,error:void 0}),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async migrateStorage(e){try{let t=await this.client.migrateStorage(e);return this.setState({storage:t,storageNotice:{path:t.currentDirectory,warnings:t.warnings},error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}dismissStorageNotice(){this.state.storageNotice!==void 0&&this.setState({storageNotice:void 0})}async cleanupOrphan(e,t){try{await this.client.worktreeCleanup(e,t);let n=await this.client.diagnostics();this.setState({diagnostics:n,error:void 0})}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async remove(e,t,n){try{await this.client.remove(e,n?{purge:!0}:{ifVersion:t}),n&&this.setState({selectedId:void 0}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async duplicate(e){try{await this.client.create({title:e.title.slice(0,196)+U(`shared.duplicate.suffix`),workspaceId:e.workspaceId,urgency:e.urgency,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0&&e.execution.runAt>Date.now()?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async loadTemplates(){try{let e=await this.client.templates();return this.setState({templates:e.templates,error:void 0}),e.templates}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),[]}}prepareTemplateMenu(){this.state.templates.length===0&&this.loadTemplates()}openTemplateManager(){this.setState({tplManagerOpen:!0}),this.loadTemplates()}closeTemplateManager(){this.setState({tplManagerOpen:!1})}async upsertTemplate(e){try{return await this.client.templateUpsert(e),await this.loadTemplates(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async deleteTemplate(e){try{await this.client.templateDelete(e),await this.loadTemplates()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async saveAsTemplate(e){return this.upsertTemplate({name:e.title.slice(0,60),task:{title:e.title,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,urgency:e.urgency,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}})}async fetchPromptCompletions(){try{return await this.client.promptCompletions()}catch{return}}openImport(){this.setState({importOpen:!0})}closeImport(){this.setState({importOpen:!1})}async importPreview(e){try{return(await this.client.importPreview(e)).plan}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async importCommit(e,t){try{let n=await this.client.importCommit(e,t);return await this.refresh(),n}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}exportJson(){let e=new Date,t=e=>String(e).padStart(2,`0`),n=`dsh-taskboard-${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}.json`,r=JSON.stringify(this.state.ledger,null,2);this.download(n,r,`application/json`)}exportCsv(){let e=e=>{let t=String(e??``);return/^[=+\-@\t\r]/.test(t)&&(t=`'${t}`),/[",\n\r]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t},t=[`id`,`title`,`status`,`urgency`,`blocked`,`project`,`claimedBy`,`mode`,`cron`,`nextRunAt`,`model`,`createdAt`,`updatedAt`,`comments`,`executions`],n=this.state.ledger.tasks.map(t=>[t.id,t.title,t.status,t.urgency,t.blocked?`yes`:`no`,t.workspaceId,t.claimedBy??``,t.execution.mode,t.execution.cron??``,t.execution.nextRunAt===void 0?``:new Date(t.execution.nextRunAt).toISOString(),t.model===void 0?``:`${t.model.provider}/${t.model.model}`,new Date(t.createdAt).toISOString(),new Date(t.updatedAt).toISOString(),t.comments.length,t.executions.length].map(e).join(`,`)),r=new Date,i=e=>String(e).padStart(2,`0`),a=`dsh-taskboard-${r.getFullYear()}${i(r.getMonth()+1)}${i(r.getDate())}.csv`;this.download(a,`\uFEFF${[t.join(`,`),...n].join(`\r `)}`,`text/csv`)}download(e,t,n){try{let r=new Blob([t],{type:n}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=e,a.click(),setTimeout(()=>URL.revokeObjectURL(i),5e3)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}};const pe=`dsh-taskboard-styles`;function me(){if(typeof document>`u`)return;let e=document.getElementById(pe);e===null&&(e=document.createElement(`style`),e.id=pe,e.textContent=` .dsh-atb-entry { display: flex; align-items: center; gap: 8px; position: relative; @@ -974,13 +974,13 @@ color: var(--dsw-alias-state-business-primary, #3e63dd); `),urgency:`relaxed`,execution:{mode:`scheduled`,cron:`0 9 * * 1`}}}},en:{"tpl-feature":{name:`New feature`,task:{title:`New feature:`,prompt:[`Implement the new feature above and hand off in order:`,`1. Clarify the requirement boundaries and acceptance criteria; list the implementation points`,`2. Implement the feature (including type definitions and error handling)`,`3. Add tests (unit / regression)`,`4. Run the relevant test suites and confirm they pass`].join(` `),urgency:`normal`,checklist:[`Implementation points clarified (requirement boundaries and acceptance criteria)`,`Feature implemented with tests added`,`Relevant test suites pass`]}},"tpl-bugfix":{name:`Bug fix`,task:{title:`Fix:`,prompt:[`Fix the issue above and hand off in order:`,`1. Reproduce the issue (write minimal reproduction steps or a test)`,`2. Locate the root cause and explain why it happens`,`3. Fix it and add regression tests`,`4. Run the relevant test suites and confirm there are no regressions`].join(` `),urgency:`urgent`,checklist:[`Issue reproduced and root cause located`,`Fix committed to the task branch`,`Regression tests pass`]}},"tpl-release":{name:`Release check`,task:{title:`Release:`,prompt:`Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).`,urgency:`normal`,checklist:[`Version bumped (package.json and version constants in sync)`,`Build passes`,`All tests pass`,`Changelog written`]}},"tpl-patrol":{name:`Routine patrol`,task:{title:`Patrol:`,prompt:[`Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.`,`List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.`,`Output a patrol summary (use {{lastComments}} to review the last patrol’s conclusions).`].join(` -`),urgency:`relaxed`,execution:{mode:`scheduled`,cron:`0 9 * * 1`}}}}};function be(e,t){if(ve.includes(e))return ye[t][e]}function xe(e){return e.builtin===!0?be(e.id,U.getSnapshot().active)?.name??e.name:e.name}function Se(e){return e.builtin===!0?be(e.id,U.getSnapshot().active)?.task??e.task:e.task}function J(e){if(e===void 0)return``;let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}`}function Ce(e,t){if(e.status!==`in_progress`)return!1;let n=e.executions.filter(e=>e.outcome===`running`);if(n.length===0)return!1;let r=Math.max(...n.map(t=>t.lastActivityAt??e.claimedAt??-1/0));return Number.isFinite(r)&&t-r>18e5}const we=`application/x-dsh-atb-task`;function Te(e){return e===void 0?``:e.replace(/^session-(taskboard-)?/,``).slice(0,8)}function Ee({task:e,controller:t,draggable:n=!1,now:r,onAlert:i}){let a=G(),[o,s]=(0,c.useState)(!1),[u,d]=(0,c.useState)(``),f=e.executions.length>0?e.executions[e.executions.length-1]:void 0,p=e.executions.find(e=>e.outcome===`running`),m=r!==void 0&&Ce(e,r),h=e.status===`in_review`&&e.trashedAt===void 0,g=[...e.executions].reverse().find(e=>e.sessionId!==void 0),_=p?.sessionId??g?.sessionId??(e.claimedBy?.startsWith(`session-`)?e.claimedBy:void 0),v=()=>{t.reject(e.id,e.version,u).then(e=>{e&&(s(!1),d(``))})};return(0,l.jsxs)(`div`,{role:`button`,tabIndex:0,className:`dsh-atb-card`,"data-urgency":e.urgency,draggable:n&&!o,onDragStart:t=>{if(p!==void 0){t.preventDefault();let n=a(`card.drag.running`,{title:e.title});i===void 0?alert(n):i(n);return}t.dataTransfer.setData(we,e.id),t.dataTransfer.effectAllowed=`move`,t.currentTarget.dataset.dragging=`true`},onDragEnd:e=>{delete e.currentTarget.dataset.dragging},onClick:()=>t.select(e.id),onKeyDown:n=>{n.target===n.currentTarget&&(n.key===`Enter`||n.key===` `)&&(n.preventDefault(),t.select(e.id))},children:[(0,l.jsx)(`div`,{className:`dsh-atb-card-title`,children:e.title}),(0,l.jsxs)(`div`,{className:`dsh-atb-card-meta`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-badge`,children:a(q[e.urgency])}),e.blocked&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`blocked`,children:a(`shared.blocked`)}),m&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`stale`,children:a(`card.badge.stale`)}),e.execution.mode===`scheduled`&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,"data-kind":`scheduled`,children:[`⏰ `,J(e.execution.nextRunAt)]}),e.model!==void 0&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,title:a(`card.badge.modelTitle`,{model:e.model.provider+`/`+e.model.model})+(e.model.reasoningEffort===void 0?``:a(`card.badge.modelEffort`,{effort:e.model.reasoningEffort})),children:[e.model.model,e.model.reasoningEffort===void 0?``:` (${e.model.reasoningEffort})`]}),e.permission===`read-only`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`blocked`,title:a(`card.badge.permReadOnlyTitle`),children:a(`card.badge.permReadOnly`)}),e.permission===`danger-full-access`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`urgent`,title:a(`card.badge.permFullTitle`),children:a(`card.badge.permFull`)}),e.checklist!==void 0&&e.checklist.length>0&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,"data-kind":e.status===`in_review`&&e.checklist.some(e=>!e.checked)?`blocked`:`checklist`,title:e.status===`in_review`&&e.checklist.some(e=>!e.checked)?a(`card.badge.checklistReview`):a(`card.badge.checklist`),children:[`☑ `,e.checklist.filter(e=>e.checked).length,`/`,e.checklist.length]}),e.status===`done`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`done`,children:a(`status.pill.done`)}),f!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":f.outcome===`running`?`running`:f.outcome,children:a(_e[f.outcome]??f.outcome)}),_!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-card-session`,title:a(`card.session.jumpTitle`,{id:_}),onClick:e=>{e.stopPropagation(),t.openSession(_).then(e=>{if(e===`missing`){let e=a(`card.session.missing`,{id:Te(_)});i===void 0?alert(e):i(e)}else if(e===`archived`){let e=a(`card.session.archived`,{id:Te(_)});i===void 0?alert(e):i(e)}else if(e===`unavailable`){let e=a(`card.session.unavailable`,{id:_});i===void 0?alert(e):i(e)}})},children:[`🤖 `,Te(_),` ↗`]}),e.comments.length>0&&(0,l.jsxs)(`span`,{children:[`💬 `,e.comments.length]}),e.trashedAt!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`trashed`,children:a(`card.badge.pendingPurge`)}),(0,l.jsx)(`span`,{style:{marginLeft:`auto`},children:J(e.updatedAt)})]}),h&&(o?(0,l.jsxs)(`div`,{className:`dsh-atb-quick-reject`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-quick-note`,value:u,placeholder:a(`card.reject.placeholder`),autoFocus:!0,spellCheck:!1,onChange:e=>d(e.target.value),onKeyDown:e=>{e.key===`Enter`?v():e.key===`Escape`&&(s(!1),d(``))}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject-confirm`,onClick:v,children:a(`card.reject.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject-cancel`,onClick:()=>{s(!1),d(``)},children:a(`shared.cancel`)})]}):(0,l.jsxs)(`div`,{className:`dsh-atb-quick`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`done`,title:a(`card.action.doneTitle`),onClick:()=>void t.move(e.id,e.version,`done`),children:a(`card.action.done`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject`,title:a(`card.action.rejectTitle`),onClick:()=>s(!0),children:a(`card.action.reject`)})]}))]})}function Y(){let[e,t]=(0,c.useState)(null);return{alert:e=>t(e),el:e===null?null:(0,l.jsx)(De,{message:e,onClose:()=>t(null)})}}function De({message:e,onClose:t}){let n=G();return(0,c.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,l.jsx)(`div`,{className:`dsh-atb-alert-backdrop`,onClick:t,children:(0,l.jsxs)(`div`,{className:`dsh-atb-alert`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`div`,{className:`dsh-atb-alert-icon`,children:`⛔`}),(0,l.jsx)(`div`,{className:`dsh-atb-alert-msg`,children:e}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:t,children:n(`alert.ok`)})]})})}const Oe=`image/png,image/jpeg,image/gif,image/webp`,ke=new Set(Oe.split(`,`));function Ae(e,t){let n=e.replace(/\.(?:png|jpe?g|gif|webp)$/i,``).replace(/[\[\]\r\n]/g,` `).replace(/\s+/g,` `).trim();return n.length>0?n.slice(0,120):t}function je(e,t){return`![${t}](${e.url})`}function Me(e,t,n,r){let i=e.slice(0,t),a=e.slice(n),o=`${i.length>0&&!i.endsWith(` +`),urgency:`relaxed`,execution:{mode:`scheduled`,cron:`0 9 * * 1`}}}}};function be(e,t){if(ve.includes(e))return ye[t][e]}function xe(e){return e.builtin===!0?be(e.id,H.getSnapshot().active)?.name??e.name:e.name}function Se(e){return e.builtin===!0?be(e.id,H.getSnapshot().active)?.task??e.task:e.task}function J(e){if(e===void 0)return``;let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}`}function Ce(e,t){if(e.status!==`in_progress`)return!1;let n=e.executions.filter(e=>e.outcome===`running`);if(n.length===0)return!1;let r=Math.max(...n.map(t=>t.lastActivityAt??e.claimedAt??-1/0));return Number.isFinite(r)&&t-r>18e5}const we=`application/x-dsh-atb-task`;function Te(e){return e===void 0?``:e.replace(/^session-(taskboard-)?/,``).slice(0,8)}function Ee({task:e,controller:t,draggable:n=!1,now:r,onAlert:i}){let a=W(),[o,s]=(0,c.useState)(!1),[u,d]=(0,c.useState)(``),f=e.executions.length>0?e.executions[e.executions.length-1]:void 0,p=e.executions.find(e=>e.outcome===`running`),m=r!==void 0&&Ce(e,r),h=e.status===`in_review`&&e.trashedAt===void 0,g=[...e.executions].reverse().find(e=>e.sessionId!==void 0),_=p?.sessionId??g?.sessionId??(e.claimedBy?.startsWith(`session-`)?e.claimedBy:void 0),v=()=>{t.reject(e.id,e.version,u).then(e=>{e&&(s(!1),d(``))})};return(0,l.jsxs)(`div`,{role:`button`,tabIndex:0,className:`dsh-atb-card`,"data-urgency":e.urgency,draggable:n&&!o,onDragStart:t=>{if(p!==void 0){t.preventDefault();let n=a(`card.drag.running`,{title:e.title});i===void 0?alert(n):i(n);return}t.dataTransfer.setData(we,e.id),t.dataTransfer.effectAllowed=`move`,t.currentTarget.dataset.dragging=`true`},onDragEnd:e=>{delete e.currentTarget.dataset.dragging},onClick:()=>t.select(e.id),onKeyDown:n=>{n.target===n.currentTarget&&(n.key===`Enter`||n.key===` `)&&(n.preventDefault(),t.select(e.id))},children:[(0,l.jsx)(`div`,{className:`dsh-atb-card-title`,children:e.title}),(0,l.jsxs)(`div`,{className:`dsh-atb-card-meta`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-badge`,children:a(q[e.urgency])}),e.blocked&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`blocked`,children:a(`shared.blocked`)}),m&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`stale`,children:a(`card.badge.stale`)}),e.execution.mode===`scheduled`&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,"data-kind":`scheduled`,children:[`⏰ `,J(e.execution.nextRunAt)]}),e.model!==void 0&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,title:a(`card.badge.modelTitle`,{model:e.model.provider+`/`+e.model.model})+(e.model.reasoningEffort===void 0?``:a(`card.badge.modelEffort`,{effort:e.model.reasoningEffort})),children:[e.model.model,e.model.reasoningEffort===void 0?``:` (${e.model.reasoningEffort})`]}),e.permission===`read-only`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`blocked`,title:a(`card.badge.permReadOnlyTitle`),children:a(`card.badge.permReadOnly`)}),e.permission===`danger-full-access`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`urgent`,title:a(`card.badge.permFullTitle`),children:a(`card.badge.permFull`)}),e.checklist!==void 0&&e.checklist.length>0&&(0,l.jsxs)(`span`,{className:`dsh-atb-badge`,"data-kind":e.status===`in_review`&&e.checklist.some(e=>!e.checked)?`blocked`:`checklist`,title:e.status===`in_review`&&e.checklist.some(e=>!e.checked)?a(`card.badge.checklistReview`):a(`card.badge.checklist`),children:[`☑ `,e.checklist.filter(e=>e.checked).length,`/`,e.checklist.length]}),e.status===`done`&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`done`,children:a(`status.pill.done`)}),f!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":f.outcome===`running`?`running`:f.outcome,children:a(_e[f.outcome]??f.outcome)}),_!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-card-session`,title:a(`card.session.jumpTitle`,{id:_}),onClick:e=>{e.stopPropagation(),t.openSession(_).then(e=>{if(e===`missing`){let e=a(`card.session.missing`,{id:Te(_)});i===void 0?alert(e):i(e)}else if(e===`archived`){let e=a(`card.session.archived`,{id:Te(_)});i===void 0?alert(e):i(e)}else if(e===`unavailable`){let e=a(`card.session.unavailable`,{id:_});i===void 0?alert(e):i(e)}})},children:[`🤖 `,Te(_),` ↗`]}),e.comments.length>0&&(0,l.jsxs)(`span`,{children:[`💬 `,e.comments.length]}),e.trashedAt!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-badge`,"data-kind":`trashed`,children:a(`card.badge.pendingPurge`)}),(0,l.jsx)(`span`,{style:{marginLeft:`auto`},children:J(e.updatedAt)})]}),h&&(o?(0,l.jsxs)(`div`,{className:`dsh-atb-quick-reject`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-quick-note`,value:u,placeholder:a(`card.reject.placeholder`),autoFocus:!0,spellCheck:!1,onChange:e=>d(e.target.value),onKeyDown:e=>{e.key===`Enter`?v():e.key===`Escape`&&(s(!1),d(``))}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject-confirm`,onClick:v,children:a(`card.reject.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject-cancel`,onClick:()=>{s(!1),d(``)},children:a(`shared.cancel`)})]}):(0,l.jsxs)(`div`,{className:`dsh-atb-quick`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`done`,title:a(`card.action.doneTitle`),onClick:()=>void t.move(e.id,e.version,`done`),children:a(`card.action.done`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-quickbtn`,"data-act":`reject`,title:a(`card.action.rejectTitle`),onClick:()=>s(!0),children:a(`card.action.reject`)})]}))]})}function Y(){let[e,t]=(0,c.useState)(null);return{alert:e=>t(e),el:e===null?null:(0,l.jsx)(De,{message:e,onClose:()=>t(null)})}}function De({message:e,onClose:t}){let n=W();return(0,c.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,l.jsx)(`div`,{className:`dsh-atb-alert-backdrop`,onClick:t,children:(0,l.jsxs)(`div`,{className:`dsh-atb-alert`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`div`,{className:`dsh-atb-alert-icon`,children:`⛔`}),(0,l.jsx)(`div`,{className:`dsh-atb-alert-msg`,children:e}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:t,children:n(`alert.ok`)})]})})}const Oe=`image/png,image/jpeg,image/gif,image/webp`,ke=new Set(Oe.split(`,`));function Ae(e,t){let n=e.replace(/\.(?:png|jpe?g|gif|webp)$/i,``).replace(/[\[\]\r\n]/g,` `).replace(/\s+/g,` `).trim();return n.length>0?n.slice(0,120):t}function je(e,t){return`![${t}](${e.url})`}function Me(e,t,n,r){let i=e.slice(0,t),a=e.slice(n),o=`${i.length>0&&!i.endsWith(` `)?` `:``}${r}${a.length>0&&!a.startsWith(` `)?` -`:``}`;return{value:i+o+a,cursor:i.length+o.length}}function X(e){return Array.from(e).filter(e=>ke.has(e.type)).slice(0,10)}function Ne(e){return[`backlog`,`todo`,`in_progress`,`in_review`,`done`,`canceled`,`archived`].filter(t=>x(e.status,t))}function Z(e){return e===void 0?``:e.replace(/^session-(taskboard-)?/,``).slice(0,8)}function Pe(e,t){if(t.systemKey===void 0)return t.body;if(t.systemRows!==void 0){let n=t.systemRows.map(t=>{let n=t.repo===``?e(`iso.repo.root`):t.repo,r=t.outcome===`merged`?`✓`:t.outcome===`noop`?`⟲`:`✗`;return t.outcome===`failed`&&t.error!==void 0?`${n} ${r} ${t.error}`:`${n} ${r}`}).join(` · `);return e(t.systemKey,{summary:n})}return e(t.systemKey,t.systemParams)}function Fe(e,t){if(e===void 0||t===void 0)return``;let n=Math.max(0,Math.round((t-e)/1e3));return n<60?`${n}s`:n<3600?`${Math.floor(n/60)}m${n%60}s`:`${Math.floor(n/3600)}h${Math.floor(n%3600/60)}m`}function Q({icon:e,children:t,tone:n,title:r}){return(0,l.jsxs)(`span`,{className:`dsh-atb-chip2`,"data-tone":n,title:r,children:[e!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:e}),t]})}function Ie({text:e}){let t=G(),[n,r]=(0,c.useState)(null),i=/!\[(.*?)\]\(((?:data:image\/[^)]+)|(?:https?:\/\/[^)]+)|(?:[^)]+\.(?:png|jpg|jpeg|gif|webp|svg)))\)/gi,a=[],o=0,s,u=0;for(;(s=i.exec(e))!==null;){s.index>o&&a.push((0,l.jsx)(`span`,{children:e.slice(o,s.index)},`txt-${o}`));let n=s[1]||t(`md.imageAlt`,{n:++u}),c=s[2]??``;a.push((0,l.jsxs)(`div`,{className:`dsh-atb-detail-img-wrap`,children:[(0,l.jsx)(`img`,{src:c,alt:n,className:`dsh-atb-detail-img`,onClick:()=>r(c),title:t(`md.imageTitle`,{alt:n})}),(0,l.jsx)(`span`,{className:`dsh-atb-detail-img-caption`,children:n})]},`img-${s.index}`)),o=i.lastIndex}return o<e.length&&a.push((0,l.jsx)(`span`,{children:e.slice(o)},`txt-${o}`)),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-markdown-body`,children:a}),n!==null&&(0,l.jsx)(`div`,{className:`dsh-atb-lightbox-backdrop`,onClick:()=>r(null),children:(0,l.jsxs)(`div`,{className:`dsh-atb-lightbox-content`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`img`,{src:n,alt:t(`md.lightboxAlt`),className:`dsh-atb-lightbox-img`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-lightbox-close`,title:t(`md.closePreview`),onClick:()=>r(null),children:`✕`})]})})]})}function Le(e){return[...e.executions].reverse().find(e=>e.isolation!==void 0||e.worktreePath!==void 0||e.isolationNote!==void 0)}function Re(e){return e===void 0?``:e.slice(0,8)}function ze(e){let t=e.slice(3),n=t.indexOf(` -> `);return n>=0&&(t=t.slice(n+4)),t.startsWith(`"`)&&t.endsWith(`"`)&&t.length>1&&(t=t.slice(1,-1)),t}function Be({controller:e,task:t,execution:n,commit:r,path:i,repo:a}){let o=G(),[s,u]=(0,c.useState)({loading:!0});return(0,c.useEffect)(()=>{let o=!0;return u({loading:!0}),e.fetchDiff(t.id,{execution:n.id,...r===void 0?{path:i??``}:{commit:r},...a===void 0?{}:{repo:a}}).then(e=>{o&&u(e===void 0?{loading:!1,failed:!0}:{loading:!1,diff:e.diff,truncated:e.truncated})}),()=>{o=!1}},[e,t.id,n.id,r,i,a]),(0,l.jsxs)(`div`,{className:`dsh-atb-diffview`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diffview-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-diffview-title`,children:r===void 0?o(`diff.file`,{path:i??``}):o(`diff.commit`,{hash:Re(r)})}),s.loading&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`shared.loading`)}),s.truncated===!0&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`diff.truncated`)})]}),s.failed===!0?(0,l.jsx)(`div`,{className:`dsh-atb-diffview-error`,children:o(`diff.failed`)}):(0,l.jsx)(`pre`,{className:`dsh-atb-diffview-pre`,children:s.diff??``})]})}function Ve({task:e,controller:t}){let n=G(),r=e.checklist??[];if(r.length===0)return null;let{done:i,total:a}=N(e),o=a-i,s=e.status===`in_review`;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`checklist`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[n(`checklist.title`),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-progress`,"data-tone":s&&o>0?`bad`:void 0,children:[`☑ `,i,`/`,a,s&&o>0?n(`checklist.unchecked`,{n:o}):i===a?n(`checklist.allDone`):``]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-cl-items`,children:r.map(r=>(0,l.jsxs)(`label`,{className:`dsh-atb-cl-item`,"data-checked":r.checked?`true`:void 0,"data-alert":s&&!r.checked?`true`:void 0,children:[(0,l.jsx)(`input`,{type:`checkbox`,checked:r.checked,onChange:()=>void t.toggleChecklistItem(e,r.id)}),(0,l.jsx)(`span`,{className:`dsh-atb-cl-text`,children:r.text}),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-meta`,children:[r.checked?`${r.checkedBy===`user`?n(`checklist.byUser`):`🤖 ${Z(r.checkedBy)}`} · ${J(r.checkedAt)}`:n(`checklist.uncheckedItem`),r.note!==void 0&&r.note.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cl-note`,title:r.note,children:n(`checklist.evidence`,{note:r.note})})]})]},r.id))})]})}function He({task:e}){let t=G(),n=[...e.executions].reverse().find(e=>e.report!==void 0),r=n?.report;if(n===void 0||r===void 0)return null;let i=(e,t)=>t!==void 0&&t.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:e}),(0,l.jsx)(`ul`,{className:`dsh-atb-rpt-list`,children:t.map((e,t)=>(0,l.jsx)(`li`,{children:e},t))})]}):null;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`report`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[t(`report.title`),(0,l.jsx)(`span`,{className:`dsh-atb-cl-progress`,children:t(`report.submitted`,{time:J(n.endedAt??n.startedAt)})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-summary`,children:r.summary}),i(t(`report.changedFiles`),r.changedFiles),i(t(`report.checks`),r.checks),i(t(`report.artifacts`),r.artifacts),r.risk.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:t(`report.risk`)}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-risk`,children:r.risk})]})]})}function Ue({task:e,controller:t}){let n=G(),{alert:r,el:i}=Y(),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(null),[d,f]=(0,c.useState)(!1),[p,m]=(0,c.useState)(null),[h,g]=(0,c.useState)(!1),_=Le(e),v=e.executions.some(e=>e.outcome===`running`);if(_===void 0)return null;let y=()=>{f(!0),t.mergeBranch(e.id).then(e=>{if(f(!1),o(!1),!e.ok)r(n(`iso.merge.failed`,{error:e.error}));else if(e.results!==void 0){let t=e=>e===``?n(`iso.repo.root`):e,i=e.results.map(e=>`${t(e.repo)} ${e.outcome===`merged`?`✓`:e.outcome===`noop`?`⟲`:`✗`}`).join(` · `),a=e.results.some(e=>e.outcome===`failed`),o=e.results.find(e=>e.outcome===`failed`)?.error;r(a?n(`iso.merge.partial`,{summary:i,error:o??``}):n(`iso.merge.done`,{summary:i}))}else e.noop===!0&&r(n(`iso.merge.noop`))})},b=i=>{f(!0),t.removeWorktree(e.id,i).then(e=>{f(!1),u(null),e.ok?e.branchError!==void 0&&r(n(`iso.remove.branchFailed`,{error:e.branchError})):r(n(`iso.remove.failed`,{error:e.error}))})};if(_.isolation!==`worktree`||_.worktreePath===void 0)return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.title`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-none`,children:[n(`iso.none`),_.isolationNote===void 0?``:` · ${_.isolationNote}`]}),i]});let x=_.repos!==void 0&&_.repos.length>0,S=x?(_.repos??[]).map(e=>({repo:e.repo,label:e.repo===``?n(`iso.repo.root`):e.repo,branch:e.branch,worktreePath:e.worktreePath,baseCommit:e.baseCommit,headCommit:e.headCommit,commits:e.commits??[],commitTotal:e.commitsTotal??(e.commits??[]).length,dirty:e.dirtyFiles??[],dirtyTotal:e.dirtyFilesTotal??(e.dirtyFiles??[]).length,...e.diffStat===void 0?{}:{diffStat:e.diffStat},...e.changedFiles===void 0?{}:{changedFiles:e.changedFiles}})):[{label:n(`iso.repo.root`),branch:_.branch??e.branch,worktreePath:_.worktreePath,baseCommit:_.baseCommit,headCommit:_.headCommit,commits:_.commits??[],commitTotal:_.commitsTotal??(_.commits??[]).length,dirty:_.dirtyFiles??[],dirtyTotal:_.dirtyFilesTotal??(_.dirtyFiles??[]).length,..._.diffStat===void 0?{}:{diffStat:_.diffStat},..._.changedFiles===void 0?{}:{changedFiles:_.changedFiles}}];return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.worktreeTitle`)}),S.map(r=>(0,l.jsxs)(`section`,{className:`dsh-atb-iso-repo`,"data-multi":x?`true`:void 0,children:[x&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-repohead`,title:r.worktreePath,children:[(0,l.jsx)(`code`,{children:r.label}),(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-facts`,children:[!x&&(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,title:r.worktreePath,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]}),(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.baseline`,{base:Re(r.baseCommit),head:Re(r.headCommit)})}),r.changedFiles!==void 0&&r.changedFiles>0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.changed`,{n:r.changedFiles})}),r.diffStat!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,title:r.diffStat,children:r.diffStat})]}),r.commits.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commits`,children:[r.commits.slice(0,10).map(i=>(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commit`,"data-open":p?.commit===i.hash&&p?.repo===r.repo?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-commit-btn`,title:n(`iso.commit.openTitle`),onClick:()=>m(p?.commit===i.hash&&p?.repo===r.repo?null:{commit:i.hash,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:Re(i.hash)}),(0,l.jsx)(`span`,{children:i.subject})]}),p?.commit===i.hash&&p?.repo===r.repo&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,commit:i.hash,repo:r.repo})]},i.hash)),r.commitTotal>10&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.commits.more`,{n:r.commitTotal})})]}):(0,l.jsx)(`div`,{className:`dsh-atb-iso-nocommit`,children:n(`iso.nocommit`)}),r.dirtyTotal>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-toggle`,onClick:()=>g(!h),children:[n(`iso.dirty.toggle`,{n:r.dirtyTotal}),n(h?`iso.dirty.collapse`:`iso.dirty.expand`)]}),h&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty-files`,children:[r.dirty.slice(0,30).map((e,t)=>{let i=ze(e);return(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-file`,title:n(`iso.dirty.openTitle`),onClick:()=>m(p?.path===i&&p?.repo===r.repo?null:{path:i,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:e.slice(0,2)}),` `,i]},`${e}-${t}`)}),r.dirtyTotal>30&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.dirty.more`,{n:r.dirtyTotal})})]}),p?.path!==void 0&&p?.repo===r.repo&&h&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,path:p.path,repo:r.repo})]})]},r.repo??`root`)),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-actions`,children:[v?(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.running`)}):a?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(`iso.merge.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:d,onClick:y,children:n(`iso.merge.go`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(!1),children:n(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:d,title:n(`iso.merge.title`),onClick:()=>o(!0),children:n(`iso.merge.button`)}),!v&&(s===null?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtTitle`),onClick:()=>u(`wt`),children:n(`iso.remove.wt`)}),e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtbTitle`),onClick:()=>u(`wtb`),children:n(`iso.remove.wtb`)})]}):(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(s===`wtb`?`iso.remove.confirmWtb`:`iso.remove.confirmWt`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,onClick:()=>b(s===`wtb`),children:n(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>u(null),children:n(`shared.cancel`)})]})),!v&&s===null&&!a&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.keep`)})]}),i]})}function We({task:e,controller:t,now:n}){let r=G(),[i,a]=(0,c.useState)(``),[o,s]=(0,c.useState)(!1),u=(0,c.useRef)(null),d=(0,c.useRef)(null),[f,p]=(0,c.useState)(!1),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(!1),[v,y]=(0,c.useState)(!1),[b,x]=(0,c.useState)(!1),{alert:S,el:ee}=Y(),C=t.getSnapshot().workspaces.find(t=>t.id===e.workspaceId),w=e.status!==`in_progress`&&e.status!==`done`&&e.status!==`archived`,T=e.executions.find(e=>e.outcome===`running`),E=e.status===`in_progress`?e.claimedBy:void 0,D=n!==void 0&&Ce(e,n),O=(e.checklist??[]).filter(e=>!e.checked).length,te=[...e.executions].reverse().find(e=>e.sessionId!==void 0),k=T?.sessionId??te?.sessionId??(e.claimedBy?.startsWith(`session-`)?e.claimedBy:void 0),A=M(e),j=t.getSnapshot(),P=j.sessionArchive?.taskId===e.id?j.sessionArchive.result:void 0,F=e=>{b||(x(!0),e().catch(()=>void 0).finally(()=>x(!1)))},I=()=>{o||i.trim().length===0||t.comment(e.id,i).then(e=>{e&&a(``)})},L=async e=>{let n=X(e);if(n.length===0||o)return;s(!0);let c=u.current,l=c?.value??i,d=c?.selectionStart??l.length,f=c?.selectionEnd??d,p=!1;try{for(let e of n){let n=await t.uploadImage(e);if(n===void 0)continue;let i=Me(l,d,f,je(n,Ae(e.name,r(`image.defaultAlt`))));l=i.value,d=i.cursor,f=d,p=!0}p&&(a(l),setTimeout(()=>{u.current?.focus(),u.current?.setSelectionRange(d,d)},0))}finally{s(!1)}},R=e=>{t.openSession(e).then(t=>{t===`missing`?S(r(`card.session.missing`,{id:Z(e)})):t===`archived`?S(r(`card.session.archived`,{id:Z(e)})):t===`unavailable`&&S(r(`card.session.unavailable`,{id:e}))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-detail`,"data-urgency":e.urgency,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-head`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlewrap`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlebar`,children:[(0,l.jsx)(`h3`,{children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-statuspill`,"data-status":e.status,children:r(ge[e.status]??e.status)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-chips`,children:[(0,l.jsxs)(Q,{tone:e.urgency,children:[`● `,r(q[e.urgency]??e.urgency)]}),(0,l.jsx)(Q,{icon:`📁`,children:C?.title??Z(e.workspaceId)}),e.model!==void 0&&(0,l.jsxs)(Q,{icon:`✦`,title:r(`card.badge.modelTitle`,{model:e.model.provider+`/`+e.model.model})+(e.model.reasoningEffort===void 0?``:r(`card.badge.modelEffort`,{effort:e.model.reasoningEffort})),children:[e.model.model,e.model.reasoningEffort===void 0?``:` · ${e.model.reasoningEffort}`]}),e.presetId!==void 0&&(0,l.jsx)(Q,{icon:`🎛`,children:e.presetId}),e.execution.mode===`scheduled`&&(0,l.jsx)(Q,{icon:`⏰`,children:r(`detail.chip.nextRun`,{cron:e.execution.cron??``,time:J(e.execution.nextRunAt)})}),e.execution.queuedRunAt!==void 0&&(0,l.jsx)(Q,{icon:`⏳`,tone:`urgent`,children:r(`detail.chip.queuedRun`,{time:J(e.execution.queuedRunAt)})}),e.blocked&&(0,l.jsx)(Q,{icon:`⛔`,tone:`urgent`,children:r(`shared.blocked`)}),e.checklist!==void 0&&e.checklist.length>0&&(0,l.jsx)(Q,{icon:`☑`,tone:e.status===`in_review`&&e.checklist.some(e=>!e.checked)?`urgent`:void 0,children:r(`detail.chip.checklist`,{done:N(e).done,total:e.checklist.length})}),e.branch!==void 0&&(0,l.jsxs)(Q,{icon:`🌿`,tone:void 0,children:[`Worktree · `,e.branch.length>28?`${e.branch.slice(0,28)}…`:e.branch]}),(e.isolation===void 0||e.isolation===`worktree`)&&e.branch===void 0&&(0,l.jsx)(Q,{icon:`🌿`,children:r(`detail.chip.isolated`)}),e.permission===`read-only`&&(0,l.jsx)(Q,{icon:`🔒`,tone:`urgent`,children:r(`detail.chip.permReadOnly`)}),e.permission===`danger-full-access`&&(0,l.jsx)(Q,{icon:`⚡`,tone:`urgent`,children:r(`detail.chip.permFull`)}),(e.permission===`workspace-write`||e.permission===void 0)&&(0,l.jsx)(Q,{icon:`📁`,children:r(`detail.chip.permWrite`)}),E!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip2 dsh-atb-chip-btn`,"data-tone":D?`urgent`:void 0,title:r(`detail.chip.holderTitle`,{id:E}),onClick:()=>R(E),children:[(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:D?`⏱`:`🤖`}),r(D?`detail.chip.holderStale`:`detail.chip.holderBy`),Z(E),r(`detail.chip.holderSuffix`)]}),e.trashedAt!==void 0&&(0,l.jsx)(Q,{icon:`🗑`,tone:`urgent`,children:r(`detail.chip.trashed`)}),(0,l.jsxs)(Q,{children:[`v`,e.version]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-sub`,children:r(`detail.sub.line`,{time:J(e.updatedAt),who:e.updatedBy.kind===`agent`?`🤖 ${Z(e.updatedBy.sessionId)}`:e.updatedBy.kind===`system`?r(`detail.updatedBy.system`):r(`detail.updatedBy.user`)})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-topbtns`,children:[k!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-session`,title:r(`detail.session.jumpTitle`,{id:k}),onClick:()=>R(k),children:r(`detail.session.jump`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,onClick:()=>t.openEditor(e.id),children:r(`detail.action.edit`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.duplicateTitle`),disabled:b,onClick:()=>F(()=>t.duplicate(e)),children:r(`detail.action.duplicate`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.saveTplTitle`),disabled:b,onClick:()=>F(async()=>{await t.saveAsTemplate(e)&&S(r(`detail.action.saveTplDone`))}),children:r(`detail.action.saveTpl`)}),w&&e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:r(`detail.action.reuseTitle`),disabled:b,onClick:()=>F(()=>t.run(e.id,!0)),children:r(`detail.action.reuse`)}),w&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:e.model===void 0?r(`detail.action.runTitleDefault`):r(`detail.action.runTitleModel`,{model:e.model.model}),disabled:b,onClick:()=>F(()=>t.run(e.id)),children:r(`detail.action.run`)}),T!==void 0&&(g?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.action.stopConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.cancel(e.id),_(!1)},children:r(`detail.action.stop`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>_(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,"data-danger":`true`,title:r(`detail.action.stopTitle`,{id:T.sessionId??``}),onClick:()=>_(!0),children:r(`detail.action.stopExec`)})),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-close`,"aria-label":r(`shared.close`),onClick:()=>t.select(void 0),children:`✕`})]})]}),e.description.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.description`)}),(0,l.jsx)(`div`,{className:`dsh-atb-desc`,children:(0,l.jsx)(Ie,{text:e.description})})]}),e.prompt.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`prompt`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.prompt`)}),(0,l.jsx)(`div`,{className:`dsh-atb-promptbox`,children:(0,l.jsx)(Ie,{text:e.prompt})})]}),(0,l.jsx)(Ue,{task:e,controller:t}),(0,l.jsx)(He,{task:e}),(0,l.jsx)(Ve,{task:e,controller:t}),P!==void 0&&(0,l.jsxs)(`div`,{role:`status`,className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{children:r(`detail.move.archiveResult`,{n:P.archived.length})}),P.failed.map(e=>(0,l.jsxs)(`span`,{children:[e.sessionId,`: `,e.error]},e.sessionId)),P.unsupported.length>0&&(0,l.jsxs)(`span`,{children:[r(`detail.move.archiveUnsupported`),` `,P.unsupported.join(`, `)]})]}),e.status===`archived`&&A.length>0&&j.archiveSessionsSupported&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:b,onClick:()=>F(()=>t.retryArchiveSessions(e.id)),children:r(`detail.move.archiveRetry`)}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-actions`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-movebtns`,children:[Ne(e).map(n=>n===`done`?f?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,"data-tone":O>0?`bad`:void 0,children:O>0?r(`detail.move.confirmDoneUnchecked`,{n:O}):r(`detail.move.confirmDone`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`done`),p(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>p(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{p(!0),y(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):n===`archived`?v?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.length===1?r(`detail.move.confirmArchiveSessionWithId`,{id:Z(A[0])}):A.length>1?r(`detail.move.confirmArchiveSessionCount`,{n:A.length}):r(`detail.move.confirmArchive`)}),A.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.join(`, `)}),A.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!j.archiveSessionsSupported||b,title:j.archiveSessionsSupported?void 0:r(`detail.move.archiveUnsupported`),onClick:()=>{F(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!0})),y(!1)},children:r(`detail.move.archiveWithSession`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>{F(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!1})),y(!1)},children:r(`detail.move.archiveCardOnly`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`archived`),y(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>y(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{y(!0),p(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>void t.move(e.id,e.version,n),children:r(`detail.move.to`,{status:r(K[n])})},n)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`blocked`,onClick:()=>void t.toggleBlocked(e),children:e.blocked?r(`detail.blocked.unmark`):r(`detail.blocked.mark`)}),E!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`release`,title:r(`detail.release.title`,{id:E}),onClick:()=>void t.move(e.id,e.version,`todo`),children:r(`detail.release.button`)})]})}),(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.comments.title`),e.comments.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.comments.length})]}),e.comments.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:r(`detail.comments.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-commentlist`,children:e.comments.map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-bubble`,"data-from":e.threadId===void 0?`user`:`agent`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-bubble-avatar`,children:e.threadId===void 0?`👤`:`🤖`}),(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-main`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-meta`,children:[(0,l.jsx)(`b`,{children:e.threadId===void 0?r(`detail.comments.user`):`agent ${Z(e.threadId)}`}),(0,l.jsx)(`span`,{children:J(e.createdAt)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-bubble-body`,children:(0,l.jsx)(Ie,{text:Pe(r,e)})})]})]},e.id))}),(0,l.jsxs)(`div`,{className:`dsh-atb-composer`,children:[(0,l.jsx)(`textarea`,{ref:u,className:`dsh-atb-composer-input`,value:i,placeholder:r(`detail.composer.placeholder`),disabled:o,onChange:e=>a(e.target.value),onKeyDown:e=>{(e.ctrlKey||e.metaKey)&&e.key===`Enter`&&i.trim().length>0&&!o&&I()},onPaste:e=>{let t=X(e.clipboardData.files);t.length>0&&(e.preventDefault(),L(t))},onDragOver:e=>{e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);t.length>0&&(e.preventDefault(),L(t))}}),(0,l.jsx)(`input`,{ref:d,type:`file`,accept:Oe,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&L(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o,title:r(`image.add`),onClick:()=>d.current?.click(),children:o?`…`:`🖼`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-composer-send`,disabled:i.trim().length===0||o,onClick:I,children:r(`detail.composer.send`)})]})]}),e.executions.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.exec.title`),(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.executions.length}),e.executionsPruned!==void 0&&e.executionsPruned>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,title:r(`detail.exec.prunedTitle`,{n:e.executionsPruned}),children:r(`detail.exec.pruned`,{n:e.executionsPruned})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-execlist`,children:[...e.executions].reverse().map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-exec-row`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-exec-dot`,"data-outcome":e.outcome}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-trigger`,children:e.trigger===`manual`?r(`detail.exec.trigger.manual`):r(`detail.exec.trigger.scheduled`)}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-outcome`,"data-outcome":e.outcome,children:r(_e[e.outcome]??e.outcome)}),(0,l.jsxs)(`span`,{className:`dsh-atb-exec-time`,children:[J(e.startedAt),e.endedAt!==void 0&&` · ${Fe(e.startedAt,e.endedAt)}`]}),e.sessionId!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-exec-session`,title:r(`detail.exec.openTitle`,{id:e.sessionId}),onClick:()=>R(e.sessionId),children:[`🤖 `,Z(e.sessionId),` ↗`]}),e.error!==void 0&&(0,l.jsxs)(`span`,{className:`dsh-atb-exec-error`,title:e.error,children:[e.error.slice(0,80),e.error.length>80?`…`:``]})]},e.id))})]}),(0,l.jsx)(`div`,{className:`dsh-atb-dangerzone`,children:e.trashedAt===void 0?(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void t.remove(e.id,e.version,!1),children:r(`detail.danger.delete`)}):m?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.danger.purgeConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.remove(e.id,e.version,!0),h(!1)},children:r(`detail.danger.purgeGo`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>h(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>h(!0),children:r(`detail.danger.purge`)})}),ee]})}const Ge=e=>[{name:`goal`,kind:`command`,description:e(`slash.cmd.goal.desc`),hint:e(`slash.cmd.goal.hint`)},{name:`schedule`,kind:`command`,description:e(`slash.cmd.schedule.desc`),hint:e(`slash.cmd.schedule.hint`)},{name:`plan`,kind:`command`,description:e(`slash.cmd.plan.desc`)},{name:`browser`,kind:`command`,description:e(`slash.cmd.browser.desc`)},{name:`grill-me`,kind:`command`,description:e(`slash.cmd.grill-me.desc`)},{name:`teamwork-preview`,kind:`command`,description:e(`slash.cmd.teamwork-preview.desc`)},{name:`learn`,kind:`command`,description:e(`slash.cmd.learn.desc`)},{name:`review`,kind:`command`,description:e(`slash.cmd.review.desc`)},{name:`security`,kind:`command`,description:e(`slash.cmd.security.desc`)},{name:`permission`,kind:`command`,description:e(`slash.cmd.permission.desc`),hint:e(`slash.cmd.permission.hint`)}],Ke=e=>[{name:`frontend-ui-engineering`,kind:`skill`,description:e(`slash.skill.frontend-ui-engineering`)},{name:`api-and-interface-design`,kind:`skill`,description:e(`slash.skill.api-and-interface-design`)},{name:`test-driven-development`,kind:`skill`,description:e(`slash.skill.test-driven-development`)},{name:`debugging-and-error-recovery`,kind:`skill`,description:e(`slash.skill.debugging-and-error-recovery`)},{name:`performance-optimization`,kind:`skill`,description:e(`slash.skill.performance-optimization`)},{name:`ci-cd-and-automation`,kind:`skill`,description:e(`slash.skill.ci-cd-and-automation`)},{name:`code-review-and-quality`,kind:`skill`,description:e(`slash.skill.code-review-and-quality`)},{name:`code-simplification`,kind:`skill`,description:e(`slash.skill.code-simplification`)},{name:`context-engineering`,kind:`skill`,description:e(`slash.skill.context-engineering`)},{name:`doubt-driven-development`,kind:`skill`,description:e(`slash.skill.doubt-driven-development`)},{name:`git-workflow-and-versioning`,kind:`skill`,description:e(`slash.skill.git-workflow-and-versioning`)},{name:`idea-refine`,kind:`skill`,description:e(`slash.skill.idea-refine`)},{name:`incremental-implementation`,kind:`skill`,description:e(`slash.skill.incremental-implementation`)},{name:`interview-me`,kind:`skill`,description:e(`slash.skill.interview-me`)},{name:`memory-leak-debugging`,kind:`skill`,description:e(`slash.skill.memory-leak-debugging`)},{name:`observability-and-instrumentation`,kind:`skill`,description:e(`slash.skill.observability-and-instrumentation`)},{name:`planning-and-task-breakdown`,kind:`skill`,description:e(`slash.skill.planning-and-task-breakdown`)},{name:`security-and-hardening`,kind:`skill`,description:e(`slash.skill.security-and-hardening`)},{name:`shipping-and-launch`,kind:`skill`,description:e(`slash.skill.shipping-and-launch`)},{name:`source-driven-development`,kind:`skill`,description:e(`slash.skill.source-driven-development`)},{name:`spec-driven-development`,kind:`skill`,description:e(`slash.skill.spec-driven-development`)},{name:`using-agent-skills`,kind:`skill`,description:e(`slash.skill.using-agent-skills`)}];function qe({value:e,onChange:t,controller:n,placeholder:r,rows:i=4,maxLength:a=8e3,disabled:o=!1,autoFocus:s=!1,className:d,ariaLabel:f,allowImages:p=!1,onUploadingChange:m}){let h=G(),g=(0,c.useRef)(null),_=(0,c.useRef)(null),v=(0,c.useRef)(null),y=(0,c.useRef)(null),[b,x]=(0,c.useState)({}),[S,ee]=(0,c.useState)(void 0),C=(0,c.useMemo)(()=>{let e=(e,t)=>{let n=new Map;for(let t of e)n.set(t.name,t);for(let e of t??[])n.set(e.name,e);return Array.from(n.values())};return{commands:e(Ge(h),S?.commands),skills:e(Ke(h),S?.skills)}},[h,S]),[w,T]=(0,c.useState)(!1),[E,D]=(0,c.useState)(``),[O,te]=(0,c.useState)(-1),[k,A]=(0,c.useState)(0),[j,M]=(0,c.useState)(!1),N=async r=>{let i=X(r);if(!p||n===void 0||i.length===0||j)return;M(!0),m?.(!0);let a=g.current,o=a?.value??e,s=a?.selectionStart??o.length,c=a?.selectionEnd??s,l=!1;try{for(let e of i){let t=await n.uploadImage(e);if(t===void 0)continue;let r=Me(o,s,c,je(t,Ae(e.name,h(`image.defaultAlt`))));o=r.value,s=r.cursor,c=s,l=!0}l&&(t(o),setTimeout(()=>{g.current?.focus(),g.current?.setSelectionRange(s,s)},0))}finally{M(!1),m?.(!1)}};(0,c.useEffect)(()=>{if(n===void 0)return;let e=!0;return n.fetchPromptCompletions().then(t=>{!e||t===void 0||ee({commands:t.commands.map(e=>({...e,kind:`command`})),skills:t.skills.map(e=>({...e,kind:`skill`}))})}),()=>{e=!1}},[n]);let P=(0,c.useMemo)(()=>{let e=E.toLowerCase().trim(),t=[...C.commands,...C.skills];return e.length===0?t:t.filter(t=>t.name.toLowerCase().includes(e)||t.description!==void 0&&t.description.toLowerCase().includes(e))},[C,E]);(0,c.useEffect)(()=>{k>=P.length&&A(Math.max(0,P.length-1))},[P.length,k]),(0,c.useLayoutEffect)(()=>{if(!w)return;let e=v.current,t=e?.children[k];if(e===null||!(t instanceof HTMLElement))return;let n=e.getBoundingClientRect(),r=t.getBoundingClientRect();r.top<n.top?e.scrollTop-=n.top-r.top:r.bottom>n.bottom&&(e.scrollTop+=r.bottom-n.bottom)},[w,k,P]);let F=()=>{let e=g.current;if(e===null)return;let t=e.selectionStart,n=e.value.slice(0,t),r=n.lastIndexOf(`/`);if(r>=0){let e=r>0?n[r-1]??` +`:``}`;return{value:i+o+a,cursor:i.length+o.length}}function X(e){return Array.from(e).filter(e=>ke.has(e.type)).slice(0,10)}function Ne(e){return[`backlog`,`todo`,`in_progress`,`in_review`,`done`,`canceled`,`archived`].filter(t=>x(e.status,t))}function Z(e){return e===void 0?``:e.replace(/^session-(taskboard-)?/,``).slice(0,8)}function Pe(e,t){if(t.systemKey===void 0)return t.body;if(t.systemRows!==void 0){let n=t.systemRows.map(t=>{let n=t.repo===``?e(`iso.repo.root`):t.repo,r=t.outcome===`merged`?`✓`:t.outcome===`noop`?`⟲`:`✗`;return t.outcome===`failed`&&t.error!==void 0?`${n} ${r} ${t.error}`:`${n} ${r}`}).join(` · `);return e(t.systemKey,{summary:n})}return e(t.systemKey,t.systemParams)}function Fe(e,t){if(e===void 0||t===void 0)return``;let n=Math.max(0,Math.round((t-e)/1e3));return n<60?`${n}s`:n<3600?`${Math.floor(n/60)}m${n%60}s`:`${Math.floor(n/3600)}h${Math.floor(n%3600/60)}m`}function Q({icon:e,children:t,tone:n,title:r}){return(0,l.jsxs)(`span`,{className:`dsh-atb-chip2`,"data-tone":n,title:r,children:[e!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:e}),t]})}function Ie({text:e}){let t=W(),[n,r]=(0,c.useState)(null),i=/!\[(.*?)\]\(((?:data:image\/[^)]+)|(?:https?:\/\/[^)]+)|(?:[^)]+\.(?:png|jpg|jpeg|gif|webp|svg)))\)/gi,a=[],o=0,s,u=0;for(;(s=i.exec(e))!==null;){s.index>o&&a.push((0,l.jsx)(`span`,{children:e.slice(o,s.index)},`txt-${o}`));let n=s[1]||t(`md.imageAlt`,{n:++u}),c=s[2]??``;a.push((0,l.jsxs)(`div`,{className:`dsh-atb-detail-img-wrap`,children:[(0,l.jsx)(`img`,{src:c,alt:n,className:`dsh-atb-detail-img`,onClick:()=>r(c),title:t(`md.imageTitle`,{alt:n})}),(0,l.jsx)(`span`,{className:`dsh-atb-detail-img-caption`,children:n})]},`img-${s.index}`)),o=i.lastIndex}return o<e.length&&a.push((0,l.jsx)(`span`,{children:e.slice(o)},`txt-${o}`)),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-markdown-body`,children:a}),n!==null&&(0,l.jsx)(`div`,{className:`dsh-atb-lightbox-backdrop`,onClick:()=>r(null),children:(0,l.jsxs)(`div`,{className:`dsh-atb-lightbox-content`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`img`,{src:n,alt:t(`md.lightboxAlt`),className:`dsh-atb-lightbox-img`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-lightbox-close`,title:t(`md.closePreview`),onClick:()=>r(null),children:`✕`})]})})]})}function Le(e){return[...e.executions].reverse().find(e=>e.isolation!==void 0||e.worktreePath!==void 0||e.isolationNote!==void 0)}function Re(e){return e===void 0?``:e.slice(0,8)}function ze(e){let t=e.slice(3),n=t.indexOf(` -> `);return n>=0&&(t=t.slice(n+4)),t.startsWith(`"`)&&t.endsWith(`"`)&&t.length>1&&(t=t.slice(1,-1)),t}function Be({controller:e,task:t,execution:n,commit:r,path:i,repo:a}){let o=W(),[s,u]=(0,c.useState)({loading:!0});return(0,c.useEffect)(()=>{let o=!0;return u({loading:!0}),e.fetchDiff(t.id,{execution:n.id,...r===void 0?{path:i??``}:{commit:r},...a===void 0?{}:{repo:a}}).then(e=>{o&&u(e===void 0?{loading:!1,failed:!0}:{loading:!1,diff:e.diff,truncated:e.truncated})}),()=>{o=!1}},[e,t.id,n.id,r,i,a]),(0,l.jsxs)(`div`,{className:`dsh-atb-diffview`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diffview-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-diffview-title`,children:r===void 0?o(`diff.file`,{path:i??``}):o(`diff.commit`,{hash:Re(r)})}),s.loading&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`shared.loading`)}),s.truncated===!0&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`diff.truncated`)})]}),s.failed===!0?(0,l.jsx)(`div`,{className:`dsh-atb-diffview-error`,children:o(`diff.failed`)}):(0,l.jsx)(`pre`,{className:`dsh-atb-diffview-pre`,children:s.diff??``})]})}function Ve({task:e,controller:t}){let n=W(),r=e.checklist??[];if(r.length===0)return null;let{done:i,total:a}=ne(e),o=a-i,s=e.status===`in_review`;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`checklist`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[n(`checklist.title`),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-progress`,"data-tone":s&&o>0?`bad`:void 0,children:[`☑ `,i,`/`,a,s&&o>0?n(`checklist.unchecked`,{n:o}):i===a?n(`checklist.allDone`):``]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-cl-items`,children:r.map(r=>(0,l.jsxs)(`label`,{className:`dsh-atb-cl-item`,"data-checked":r.checked?`true`:void 0,"data-alert":s&&!r.checked?`true`:void 0,children:[(0,l.jsx)(`input`,{type:`checkbox`,checked:r.checked,onChange:()=>void t.toggleChecklistItem(e,r.id)}),(0,l.jsx)(`span`,{className:`dsh-atb-cl-text`,children:r.text}),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-meta`,children:[r.checked?`${r.checkedBy===`user`?n(`checklist.byUser`):`🤖 ${Z(r.checkedBy)}`} · ${J(r.checkedAt)}`:n(`checklist.uncheckedItem`),r.note!==void 0&&r.note.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cl-note`,title:r.note,children:n(`checklist.evidence`,{note:r.note})})]})]},r.id))})]})}function He({task:e}){let t=W(),n=[...e.executions].reverse().find(e=>e.report!==void 0),r=n?.report;if(n===void 0||r===void 0)return null;let i=(e,t)=>t!==void 0&&t.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:e}),(0,l.jsx)(`ul`,{className:`dsh-atb-rpt-list`,children:t.map((e,t)=>(0,l.jsx)(`li`,{children:e},t))})]}):null;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`report`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[t(`report.title`),(0,l.jsx)(`span`,{className:`dsh-atb-cl-progress`,children:t(`report.submitted`,{time:J(n.endedAt??n.startedAt)})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-summary`,children:r.summary}),i(t(`report.changedFiles`),r.changedFiles),i(t(`report.checks`),r.checks),i(t(`report.artifacts`),r.artifacts),r.risk.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:t(`report.risk`)}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-risk`,children:r.risk})]})]})}function Ue({task:e,controller:t}){let n=W(),{alert:r,el:i}=Y(),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(null),[d,f]=(0,c.useState)(!1),[p,m]=(0,c.useState)(null),[h,g]=(0,c.useState)(!1),_=Le(e),v=e.executions.some(e=>e.outcome===`running`);if(_===void 0)return null;let y=()=>{f(!0),t.mergeBranch(e.id).then(e=>{if(f(!1),o(!1),!e.ok)r(n(`iso.merge.failed`,{error:e.error}));else if(e.results!==void 0){let t=e=>e===``?n(`iso.repo.root`):e,i=e.results.map(e=>`${t(e.repo)} ${e.outcome===`merged`?`✓`:e.outcome===`noop`?`⟲`:`✗`}`).join(` · `),a=e.results.some(e=>e.outcome===`failed`),o=e.results.find(e=>e.outcome===`failed`)?.error;r(a?n(`iso.merge.partial`,{summary:i,error:o??``}):n(`iso.merge.done`,{summary:i}))}else e.noop===!0&&r(n(`iso.merge.noop`))})},b=i=>{f(!0),t.removeWorktree(e.id,i).then(e=>{f(!1),u(null),e.ok?e.branchError!==void 0&&r(n(`iso.remove.branchFailed`,{error:e.branchError})):r(n(`iso.remove.failed`,{error:e.error}))})};if(_.isolation!==`worktree`||_.worktreePath===void 0)return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.title`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-none`,children:[n(`iso.none`),_.isolationNote===void 0?``:` · ${_.isolationNote}`]}),i]});let x=_.repos!==void 0&&_.repos.length>0,S=x?(_.repos??[]).map(e=>({repo:e.repo,label:e.repo===``?n(`iso.repo.root`):e.repo,branch:e.branch,worktreePath:e.worktreePath,baseCommit:e.baseCommit,headCommit:e.headCommit,commits:e.commits??[],commitTotal:e.commitsTotal??(e.commits??[]).length,dirty:e.dirtyFiles??[],dirtyTotal:e.dirtyFilesTotal??(e.dirtyFiles??[]).length,...e.diffStat===void 0?{}:{diffStat:e.diffStat},...e.changedFiles===void 0?{}:{changedFiles:e.changedFiles}})):[{label:n(`iso.repo.root`),branch:_.branch??e.branch,worktreePath:_.worktreePath,baseCommit:_.baseCommit,headCommit:_.headCommit,commits:_.commits??[],commitTotal:_.commitsTotal??(_.commits??[]).length,dirty:_.dirtyFiles??[],dirtyTotal:_.dirtyFilesTotal??(_.dirtyFiles??[]).length,..._.diffStat===void 0?{}:{diffStat:_.diffStat},..._.changedFiles===void 0?{}:{changedFiles:_.changedFiles}}];return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.worktreeTitle`)}),S.map(r=>(0,l.jsxs)(`section`,{className:`dsh-atb-iso-repo`,"data-multi":x?`true`:void 0,children:[x&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-repohead`,title:r.worktreePath,children:[(0,l.jsx)(`code`,{children:r.label}),(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-facts`,children:[!x&&(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,title:r.worktreePath,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]}),(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.baseline`,{base:Re(r.baseCommit),head:Re(r.headCommit)})}),r.changedFiles!==void 0&&r.changedFiles>0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.changed`,{n:r.changedFiles})}),r.diffStat!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,title:r.diffStat,children:r.diffStat})]}),r.commits.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commits`,children:[r.commits.slice(0,10).map(i=>(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commit`,"data-open":p?.commit===i.hash&&p?.repo===r.repo?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-commit-btn`,title:n(`iso.commit.openTitle`),onClick:()=>m(p?.commit===i.hash&&p?.repo===r.repo?null:{commit:i.hash,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:Re(i.hash)}),(0,l.jsx)(`span`,{children:i.subject})]}),p?.commit===i.hash&&p?.repo===r.repo&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,commit:i.hash,repo:r.repo})]},i.hash)),r.commitTotal>10&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.commits.more`,{n:r.commitTotal})})]}):(0,l.jsx)(`div`,{className:`dsh-atb-iso-nocommit`,children:n(`iso.nocommit`)}),r.dirtyTotal>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-toggle`,onClick:()=>g(!h),children:[n(`iso.dirty.toggle`,{n:r.dirtyTotal}),n(h?`iso.dirty.collapse`:`iso.dirty.expand`)]}),h&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty-files`,children:[r.dirty.slice(0,30).map((e,t)=>{let i=ze(e);return(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-file`,title:n(`iso.dirty.openTitle`),onClick:()=>m(p?.path===i&&p?.repo===r.repo?null:{path:i,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:e.slice(0,2)}),` `,i]},`${e}-${t}`)}),r.dirtyTotal>30&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.dirty.more`,{n:r.dirtyTotal})})]}),p?.path!==void 0&&p?.repo===r.repo&&h&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,path:p.path,repo:r.repo})]})]},r.repo??`root`)),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-actions`,children:[v?(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.running`)}):a?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(`iso.merge.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:d,onClick:y,children:n(`iso.merge.go`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(!1),children:n(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:d,title:n(`iso.merge.title`),onClick:()=>o(!0),children:n(`iso.merge.button`)}),!v&&(s===null?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtTitle`),onClick:()=>u(`wt`),children:n(`iso.remove.wt`)}),e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtbTitle`),onClick:()=>u(`wtb`),children:n(`iso.remove.wtb`)})]}):(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(s===`wtb`?`iso.remove.confirmWtb`:`iso.remove.confirmWt`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,onClick:()=>b(s===`wtb`),children:n(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>u(null),children:n(`shared.cancel`)})]})),!v&&s===null&&!a&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.keep`)})]}),i]})}function We({task:e,controller:t,now:n}){let r=W(),[i,a]=(0,c.useState)(``),[o,s]=(0,c.useState)(!1),u=(0,c.useRef)(null),d=(0,c.useRef)(null),[f,p]=(0,c.useState)(!1),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(!1),[v,y]=(0,c.useState)(!1),[b,x]=(0,c.useState)(!1),{alert:S,el:ee}=Y(),te=t.getSnapshot().workspaces.find(t=>t.id===e.workspaceId),C=e.status!==`in_progress`&&e.status!==`done`&&e.status!==`archived`,w=e.executions.find(e=>e.outcome===`running`),T=e.status===`in_progress`?e.claimedBy:void 0,E=n!==void 0&&Ce(e,n),D=(e.checklist??[]).filter(e=>!e.checked).length,O=[...e.executions].reverse().find(e=>e.sessionId!==void 0),k=w?.sessionId??O?.sessionId??(e.claimedBy?.startsWith(`session-`)?e.claimedBy:void 0),A=M(e),j=t.getSnapshot(),N=j.sessionArchive?.taskId===e.id?j.sessionArchive.result:void 0,P=e=>{b||(x(!0),e().catch(()=>void 0).finally(()=>x(!1)))},F=()=>{o||i.trim().length===0||t.comment(e.id,i).then(e=>{e&&a(``)})},I=async e=>{let n=X(e);if(n.length===0||o)return;s(!0);let c=u.current,l=c?.value??i,d=c?.selectionStart??l.length,f=c?.selectionEnd??d,p=!1;try{for(let e of n){let n=await t.uploadImage(e);if(n===void 0)continue;let i=Me(l,d,f,je(n,Ae(e.name,r(`image.defaultAlt`))));l=i.value,d=i.cursor,f=d,p=!0}p&&(a(l),setTimeout(()=>{u.current?.focus(),u.current?.setSelectionRange(d,d)},0))}finally{s(!1)}},L=e=>{t.openSession(e).then(t=>{t===`missing`?S(r(`card.session.missing`,{id:Z(e)})):t===`archived`?S(r(`card.session.archived`,{id:Z(e)})):t===`unavailable`&&S(r(`card.session.unavailable`,{id:e}))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-detail`,"data-urgency":e.urgency,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-head`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlewrap`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlebar`,children:[(0,l.jsx)(`h3`,{children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-statuspill`,"data-status":e.status,children:r(ge[e.status]??e.status)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-chips`,children:[(0,l.jsxs)(Q,{tone:e.urgency,children:[`● `,r(q[e.urgency]??e.urgency)]}),(0,l.jsx)(Q,{icon:`📁`,children:te?.title??Z(e.workspaceId)}),e.model!==void 0&&(0,l.jsxs)(Q,{icon:`✦`,title:r(`card.badge.modelTitle`,{model:e.model.provider+`/`+e.model.model})+(e.model.reasoningEffort===void 0?``:r(`card.badge.modelEffort`,{effort:e.model.reasoningEffort})),children:[e.model.model,e.model.reasoningEffort===void 0?``:` · ${e.model.reasoningEffort}`]}),e.presetId!==void 0&&(0,l.jsx)(Q,{icon:`🎛`,children:e.presetId}),e.execution.mode===`scheduled`&&(0,l.jsx)(Q,{icon:`⏰`,children:r(`detail.chip.nextRun`,{cron:e.execution.cron??``,time:J(e.execution.nextRunAt)})}),e.execution.queuedRunAt!==void 0&&(0,l.jsx)(Q,{icon:`⏳`,tone:`urgent`,children:r(`detail.chip.queuedRun`,{time:J(e.execution.queuedRunAt)})}),e.blocked&&(0,l.jsx)(Q,{icon:`⛔`,tone:`urgent`,children:r(`shared.blocked`)}),e.checklist!==void 0&&e.checklist.length>0&&(0,l.jsx)(Q,{icon:`☑`,tone:e.status===`in_review`&&e.checklist.some(e=>!e.checked)?`urgent`:void 0,children:r(`detail.chip.checklist`,{done:ne(e).done,total:e.checklist.length})}),e.branch!==void 0&&(0,l.jsxs)(Q,{icon:`🌿`,tone:void 0,children:[`Worktree · `,e.branch.length>28?`${e.branch.slice(0,28)}…`:e.branch]}),(e.isolation===void 0||e.isolation===`worktree`)&&e.branch===void 0&&(0,l.jsx)(Q,{icon:`🌿`,children:r(`detail.chip.isolated`)}),e.permission===`read-only`&&(0,l.jsx)(Q,{icon:`🔒`,tone:`urgent`,children:r(`detail.chip.permReadOnly`)}),e.permission===`danger-full-access`&&(0,l.jsx)(Q,{icon:`⚡`,tone:`urgent`,children:r(`detail.chip.permFull`)}),(e.permission===`workspace-write`||e.permission===void 0)&&(0,l.jsx)(Q,{icon:`📁`,children:r(`detail.chip.permWrite`)}),T!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip2 dsh-atb-chip-btn`,"data-tone":E?`urgent`:void 0,title:r(`detail.chip.holderTitle`,{id:T}),onClick:()=>L(T),children:[(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:E?`⏱`:`🤖`}),r(E?`detail.chip.holderStale`:`detail.chip.holderBy`),Z(T),r(`detail.chip.holderSuffix`)]}),e.trashedAt!==void 0&&(0,l.jsx)(Q,{icon:`🗑`,tone:`urgent`,children:r(`detail.chip.trashed`)}),(0,l.jsxs)(Q,{children:[`v`,e.version]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-sub`,children:r(`detail.sub.line`,{time:J(e.updatedAt),who:e.updatedBy.kind===`agent`?`🤖 ${Z(e.updatedBy.sessionId)}`:e.updatedBy.kind===`system`?r(`detail.updatedBy.system`):r(`detail.updatedBy.user`)})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-topbtns`,children:[k!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-session`,title:r(`detail.session.jumpTitle`,{id:k}),onClick:()=>L(k),children:r(`detail.session.jump`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,onClick:()=>t.openEditor(e.id),children:r(`detail.action.edit`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.duplicateTitle`),disabled:b,onClick:()=>P(()=>t.duplicate(e)),children:r(`detail.action.duplicate`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.saveTplTitle`),disabled:b,onClick:()=>P(async()=>{await t.saveAsTemplate(e)&&S(r(`detail.action.saveTplDone`))}),children:r(`detail.action.saveTpl`)}),C&&e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:r(`detail.action.reuseTitle`),disabled:b,onClick:()=>P(()=>t.run(e.id,!0)),children:r(`detail.action.reuse`)}),C&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:e.model===void 0?r(`detail.action.runTitleDefault`):r(`detail.action.runTitleModel`,{model:e.model.model}),disabled:b,onClick:()=>P(()=>t.run(e.id)),children:r(`detail.action.run`)}),w!==void 0&&(g?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.action.stopConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.cancel(e.id),_(!1)},children:r(`detail.action.stop`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>_(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,"data-danger":`true`,title:r(`detail.action.stopTitle`,{id:w.sessionId??``}),onClick:()=>_(!0),children:r(`detail.action.stopExec`)})),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-close`,"aria-label":r(`shared.close`),onClick:()=>t.select(void 0),children:`✕`})]})]}),e.description.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.description`)}),(0,l.jsx)(`div`,{className:`dsh-atb-desc`,children:(0,l.jsx)(Ie,{text:e.description})})]}),e.prompt.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`prompt`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.prompt`)}),(0,l.jsx)(`div`,{className:`dsh-atb-promptbox`,children:(0,l.jsx)(Ie,{text:e.prompt})})]}),(0,l.jsx)(Ue,{task:e,controller:t}),(0,l.jsx)(He,{task:e}),(0,l.jsx)(Ve,{task:e,controller:t}),N!==void 0&&(0,l.jsxs)(`div`,{role:`status`,className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{children:r(`detail.move.archiveResult`,{n:N.archived.length})}),N.failed.map(e=>(0,l.jsxs)(`span`,{children:[e.sessionId,`: `,e.error]},e.sessionId)),N.unsupported.length>0&&(0,l.jsxs)(`span`,{children:[r(`detail.move.archiveUnsupported`),` `,N.unsupported.join(`, `)]})]}),e.status===`archived`&&A.length>0&&j.archiveSessionsSupported&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:b,onClick:()=>P(()=>t.retryArchiveSessions(e.id)),children:r(`detail.move.archiveRetry`)}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-actions`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-movebtns`,children:[Ne(e).map(n=>n===`done`?f?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,"data-tone":D>0?`bad`:void 0,children:D>0?r(`detail.move.confirmDoneUnchecked`,{n:D}):r(`detail.move.confirmDone`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`done`),p(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>p(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{p(!0),y(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):n===`archived`?v?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.length===1?r(`detail.move.confirmArchiveSessionWithId`,{id:Z(A[0])}):A.length>1?r(`detail.move.confirmArchiveSessionCount`,{n:A.length}):r(`detail.move.confirmArchive`)}),A.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.join(`, `)}),A.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!j.archiveSessionsSupported||b,title:j.archiveSessionsSupported?void 0:r(`detail.move.archiveUnsupported`),onClick:()=>{P(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!0})),y(!1)},children:r(`detail.move.archiveWithSession`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>{P(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!1})),y(!1)},children:r(`detail.move.archiveCardOnly`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`archived`),y(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>y(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{y(!0),p(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>void t.move(e.id,e.version,n),children:r(`detail.move.to`,{status:r(K[n])})},n)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`blocked`,onClick:()=>void t.toggleBlocked(e),children:e.blocked?r(`detail.blocked.unmark`):r(`detail.blocked.mark`)}),T!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`release`,title:r(`detail.release.title`,{id:T}),onClick:()=>void t.move(e.id,e.version,`todo`),children:r(`detail.release.button`)})]})}),(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.comments.title`),e.comments.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.comments.length})]}),e.comments.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:r(`detail.comments.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-commentlist`,children:e.comments.map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-bubble`,"data-from":e.threadId===void 0?`user`:`agent`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-bubble-avatar`,children:e.threadId===void 0?`👤`:`🤖`}),(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-main`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-meta`,children:[(0,l.jsx)(`b`,{children:e.threadId===void 0?r(`detail.comments.user`):`agent ${Z(e.threadId)}`}),(0,l.jsx)(`span`,{children:J(e.createdAt)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-bubble-body`,children:(0,l.jsx)(Ie,{text:Pe(r,e)})})]})]},e.id))}),(0,l.jsxs)(`div`,{className:`dsh-atb-composer`,children:[(0,l.jsx)(`textarea`,{ref:u,className:`dsh-atb-composer-input`,value:i,placeholder:r(`detail.composer.placeholder`),disabled:o,onChange:e=>a(e.target.value),onKeyDown:e=>{(e.ctrlKey||e.metaKey)&&e.key===`Enter`&&i.trim().length>0&&!o&&F()},onPaste:e=>{let t=X(e.clipboardData.files);t.length>0&&(e.preventDefault(),I(t))},onDragOver:e=>{e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);t.length>0&&(e.preventDefault(),I(t))}}),(0,l.jsx)(`input`,{ref:d,type:`file`,accept:Oe,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&I(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o,title:r(`image.add`),onClick:()=>d.current?.click(),children:o?`…`:`🖼`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-composer-send`,disabled:i.trim().length===0||o,onClick:F,children:r(`detail.composer.send`)})]})]}),e.executions.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.exec.title`),(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.executions.length}),e.executionsPruned!==void 0&&e.executionsPruned>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,title:r(`detail.exec.prunedTitle`,{n:e.executionsPruned}),children:r(`detail.exec.pruned`,{n:e.executionsPruned})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-execlist`,children:[...e.executions].reverse().map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-exec-row`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-exec-dot`,"data-outcome":e.outcome}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-trigger`,children:e.trigger===`manual`?r(`detail.exec.trigger.manual`):r(`detail.exec.trigger.scheduled`)}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-outcome`,"data-outcome":e.outcome,children:r(_e[e.outcome]??e.outcome)}),(0,l.jsxs)(`span`,{className:`dsh-atb-exec-time`,children:[J(e.startedAt),e.endedAt!==void 0&&` · ${Fe(e.startedAt,e.endedAt)}`]}),e.sessionId!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-exec-session`,title:r(`detail.exec.openTitle`,{id:e.sessionId}),onClick:()=>L(e.sessionId),children:[`🤖 `,Z(e.sessionId),` ↗`]}),e.error!==void 0&&(0,l.jsxs)(`span`,{className:`dsh-atb-exec-error`,title:e.error,children:[e.error.slice(0,80),e.error.length>80?`…`:``]})]},e.id))})]}),(0,l.jsx)(`div`,{className:`dsh-atb-dangerzone`,children:e.trashedAt===void 0?(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void t.remove(e.id,e.version,!1),children:r(`detail.danger.delete`)}):m?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.danger.purgeConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.remove(e.id,e.version,!0),h(!1)},children:r(`detail.danger.purgeGo`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>h(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>h(!0),children:r(`detail.danger.purge`)})}),ee]})}const Ge=e=>[{name:`goal`,kind:`command`,description:e(`slash.cmd.goal.desc`),hint:e(`slash.cmd.goal.hint`)},{name:`schedule`,kind:`command`,description:e(`slash.cmd.schedule.desc`),hint:e(`slash.cmd.schedule.hint`)},{name:`plan`,kind:`command`,description:e(`slash.cmd.plan.desc`)},{name:`browser`,kind:`command`,description:e(`slash.cmd.browser.desc`)},{name:`grill-me`,kind:`command`,description:e(`slash.cmd.grill-me.desc`)},{name:`teamwork-preview`,kind:`command`,description:e(`slash.cmd.teamwork-preview.desc`)},{name:`learn`,kind:`command`,description:e(`slash.cmd.learn.desc`)},{name:`review`,kind:`command`,description:e(`slash.cmd.review.desc`)},{name:`security`,kind:`command`,description:e(`slash.cmd.security.desc`)},{name:`permission`,kind:`command`,description:e(`slash.cmd.permission.desc`),hint:e(`slash.cmd.permission.hint`)}],Ke=e=>[{name:`frontend-ui-engineering`,kind:`skill`,description:e(`slash.skill.frontend-ui-engineering`)},{name:`api-and-interface-design`,kind:`skill`,description:e(`slash.skill.api-and-interface-design`)},{name:`test-driven-development`,kind:`skill`,description:e(`slash.skill.test-driven-development`)},{name:`debugging-and-error-recovery`,kind:`skill`,description:e(`slash.skill.debugging-and-error-recovery`)},{name:`performance-optimization`,kind:`skill`,description:e(`slash.skill.performance-optimization`)},{name:`ci-cd-and-automation`,kind:`skill`,description:e(`slash.skill.ci-cd-and-automation`)},{name:`code-review-and-quality`,kind:`skill`,description:e(`slash.skill.code-review-and-quality`)},{name:`code-simplification`,kind:`skill`,description:e(`slash.skill.code-simplification`)},{name:`context-engineering`,kind:`skill`,description:e(`slash.skill.context-engineering`)},{name:`doubt-driven-development`,kind:`skill`,description:e(`slash.skill.doubt-driven-development`)},{name:`git-workflow-and-versioning`,kind:`skill`,description:e(`slash.skill.git-workflow-and-versioning`)},{name:`idea-refine`,kind:`skill`,description:e(`slash.skill.idea-refine`)},{name:`incremental-implementation`,kind:`skill`,description:e(`slash.skill.incremental-implementation`)},{name:`interview-me`,kind:`skill`,description:e(`slash.skill.interview-me`)},{name:`memory-leak-debugging`,kind:`skill`,description:e(`slash.skill.memory-leak-debugging`)},{name:`observability-and-instrumentation`,kind:`skill`,description:e(`slash.skill.observability-and-instrumentation`)},{name:`planning-and-task-breakdown`,kind:`skill`,description:e(`slash.skill.planning-and-task-breakdown`)},{name:`security-and-hardening`,kind:`skill`,description:e(`slash.skill.security-and-hardening`)},{name:`shipping-and-launch`,kind:`skill`,description:e(`slash.skill.shipping-and-launch`)},{name:`source-driven-development`,kind:`skill`,description:e(`slash.skill.source-driven-development`)},{name:`spec-driven-development`,kind:`skill`,description:e(`slash.skill.spec-driven-development`)},{name:`using-agent-skills`,kind:`skill`,description:e(`slash.skill.using-agent-skills`)}];function qe({value:e,onChange:t,controller:n,placeholder:r,rows:i=4,maxLength:a=8e3,disabled:o=!1,autoFocus:s=!1,className:d,ariaLabel:f,allowImages:p=!1,onUploadingChange:m}){let h=W(),g=(0,c.useRef)(null),_=(0,c.useRef)(null),v=(0,c.useRef)(null),y=(0,c.useRef)(null),[b,x]=(0,c.useState)({}),[S,ee]=(0,c.useState)(void 0),te=(0,c.useMemo)(()=>{let e=(e,t)=>{let n=new Map;for(let t of e)n.set(t.name,t);for(let e of t??[])n.set(e.name,e);return Array.from(n.values())};return{commands:e(Ge(h),S?.commands),skills:e(Ke(h),S?.skills)}},[h,S]),[C,w]=(0,c.useState)(!1),[T,E]=(0,c.useState)(``),[D,O]=(0,c.useState)(-1),[k,A]=(0,c.useState)(0),[j,M]=(0,c.useState)(!1),ne=async r=>{let i=X(r);if(!p||n===void 0||i.length===0||j)return;M(!0),m?.(!0);let a=g.current,o=a?.value??e,s=a?.selectionStart??o.length,c=a?.selectionEnd??s,l=!1;try{for(let e of i){let t=await n.uploadImage(e);if(t===void 0)continue;let r=Me(o,s,c,je(t,Ae(e.name,h(`image.defaultAlt`))));o=r.value,s=r.cursor,c=s,l=!0}l&&(t(o),setTimeout(()=>{g.current?.focus(),g.current?.setSelectionRange(s,s)},0))}finally{M(!1),m?.(!1)}};(0,c.useEffect)(()=>{if(n===void 0)return;let e=!0;return n.fetchPromptCompletions().then(t=>{!e||t===void 0||ee({commands:t.commands.map(e=>({...e,kind:`command`})),skills:t.skills.map(e=>({...e,kind:`skill`}))})}),()=>{e=!1}},[n]);let N=(0,c.useMemo)(()=>{let e=T.toLowerCase().trim(),t=[...te.commands,...te.skills];return e.length===0?t:t.filter(t=>t.name.toLowerCase().includes(e)||t.description!==void 0&&t.description.toLowerCase().includes(e))},[te,T]);(0,c.useEffect)(()=>{k>=N.length&&A(Math.max(0,N.length-1))},[N.length,k]),(0,c.useLayoutEffect)(()=>{if(!C)return;let e=v.current,t=e?.children[k];if(e===null||!(t instanceof HTMLElement))return;let n=e.getBoundingClientRect(),r=t.getBoundingClientRect();r.top<n.top?e.scrollTop-=n.top-r.top:r.bottom>n.bottom&&(e.scrollTop+=r.bottom-n.bottom)},[C,k,N]);let P=()=>{let e=g.current;if(e===null)return;let t=e.selectionStart,n=e.value.slice(0,t),r=n.lastIndexOf(`/`);if(r>=0){let e=r>0?n[r-1]??` `:` -`,t=/\s/.test(e)||r===0,i=n.slice(r+1),a=!/\s/.test(i);if(t&&a){te(r),D(i),T(!0);return}}T(!1)},I=n=>{let r=g.current;if(r===null||O<0)return;let i=r.selectionStart,a=e.slice(0,O),o=e.slice(i),s=`/${n.name} `;t(a+s+o),T(!1),setTimeout(()=>{if(g.current!==null){let e=O+s.length;g.current.focus(),g.current.setSelectionRange(e,e)}},0)},L=e=>{if(w&&P.length>0){if(e.key===`ArrowDown`){e.preventDefault(),A(e=>(e+1)%P.length);return}if(e.key===`ArrowUp`){e.preventDefault(),A(e=>(e-1+P.length)%P.length);return}if(e.key===`Enter`||e.key===`Tab`){let t=P[k];if(t!==void 0){e.preventDefault(),I(t);return}}if(e.key===`Escape`){e.preventDefault(),T(!1);return}}},R=(0,c.useCallback)(()=>{let e=g.current;if(e===null)return;let t=e.getBoundingClientRect(),n=window.innerHeight,r=_.current?.offsetHeight??0,i=r>0?r:240,a=t.top-6-8,o=n-8-(t.bottom+6),s=o>a,c=Math.min(i,Math.max(s?o:a,120)),l=s?t.bottom+6:t.top-6-c;x(e=>e.left===t.left&&e.top===l&&e.width===t.width&&e.maxHeight===c?e:{position:`fixed`,left:t.left,top:l,width:t.width,maxHeight:c,zIndex:100})},[]);return(0,c.useLayoutEffect)(()=>{w&&R()}),(0,c.useEffect)(()=>{if(w)return window.addEventListener(`scroll`,R,!0),window.addEventListener(`resize`,R),()=>{window.removeEventListener(`scroll`,R,!0),window.removeEventListener(`resize`,R)}},[w,R]),(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-wrap ${d??``}`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-inner`,children:[(0,l.jsx)(`textarea`,{ref:g,className:`dsh-atb-prompt-input`,value:e,rows:i,maxLength:a,disabled:o||j,autoFocus:s,placeholder:r,"aria-label":f,onChange:e=>{t(e.target.value),F()},onKeyUp:F,onClick:F,onKeyDown:L,onPaste:e=>{let t=X(e.clipboardData.files);p&&t.length>0&&(e.preventDefault(),N(t))},onDragOver:e=>{p&&e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);p&&t.length>0&&(e.preventDefault(),N(t))}}),w&&P.length>0&&(0,u.createPortal)((0,l.jsxs)(`div`,{ref:_,className:`dsh-atb-slash-popup`,style:b,role:`listbox`,"aria-label":h(`slash.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-slash-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-title`,children:h(`slash.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-slash-hint`,children:h(`slash.hint`)})]}),(0,l.jsx)(`div`,{ref:v,className:`dsh-atb-slash-list`,children:P.map((e,t)=>(0,l.jsxs)(`div`,{role:`option`,"aria-selected":t===k,className:`dsh-atb-slash-item`,"data-active":t===k?`true`:void 0,"data-kind":e.kind,onClick:()=>I(e),onMouseEnter:()=>A(t),children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-badge`,"data-kind":e.kind,children:e.kind===`command`?h(`slash.badge.command`):h(`slash.badge.skill`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-slash-name`,children:[`/`,e.name]}),e.hint&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-param`,children:e.hint}),e.description&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-desc`,children:e.description})]},`${e.kind}-${e.name}`))})]}),document.body)]}),p&&(0,l.jsxs)(`div`,{className:`dsh-atb-image-actions`,children:[(0,l.jsx)(`input`,{ref:y,type:`file`,accept:`image/png,image/jpeg,image/gif,image/webp`,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&N(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o||j,onClick:()=>y.current?.click(),children:h(j?`image.uploading`:`image.add`)}),(0,l.jsx)(`span`,{className:`dsh-atb-image-hint`,children:h(`image.hint`)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-prompt-foot`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-prompt-tip`,children:[h(`slash.tipA`),` `,(0,l.jsx)(`code`,{children:`/`}),` `,h(`slash.tipB`)]})})]})}const Je=`dsh-taskboard-last-model-v1`;function Ye(){try{let e=localStorage.getItem(Je);if(e===null)return;let t=JSON.parse(e);if(typeof t==`object`&&t){let{provider:e,model:n,reasoningEffort:r}=t;if(typeof e==`string`&&typeof n==`string`&&e.trim().length>0&&n.trim().length>0)return{provider:e.trim(),model:n.trim(),...typeof r==`string`&&r.trim().length>0?{reasoningEffort:r.trim()}:{}}}return}catch{return}}function Xe(e){try{e===void 0?localStorage.removeItem(Je):localStorage.setItem(Je,JSON.stringify(e))}catch{}}const Ze=e=>[{value:`urgent`,label:e(`form.urgency.urgent`)},{value:`normal`,label:e(`form.urgency.normal`)},{value:`relaxed`,label:e(`form.urgency.relaxed`)}],Qe=e=>[{label:e(`form.cron.daily`),cron:`0 9 * * *`},{label:e(`form.cron.hourly`),cron:`0 * * * *`},{label:e(`form.cron.every10min`),cron:`*/10 * * * *`},{label:e(`form.cron.weekly`),cron:`0 9 * * 1`}],$e=e=>[{value:`workspace-write`,label:e(`form.perm.write`),icon:`📁`},{value:`read-only`,label:e(`form.perm.readOnly`),icon:`🔒`},{value:`danger-full-access`,label:e(`form.perm.fullAccess`),icon:`⚡`}];function $({label:e,required:t=!1,full:n=!1,children:r}){return(0,l.jsxs)(`label`,{className:`dsh-atb-field`,"data-span":n?`full`:void 0,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-field-label`,children:[e,t&&(0,l.jsx)(`em`,{className:`dsh-atb-req`,children:`*`})]}),r]})}function et({rows:e,onChange:t,editing:n}){let r=G(),i=(n,r)=>{t(e.map((e,t)=>t===n?{...e,...r}:e))},a=e.filter(e=>e.checked).length;return(0,l.jsxs)(`div`,{className:`dsh-atb-cke`,children:[e.map((a,o)=>(0,l.jsxs)(`div`,{className:`dsh-atb-cke-row`,children:[n&&(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-cke-box`,checked:a.checked,title:r(`form.check.checkedTitle`,{who:a.checkedBy??r(`form.check.notCheckedYet`)}),onChange:e=>i(o,{checked:e.target.checked})}),(0,l.jsx)(`input`,{className:`dsh-atb-cke-text`,value:a.text,maxLength:200,placeholder:r(`form.check.itemPlaceholder`,{n:o+1}),spellCheck:!1,onChange:e=>i(o,{text:e.target.value})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-del`,title:r(`form.check.removeTitle`),onClick:()=>t(e.filter((e,t)=>t!==o)),children:`✕`})]},a.id??`new-${o}`)),e.length<30&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-add`,onClick:()=>t([...e,{text:``,checked:!1}]),children:r(`form.check.add`)}),e.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cke-hint`,children:n?r(`form.check.hintEdit`,{checked:a,total:e.length}):r(`form.check.hintCreate`,{n:e.length})})]})}function tt({controller:e,task:t}){let n=G(),r=e.getSnapshot(),i=r.templatePrefill,a=t!==void 0,[o,s]=(0,c.useState)(t?.title??i?.title??``),[u,d]=(0,c.useState)(t?.description??i?.description??``),[f,p]=(0,c.useState)(t?.prompt??i?.prompt??``),[m,h]=(0,c.useState)(t?.workspaceId??r.filters.workspaceId??r.workspaces[0]?.id??``),[g,_]=(0,c.useState)(t?.urgency??(i?.urgency===`urgent`||i?.urgency===`relaxed`?i.urgency:`normal`)),v=t?.execution??i?.execution,[y,b]=(0,c.useState)(v?.mode===`scheduled`?v.cron===void 0?`once`:`periodic`:`claim`),[x,C]=(0,c.useState)(t?.execution.cron??i?.execution?.cron??`0 9 * * *`),[T,E]=(0,c.useState)(t?.execution.periodicCompletion??i?.execution?.periodicCompletion??`spawn`),[D,O]=(0,c.useState)(t?.execution.reuseSessionId===void 0?t===void 0?i?.execution?.reuseSessionId===void 0?i?.execution?.sessionReuseMode??`fresh`:`selected:${i.execution.reuseSessionId}`:t.execution.sessionReuseMode??`reuse`:`selected:${t.execution.reuseSessionId}`),[k,j]=(0,c.useState)([]),[M,N]=(0,c.useState)(!1),[P,F]=(0,c.useState)(!1),I=v?.mode===`scheduled`&&v.runAt!==void 0?v.runAt:void 0,[L,R]=(0,c.useState)(()=>{if(I===void 0)return``;let e=new Date(I);if(Number.isNaN(e.getTime()))return``;let t=e=>String(e).padStart(2,`0`);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`}),[z,B]=(0,c.useState)([]),V=t?.model??i?.model??(a?void 0:Ye()),[H,ne]=(0,c.useState)(V===void 0?``:JSON.stringify({provider:V.provider,model:V.model})),[re,ie]=(0,c.useState)(V?.reasoningEffort??``),ae=t?.presetId??i?.presetId??``,[oe,se]=(0,c.useState)(ae),[U,ce]=(0,c.useState)([]),[W,le]=(0,c.useState)(void 0),[ue,de]=(0,c.useState)(t?.permission??(i?.permission?S(i.permission):w(r.ledger.settings))),[fe,pe]=(0,c.useState)(t?.isolation??(i?.isolation===`none`?`none`:i?.isolation===`worktree`?`worktree`:ee(r.ledger.settings))),[me,he]=(0,c.useState)(t?.checklist!==void 0&&t.checklist.length>0?t.checklist.map(e=>({...e})):(i?.checklist??[]).map(e=>({text:e,checked:!1}))),ge=(0,c.useRef)(null),[K,q]=(0,c.useState)(!1),[_e,ve]=(0,c.useState)(!1);(0,c.useEffect)(()=>{ge.current?.focus();let t=t=>{t.key===`Escape`&&e.closeForm()};return document.addEventListener(`keydown`,t),()=>document.removeEventListener(`keydown`,t)},[e]),(0,c.useEffect)(()=>{e.fetchModelCatalog().then(B).catch(()=>B([]))},[e]),(0,c.useEffect)(()=>{if(m===``)return;let t=!0;return N(!0),F(!1),e.fetchProjectSessions(m).then(e=>{t&&j(e)}).catch(()=>{t&&(j([]),F(!0))}).finally(()=>{t&&N(!1)}),()=>{t=!1}},[e,m]),(0,c.useEffect)(()=>{e.fetchPresetCatalog().then(e=>{ce(e.presets),le(e.defaultId),!a&&ae===``&&e.defaultId!==void 0&&se(e.defaultId)}).catch(()=>ce([]))},[e,a,t?.presetId,ae]);let ye=y===`periodic`?te(x.trim()):null,be=ye===null?null:A(ye,Date.now()),xe=y===`periodic`&&(ye===null||be===null),Se=y===`once`&&L!==``?new Date(L).getTime():NaN,Ce=y===`once`&&(L===``||Number.isNaN(Se)||Se<=Date.now()),we=o.trim().length>0&&m!==``&&!xe&&!Ce&&(y===`claim`||!D.startsWith(`selected:`)||!M&&k.some(e=>e.id===D.slice(9))),Te=()=>{let e=D.startsWith(`selected:`)?{reuseSessionId:D.slice(9)}:{sessionReuseMode:D===`reuse`?`reuse`:`fresh`};return y===`periodic`?{mode:`scheduled`,cron:x.trim(),periodicCompletion:T,...e}:y===`once`?{mode:`scheduled`,runAt:new Date(L).toISOString(),...D.startsWith(`selected:`)?{reuseSessionId:D.slice(9)}:{}}:{mode:`claim`}},Ee=a&&t.status===`in_progress`,Y=a&&((t.executions?.length??0)>0||t.status===`in_progress`),De=e.gitAvailable(m),Oe=e.repoCount(m),ke=Y||!De,Ae=()=>{if(De)return fe},je=()=>oe.trim().length>0?oe.trim():void 0,Me=()=>me.map(e=>({...e,text:e.text.trim()})).filter(e=>e.text.length>0),X=H===``?void 0:JSON.parse(H),Ne=(X===void 0?void 0:z.find(e=>e.provider===X.provider&&e.model===X.model))?.reasoning,Z=()=>{if(X===void 0)return;let e=re.trim();return{provider:X.provider,model:X.model,...e.length>0?{reasoningEffort:e}:{}}},Pe=()=>{if(!we||K||_e)return;let n=Z();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(a?e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:ue,checklist:s.length>0?s:null}):e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n,...r===void 0?{}:{isolation:r},...i===void 0?{}:{presetId:i},permission:ue,...s.length>0?{checklist:s.map(e=>e.text)}:{}})).catch(()=>void 0).finally(()=>q(!1))},Fe=()=>{if(!we||Ee||K||_e)return;let n=Z();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(async()=>{if(a)await e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:ue,checklist:s.length>0?s:null})&&await e.run(t.id);else{let t=await e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n,...r===void 0?{}:{isolation:r},...i===void 0?{}:{presetId:i},permission:ue,...s.length>0?{checklist:s.map(e=>e.text)}:{}});t!==void 0&&await e.run(t)}})().catch(()=>void 0).finally(()=>q(!1))},Q=we?y===`periodic`&&be!==null?n(`form.hint.nextRun`,{time:J(be)}):y===`once`&&!Number.isNaN(Se)?n(`form.hint.onceAt`,{time:J(Se)}):a?n(`form.hint.saveVersion`,{v:t.version,next:t.version+1}):n(`form.hint.createClaim`):o.trim().length===0?n(`form.hint.needTitle`):n(m===``?`form.hint.needProject`:Ce?`form.hint.runAtBad`:`form.hint.cronBad`);return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-taskform-modal`,"data-mode":a?`edit`:`create`,role:`dialog`,"aria-modal":`true`,"aria-label":n(a?`form.title.edit`:`form.title.create`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:a?`✎`:`✚`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:n(a?`form.title.edit`:`form.title.create`)}),(0,l.jsx)(`p`,{children:n(a?`form.subtitle.edit`:`form.subtitle.create`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeForm(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body dsh-atb-taskform-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-left`,children:[(0,l.jsx)($,{label:n(`form.field.title`),required:!0,full:!0,children:(0,l.jsx)(`input`,{ref:ge,value:o,onChange:e=>s(e.target.value),placeholder:n(`form.field.titlePlaceholder`),maxLength:200})}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.project`),required:!0,children:(0,l.jsx)(`select`,{value:m,onChange:e=>{h(e.target.value),O(`fresh`),j([])},children:r.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))})}),(0,l.jsx)($,{label:n(`form.field.model`),children:(0,l.jsxs)(`select`,{value:H,onChange:e=>{let t=e.target.value;if(ne(t),t===``)ie(``);else{let e=JSON.parse(t),n=z.find(t=>t.provider===e.provider&&t.model===e.model);n?.reasoning?.defaultEffort===void 0?ie(``):ie(n.reasoning.defaultEffort)}},children:[(0,l.jsx)(`option`,{value:``,children:n(`form.field.modelDefault`)}),z.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:n(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})}),X!==void 0&&(0,l.jsx)($,{label:n(`form.field.effort`),children:(0,l.jsxs)(`select`,{value:re,onChange:e=>ie(e.target.value),title:n(`form.field.effortTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.effort.follow`),Ne?.defaultEffort===void 0?``:n(`shared.current`,{name:Ne.efforts.find(e=>e.id===Ne.defaultEffort)?.name??Ne.defaultEffort})]}),Ne!==void 0&&Ne.efforts.length>0?Ne.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id)):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`option`,{value:`low`,children:n(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:n(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:n(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:n(`form.effort.none`)})]})]})}),U.length>0&&(0,l.jsx)($,{label:n(`form.field.preset`),children:(0,l.jsxs)(`select`,{value:oe,onChange:e=>se(e.target.value),title:n(`form.field.presetTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.preset.follow`),W===void 0?``:n(`shared.current`,{name:U.find(e=>e.id===W)?.name??W})]}),U.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===W?n(`form.preset.defaultTag`):``]},e.id))]})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.permission`),children:(0,l.jsx)(`select`,{className:`dsh-atb-permission-select`,value:ue,onChange:e=>de(S(e.target.value)),children:$e(n).map(e=>(0,l.jsxs)(`option`,{value:e.value,children:[e.icon,` `,e.label,e.value===`workspace-write`?n(`form.perm.defaultTag`):``]},e.value))})}),(0,l.jsx)($,{label:n(`form.field.urgency`),children:(0,l.jsx)(`select`,{className:`dsh-atb-urgency-select`,value:g,onChange:e=>_(e.target.value),children:Ze(n).map(e=>(0,l.jsx)(`option`,{value:e.value,children:e.label},e.value))})})]}),(0,l.jsx)($,{label:n(`form.field.mode`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-exec":`true`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`claim`,onClick:()=>b(`claim`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.claim`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.claimHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`once`,onClick:()=>b(`once`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.once`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.onceHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`periodic`,onClick:()=>b(`periodic`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.periodic`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.periodicHint`)})]})]})}),y===`once`&&(0,l.jsx)($,{label:n(`form.field.runAt`),required:!0,full:!0,children:(0,l.jsx)(`input`,{type:`datetime-local`,className:Ce?`dsh-atb-input-bad`:void 0,value:L,onChange:e=>R(e.target.value),spellCheck:!1})}),y===`periodic`&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)($,{label:n(`form.field.cron`),required:!0,full:!0,children:[(0,l.jsx)(`input`,{className:xe?`dsh-atb-input-bad`:void 0,value:x,onChange:e=>C(e.target.value),placeholder:n(`form.cron.placeholder`),spellCheck:!1}),(0,l.jsxs)(`span`,{className:`dsh-atb-cron-presets`,children:[Qe(n).map(e=>(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cron-preset`,"data-on":x.trim()===e.cron,onClick:()=>C(e.cron),children:e.label},e.cron)),!xe&&be!==null&&(0,l.jsx)(`span`,{className:`dsh-atb-cron-next`,children:n(`form.cron.next`,{time:J(be)})})]})]}),(0,l.jsx)($,{label:n(`form.field.periodicCompletion`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":T===`rearm`,onClick:()=>E(`rearm`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.rearm`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.rearmHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":T===`spawn`,onClick:()=>E(`spawn`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.spawn`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.spawnHint`)})]})]})})]}),y===`periodic`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:D,onChange:e=>O(e.target.value),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.fresh`)}),(0,l.jsx)(`option`,{value:`reuse`,children:n(`form.session.reuse`)}),k.map(e=>(0,l.jsx)(`option`,{value:`selected:${e.id}`,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),P&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!P&&D.startsWith(`selected:`)&&!k.some(e=>e.id===D.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),y===`once`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:D.startsWith(`selected:`)?D:`fresh`,onChange:e=>O(e.target.value===`fresh`?`fresh`:`selected:${e.target.value}`),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.new`)}),k.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),P&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!P&&D.startsWith(`selected:`)&&!k.some(e=>e.id===D.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),(0,l.jsxs)($,{label:n(`form.field.isolation`),full:!0,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-disabled":ke?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`worktree`,disabled:ke,title:n(Y?`form.iso.locked`:De?`form.iso.worktreeTitle`:`form.iso.nonGit`),onClick:()=>pe(`worktree`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.worktree`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.worktreeHint`:`form.iso.nonGit`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`none`,disabled:ke,title:n(Y?`form.iso.locked`:`form.iso.noneTitle`),onClick:()=>pe(`none`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.none`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.noneHint`:`form.iso.noneHintNonGit`)})]})]}),!De&&!Y&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.iso.nonGitNote`)}),De&&!Y&&Oe>1&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,"data-mirror":`true`,children:n(`form.iso.mirrorNote`,{n:Oe})})]}),(0,l.jsx)($,{label:n(a?`form.field.checklist`:`form.field.checklistOptional`),full:!0,children:(0,l.jsx)(et,{rows:me,onChange:he,editing:a})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-right`,children:[(0,l.jsx)($,{label:n(a?`form.field.description`:`form.field.descriptionOptional`),full:!0,children:(0,l.jsx)(qe,{value:u,onChange:d,controller:e,rows:7,placeholder:n(`form.desc.placeholder`),allowImages:!0,onUploadingChange:ve})}),(0,l.jsx)($,{label:n(a?`form.field.prompt`:`form.field.promptOptional`),full:!0,children:(0,l.jsx)(qe,{value:f,onChange:p,controller:e,rows:7,placeholder:n(`form.prompt.placeholder`)})})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,"data-tone":we?void 0:`bad`,children:Q}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeForm(),children:n(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!we||Ee||K||_e,title:n(Ee?`form.action.runBlockedTitle`:K||_e?`form.action.runBusyTitle`:`form.action.runTitle`),onClick:Fe,children:n(`form.action.run`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!we||K||_e,onClick:Pe,children:n(a?`form.action.save`:`form.action.create`)})]})]})]})})}const nt=e=>[{value:`none`,name:e(`form.iso.none`),hint:e(`set.iso.noneHint`)},{value:`worktree`,name:e(`form.iso.worktree`),hint:e(`set.iso.worktreeHint`)}];function rt({controller:e}){let t=G(),n=e.getSnapshot(),r=n.ledger.settings?.defaultIsolation??`none`,i=C(n.ledger.settings),a=w(n.ledger.settings),o=T(n.ledger.settings),s=E(n.ledger.settings),u=D(n.ledger.settings),d=O(n.ledger.settings),[f,p]=(0,c.useState)(r),[m,h]=(0,c.useState)(i),[g,_]=(0,c.useState)(a),[v,y]=(0,c.useState)(String(o)),[b,x]=(0,c.useState)(String(s)),[S,ee]=(0,c.useState)(String(u)),[te,k]=(0,c.useState)(String(d)),[A,j]=(0,c.useState)(n.storage?.currentDirectory??``),[M,N]=(0,c.useState)(!1),[P,F]=(0,c.useState)(!1),I=Number(v),L=Number(b),R=Number(S),z=Number(te),B=Number.isSafeInteger(I)&&I>=1&&I<=100&&Number.isSafeInteger(L)&&L>=1&&L<=1440&&Number.isSafeInteger(R)&&R>=0&&R<=10080&&Number.isSafeInteger(z)&&z>=0&&z<=6e4,V=f!==r||m!==i||g!==a||v!==String(o)||b!==String(s)||S!==String(u)||te!==String(d),H=A.trim().length===0?n.storage?.defaultDirectory??``:A.trim(),ne=n.storage!==void 0&&H!==n.storage.currentDirectory;return(0,c.useEffect)(()=>{!M&&n.storage!==void 0&&j(n.storage.currentDirectory)},[n.storage,M]),(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeSettings()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-set`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`set.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`🛠`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`set.title`)}),(0,l.jsx)(`p`,{children:t(`set.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeSettings(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.iso.heading`)}),(0,l.jsx)(`div`,{className:`dsh-atb-mode-picker`,children:nt(t).map(e=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===e.value,title:e.hint,onClick:()=>p(e.value),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:e.name}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:e.hint})]},e.value))}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.iso.current`,{current:t(r===`worktree`?`form.iso.worktree`:`form.iso.none`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.sync.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":!m,title:t(`set.sync.off.title`),onClick:()=>h(!1),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.off.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.off.hint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":m,title:t(`set.sync.on.title`),onClick:()=>h(!0),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.on.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.on.hint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(i?`set.sync.stateOn`:`set.sync.stateOff`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.perm.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-perm-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":g===`workspace-write`,onClick:()=>_(`workspace-write`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.writeName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.writeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":g===`read-only`,onClick:()=>_(`read-only`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.readOnlyName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.readOnlyHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":g===`danger-full-access`,onClick:()=>_(`danger-full-access`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.fullName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.fullHint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.perm.current`,{current:t(a===`read-only`?`set.perm.readOnlyName`:a===`danger-full-access`?`set.perm.fullName`:`set.perm.writeName`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.schedule.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.schedule.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-settings-numbers`,children:[(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.concurrent`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`100`,step:`1`,value:v,onChange:e=>y(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.missedAfter`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`1440`,step:`1`,value:b,onChange:e=>x(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.queueMaxAge`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`10080`,step:`1`,value:S,onChange:e=>ee(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.dispatchInterval`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`60000`,step:`1`,value:te,onChange:e=>k(e.target.value)})]})]}),!B&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:t(`set.schedule.invalid`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.storage.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.storage.hint`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-storage-path`,value:A,disabled:n.storage===void 0||P,placeholder:n.storage?.defaultDirectory??t(`set.storage.loading`),onChange:t=>{j(t.target.value),N(!0),e.dismissStorageNotice()}}),n.storageNotice!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-notice`,role:`status`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-storage-notice-ok`,children:[`✓ `,t(`set.storage.migrated`,{path:n.storageNotice.path})]}),n.storageNotice.warnings.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-notice-warn`,children:t(`set.storage.warnings`,{warnings:n.storageNotice.warnings.join(`; `)})})]}),n.storage!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-meta`,children:[(0,l.jsx)(`span`,{children:t(`set.storage.current`,{path:n.storage.currentDirectory})}),(0,l.jsx)(`span`,{children:t(`set.storage.assets`,{count:n.storage.assetCount,size:(n.storage.assetBytes/1024/1024).toFixed(1)})}),n.storage.error!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:n.storage.error})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-storage-actions`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||P,onClick:()=>{j(n.storage?.defaultDirectory??``),N(!0)},children:t(`set.storage.default`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||P||H.length===0,onClick:()=>{F(!0),e.checkStorage(H).finally(()=>F(!1))},children:t(`set.storage.check`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!ne||P,onClick:()=>{window.confirm(t(`set.storage.confirm`,{from:n.storage?.currentDirectory??``,to:H}))&&(F(!0),e.migrateStorage(H).then(e=>{e&&(N(!1),j(H))}).finally(()=>F(!1)))},children:t(P?`set.storage.migrating`:`set.storage.migrate`)})]})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(V?`set.foot.dirty`:`set.foot.clean`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeSettings(),children:t(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!V||!B,onClick:()=>{e.updateSettings({defaultIsolation:f,syncExternalSessions:m,defaultPermission:g,maxConcurrent:I,scheduleMissedAfterMinutes:L,queueMaxAgeMinutes:R,dispatchIntervalMs:z}).then(t=>{t&&e.closeSettings()})},children:t(`set.action.save`)})]})]})]})})}function it({row:e}){return(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,title:e.id,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.status})]})}function at({controller:e}){let t=G(),[n,r]=(0,c.useState)(``),[i,a]=(0,c.useState)(null),[o,s]=(0,c.useState)(void 0),[u,d]=(0,c.useState)(void 0),[f,p]=(0,c.useState)(`merge`),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(void 0),[v,y]=(0,c.useState)(!1),b=(0,c.useRef)(null),{alert:x,el:S}=Y(),ee=n=>{d(void 0),s(void 0),_(void 0),y(!1),r(``),a(null),n!==void 0&&n.text().then(i=>{try{let t=JSON.parse(i);a(t),r(n.name),e.importPreview(t).then(e=>{e!==void 0&&d(e)})}catch{s(t(`imp.parseError`))}})},C=()=>{if(!(i===null||u===void 0||m)){if(f===`replace`&&!v){y(!0);return}h(!0),e.importCommit(f,i).then(e=>{h(!1),y(!1),e!==void 0&&_(e.mode===`replace`?t(`imp.result.replace`,{n:e.created+e.overwritten,total:e.replacedTotal??0}):t(`imp.result.merge`,{n:e.created,m:e.overwritten}))})}},w=()=>e.closeImport();return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&w()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-imp`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`imp.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⬆`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`imp.title`)}),(0,l.jsx)(`p`,{children:t(`imp.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:w,children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-picker`,children:[(0,l.jsx)(`input`,{ref:b,type:`file`,accept:`.json,application/json`,onChange:e=>ee(e.target.files?.[0])}),n.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-imp-filename`,children:n})]}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-note`,children:t(`imp.note`)}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-error`,children:o}),u===void 0&&o===void 0&&n.length>0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`imp.previewing`)}),u!==void 0&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stats`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`ok`,children:[(0,l.jsx)(`b`,{children:u.create.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.create`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`warn`,children:[(0,l.jsx)(`b`,{children:u.overwrite.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.overwrite`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":u.invalid.length>0?`bad`:void 0,children:[(0,l.jsx)(`b`,{children:u.invalid.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.invalid`)})]})]}),u.create.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.create`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.create.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.overwrite.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.overwrite`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.overwrite.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.invalid.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.invalid`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.invalid.map((e,n)=>(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,"data-tone":`bad`,title:e.id??``,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.id??t(`imp.noId`)}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.reason})]},e.id??`invalid-${n}`))})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`merge`,onClick:()=>{p(`merge`),y(!1)},children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.merge`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.mergeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`replace`,onClick:()=>p(`replace`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.replace`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.replaceHint`)})]})]}),g!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-result`,children:g})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(f===`replace`?v?`imp.foot.replaceConfirm`:`imp.foot.replaceNeedConfirm`:`imp.foot.mergeHint`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:w,children:t(g===void 0?`shared.cancel`:`shared.close`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,"data-danger":f===`replace`&&v?`true`:void 0,disabled:u===void 0||m,onClick:C,children:t(f===`replace`&&v?`imp.action.confirmReplace`:`imp.action.run`)})]})]})]}),S]})}function ot({controller:e}){let t=G(),n=e.getSnapshot(),[r,i]=(0,c.useState)({}),[a,o]=(0,c.useState)(void 0),{alert:s,el:u}=Y(),d=()=>e.closeTemplateManager(),f=e=>r[e.id]??xe(e),p=e=>{if(e===void 0)return``;let n=q[e];return n===void 0?` · ${e}`:` · ${t(n)}`},m=(r,a)=>{let o=n.templates.find(e=>e.id===r);o===void 0||a===xe(o)||e.upsertTemplate({id:r,name:a,task:o.task}).then(e=>{e&&(i(e=>{let t={...e};return delete t[r],t}),s(t(`tpl.renamed`)))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&d()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-tplm`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`tpl.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⌗`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`tpl.title`)}),(0,l.jsx)(`p`,{children:t(`tpl.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:d,children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:n.templates.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`tpl.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-tplm-list`,children:n.templates.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-tplm-row`,children:[(0,l.jsx)(`input`,{className:`dsh-atb-tplm-name`,value:f(n),maxLength:60,spellCheck:!1,"aria-label":t(`tpl.name.aria`,{name:xe(n)}),onChange:e=>i(t=>({...t,[n.id]:e.target.value})),onKeyDown:e=>{e.key===`Enter`&&m(n.id,f(n))}}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-meta`,title:`${n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`)}${n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``}${p(n.task.urgency)}${n.task.permission===void 0?``:` · ${t(`shared.permission`)}: ${n.task.permission}`}`,children:[n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`),n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``,p(n.task.urgency),n.task.permission!==void 0&&n.task.permission!==`workspace-write`?` · ${n.task.permission===`read-only`?t(`tpl.meta.permReadOnly`):t(`tpl.meta.permFull`)}`:``]}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-btns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:f(n)===xe(n)||f(n).trim().length===0,title:t(`tpl.rename.title`),onClick:()=>m(n.id,f(n)),children:t(`tpl.rename.button`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`tpl.use.title`),onClick:()=>{d(),e.newFromTemplate(Se(n))},children:t(`tpl.use.button`)}),a===n.id?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{e.deleteTemplate(n.id),o(void 0)},children:t(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(void 0),children:t(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,title:t(`tpl.delete.title`),onClick:()=>o(n.id),children:`🗑`})]})]},n.id))})}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(`tpl.foot.hint`)}),(0,l.jsx)(`span`,{className:`dsh-atb-modal-footbtns`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:d,children:t(`shared.close`)})})]})]}),u]})}const st={urgent:0,normal:1,relaxed:2};function ct(e,t){let n=e.search.trim().toLowerCase(),r=t.filter(t=>(e.filters.workspaceId===void 0||t.workspaceId===e.filters.workspaceId)&&(e.filters.urgencies.length===0||e.filters.urgencies.includes(t.urgency))&&(n.length===0||t.title.toLowerCase().includes(n)||t.id.toLowerCase().includes(n)));if(e.sortBy==="default")return r;let i=[...r];return e.sortBy===`updated`?i.sort((e,t)=>t.updatedAt-e.updatedAt):e.sortBy===`created`?i.sort((e,t)=>t.createdAt-e.createdAt):e.sortBy===`urgency`?i.sort((e,t)=>st[e.urgency]-st[t.urgency]||t.updatedAt-e.updatedAt):e.sortBy===`title`&&i.sort((e,t)=>e.title.localeCompare(t.title,void 0,{numeric:!0})),i}function lt({controller:e}){let t=G(),n=(0,c.useSyncExternalStore)(t=>e.subscribe(t),()=>e.getSnapshot()),[r,i]=(0,c.useState)(()=>Date.now());(0,c.useEffect)(()=>{let e=setInterval(()=>i(Date.now()),6e4);return()=>clearInterval(e)},[]);let a=ct(n,n.ledger.tasks.filter(e=>e.trashedAt===void 0)),o=n.selectedId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.selectedId),{alert:s,el:u}=Y(),[d,f]=(0,c.useState)(!1),p=()=>f(!1),[m,h]=(0,c.useState)(!1),g=()=>h(!1);return(0,l.jsxs)(`div`,{className:`dsh-atb-board`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-toolbar`,children:[(0,l.jsx)(`h2`,{className:`dsh-atb-title`,children:t(`board.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-count`,children:t(`board.count.tasks`,{n:a.length,rev:n.ledger.revision})}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{let t=!d;f(t),t&&e.prepareTemplateMenu()},children:t(`board.action.newTask`)}),d&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:p}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.setComposer(!0)},children:t(`board.action.blankTask`)}),n.templates.map(t=>{let n=xe(t),r=Se(t);return(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:r.description!==void 0&&r.description.length>0?r.description.slice(0,120):n,onClick:()=>{p(),e.newFromTemplate(r)},children:n},t.id)}),(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-sep`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.openTemplateManager()},children:t(`board.action.manageTemplates`)})]})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-spacer`}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-search`,value:n.search,placeholder:t(`board.search.placeholder`),spellCheck:!1,onChange:t=>e.setSearch(t.target.value)}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.filters.workspaceId??``,onChange:t=>e.setWorkspaceFilter(t.target.value===``?void 0:t.target.value),children:[(0,l.jsx)(`option`,{value:``,children:t(`board.filter.allProjects`)}),n.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))]}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.sortBy,title:t(`board.sort.title`),onChange:t=>e.setSortBy(t.target.value),children:[(0,l.jsx)(`option`,{value:`default`,children:t(`board.sort.default`)}),(0,l.jsx)(`option`,{value:`updated`,children:t(`board.sort.updated`)}),(0,l.jsx)(`option`,{value:`urgency`,children:t(`board.sort.urgency`)}),(0,l.jsx)(`option`,{value:`created`,children:t(`board.sort.created`)}),(0,l.jsx)(`option`,{value:`title`,children:t(`board.sort.byTitle`)})]}),[`urgent`,`normal`,`relaxed`].map(r=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip`,"data-urgency":r,"data-on":n.filters.urgencies.includes(r),onClick:()=>e.toggleUrgency(r),children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-urgency":r}),t(q[r])]},r)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.toggleSecondary(),children:n.secondaryOpen?t(`board.action.backToBoard`):t(`board.action.otherTasks`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.settingsTitle`),onClick:()=>e.openSettings(),children:t(`board.action.settings`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.diagTitle`),onClick:()=>e.openDiagnostics(),children:t(`board.action.diag`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.importTitle`),onClick:()=>e.openImport(),children:t(`board.action.import`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.exportTitle`),onClick:()=>h(!m),children:t(`board.action.export`)}),m&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:g}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.jsonTitle`),onClick:()=>{g(),e.exportJson()},children:t(`board.export.json`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.csvTitle`),onClick:()=>{g(),e.exportCsv()},children:t(`board.export.csv`)})]})]})]}),(0,l.jsxs)(`a`,{className:`dsh-atb-ver`,href:`https://github.com/cloader/dsh-taskboard`,target:`_blank`,rel:`noopener noreferrer`,children:[`V`,`0.8.6`]})]}),n.error!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-error`,children:n.error}),n.secondaryOpen?(0,l.jsx)(ft,{controller:e,tasks:ct(n,n.ledger.tasks)}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(i=>{let o=a.filter(e=>e.status===i);return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,onDragOver:e=>{e.dataTransfer.types.includes(`application/x-dsh-atb-task`)&&(e.preventDefault(),e.dataTransfer.dropEffect=`move`,e.currentTarget.dataset.dragover=`true`)},onDragLeave:e=>{delete e.currentTarget.dataset.dragover},onDrop:r=>{r.preventDefault(),delete r.currentTarget.dataset.dragover;let a=r.dataTransfer.getData(we);if(a.length===0)return;let o=n.ledger.tasks.find(e=>e.id===a);if(!(o===void 0||o.status===i)){if(!x(o.status,i)){s(t(`board.drag.forbidden`,{from:t(he[o.status]),to:t(he[i])}));return}e.move(a,o.version,i)}},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":i}),t(he[i]),(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:o.length}),i===`in_progress`&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-queuechip`,title:t(`queue.panel.title`),"data-bad":(n.queue?.depth??0)>0?`true`:void 0,onClick:()=>e.openQueuePanel(),children:t(`board.queue.chip`,{n:n.queue?.depth??0})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[o.map(t=>(0,l.jsx)(Ee,{task:t,controller:e,draggable:!0,now:r,onAlert:s},t.id)),o.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:t(`board.empty`)})]})]},i)})}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-detailpanel`,children:(0,l.jsx)(We,{task:o,controller:e,now:r},o.id)}),n.composerOpen&&(0,l.jsx)(tt,{controller:e,task:n.editingId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.editingId)}),n.diagOpen&&(0,l.jsx)(dt,{controller:e}),n.queueOpen&&(0,l.jsx)(ut,{controller:e,onAlert:s}),n.settingsOpen&&(0,l.jsx)(rt,{controller:e}),n.importOpen&&(0,l.jsx)(at,{controller:e}),n.tplManagerOpen&&(0,l.jsx)(ot,{controller:e}),u]})}function ut({controller:e,onAlert:t}){let n=G(),r=e.getSnapshot().queue,[i,a]=(0,c.useState)(!1),[o,s]=(0,c.useState)(!1),u=r?.depth??0,d=r?.oldestWaitMinutes,f=async()=>{if(!i){a(!0);return}s(!0);let r=await e.clearQueue();s(!1),a(!1),r!==void 0&&(t(n(`queue.cleared`,{n:r.cleared})),e.closeQueuePanel())};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeQueuePanel()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-queuepanel`,role:`dialog`,"aria-modal":`true`,"aria-label":n(`queue.panel.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`📋`}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-headtext`,children:(0,l.jsx)(`h3`,{children:n(`queue.panel.title`)})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeQueuePanel(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":u>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:u}),(0,l.jsx)(`span`,{children:n(`queue.depth`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":d!==void 0&&d>5?`true`:void 0,children:[(0,l.jsxs)(`b`,{children:[d??0,`m`]}),(0,l.jsx)(`span`,{children:n(`queue.wait`)})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-queuepanel-actions`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:u===0||o,"data-armed":i?`true`:void 0,onClick:()=>{f()},children:n(i?`queue.confirm`:`queue.clear`)})})]})]})})}function dt({controller:e}){let t=G(),n=e.getSnapshot(),r=n.diagnostics,i=e=>{let t=n.workspaces.find(t=>t.id===e);return t?.title??t?.path??e.slice(0,8)};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeDiagnostics()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-diag`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`diag.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⚙`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`diag.title`)}),(0,l.jsx)(`p`,{children:t(`diag.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeDiagnostics(),children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:r===void 0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`shared.loading`)}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.revision}),(0,l.jsx)(`span`,{children:t(`diag.revision`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.tasks}),(0,l.jsx)(`span`,{children:t(`diag.tasks`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.staleRunning>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.staleRunning}),(0,l.jsx)(`span`,{children:t(`diag.running`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.orphanWorktrees.length>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.orphanWorktrees.length}),(0,l.jsx)(`span`,{children:t(`diag.orphans`)})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.orphans.heading`)}),r.orphanWorktrees.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.orphanWorktrees.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-diag-orphan`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:n.path,children:[i(n.workspaceId),` · `,n.taskId]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void e.cleanupOrphan(n.workspaceId,n.taskId),children:t(`diag.orphans.cleanup`)})]},n.path))}),(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.hint`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.gitignore.heading`)}),(r.gitIgnoreSuggestions??[]).length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.gitignore.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.gitIgnoreSuggestions.map(e=>(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphan`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:e.workspacePath,children:[i(e.workspaceId),` · `,t(`diag.gitignore.suggestA`),` `,(0,l.jsx)(`code`,{children:`.dsh-worktrees/`}),t(`diag.gitignore.suggestB`)]})},e.workspaceId))})]})]})})]})})}function ft({controller:e,tasks:t}){let n=G(),[r,i]=(0,c.useState)(`none`),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(()=>new Set),d=t.filter(e=>e.trashedAt!==void 0),f=t.filter(e=>e.trashedAt===void 0&&e.status===`archived`),p=t.filter(e=>e.trashedAt===void 0&&e.status===`canceled`),m=e=>{u(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},h=e=>{u(t=>{let n=e.length>0&&e.every(e=>t.has(e.id)),r=new Set(t);for(let t of e)n?r.delete(t.id):r.add(t.id);return r})},g=async t=>{let n=(t===`trashArchived`?f:d).filter(e=>s.has(e.id));o(!0);try{for(let r of n){try{await e.remove(r.id,r.version,t===`purgeTrashed`)}catch{}u(e=>{let t=new Set(e);return t.delete(r.id),t})}}finally{o(!1),i(`none`),u(new Set)}},_=()=>{i(`none`),u(new Set)},v=[{label:n(`status.column.canceled`),dot:`canceled`,rows:p},{label:n(`status.column.archived`),dot:`archived`,rows:f,action:`trashArchived`},{label:n(`board.group.trashed`),dot:`trashed`,rows:d,action:`purgeTrashed`}];return d.length+f.length+p.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-secondary`,children:(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.secondary.empty`)})}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(t=>{let o=r===t.action,c=o?t.rows.filter(e=>s.has(e.id)).length:0,d=t.rows.length>0&&c===t.rows.length;return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":t.dot}),t.label,(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:t.rows.length}),t.action&&t.rows.length>0&&(o?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:n(`board.batch.confirmCancelTip`),onClick:_,children:n(`board.batch.cancel`)}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:a||c===0,title:n(c===0?`board.batch.noneSelected`:`board.batch.confirmTip`),onClick:()=>{g(t.action)},children:[n(`board.batch.confirm`),`(`,c,`)`]}),(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selectall`,checked:d,disabled:a,title:n(`board.batch.selectAll`),onClick:e=>e.stopPropagation(),onChange:()=>h(t.rows)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:t.action===`trashArchived`?n(`board.batch.trashArchivedTip`):n(`board.batch.purgeTrashedTip`),onClick:()=>{i(t.action),u(new Set)},children:t.action===`trashArchived`?n(`board.batch.trashArchived`):n(`board.batch.purgeTrashed`)}))]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[t.rows.map(n=>t.action&&o?(0,l.jsxs)(`div`,{className:`dsh-atb-selrow`,children:[(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selbox`,checked:s.has(n.id),disabled:a,onClick:e=>e.stopPropagation(),onChange:()=>m(n.id)}),(0,l.jsx)(Ee,{task:n,controller:e})]},n.id):(0,l.jsx)(Ee,{task:n,controller:e},n.id)),t.rows.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.empty`)})]})]},t.label)})})}function pt(e){let t=()=>{if((document.body.getAttribute(`data-dsh-desktop-platform`)??new URLSearchParams(window.location.search).get(`dsh-desktop-platform`))!==`win32`){delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`);return}e.dataset.dshAtbWindows=``,e.style.setProperty(`--dsh-atb-viewport-top`,`${e.getBoundingClientRect().top}px`)},n,r=()=>{n===void 0&&(n=requestAnimationFrame(()=>{n=void 0,t()}))},i=new MutationObserver(r);i.observe(document.body,{attributes:!0,childList:!0,subtree:!0,attributeFilter:[`class`,`data-dsh-desktop-platform`,`data-dsh-desktop-mode`,`data-details-collapsed`]}),i.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-dsh-atb-active`]});let a=typeof ResizeObserver>`u`?void 0:new ResizeObserver(r);return a?.observe(e),e.parentElement!==null&&a?.observe(e.parentElement),window.addEventListener(`resize`,r),r(),()=>{i.disconnect(),a?.disconnect(),window.removeEventListener(`resize`,r),n!==void 0&&cancelAnimationFrame(n),delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`)}}function mt(){let e=document.querySelector(`[data-pane="sidebar"], [class*="sidebarCol"], .dshDesktopUpstreamSidebar, .dshDesktopSidebarSurface`);if(e!==null)return e.querySelector(`[class*="logoRow"]`)?.parentElement??e.firstElementChild}function ht(e){let t=e.querySelector(`button[class*="newSession"]`);if(t!==null)return t;for(let t of e.children)if(t instanceof HTMLButtonElement&&!t.matches(`[data-dsh-atb-entry]`))return t;let n=e.querySelector(`button[aria-label="新建会话"], button[aria-label="New Session"], button[aria-label*="新会话"], button[aria-label*="new session" i]`);return n===null?Array.from(e.querySelectorAll(`button`)).find(e=>!e.matches(`[data-dsh-atb-entry]`)&&/新会话|新建会话|new session/i.test(e.textContent??``)):n}function gt(e){let t=document.createElement(`button`);return t.type=`button`,t.dataset.dshAtbEntry=``,t.className=`dsh-atb-entry`,t.setAttribute(`aria-label`,W(`shared.entry.aria`)),t.innerHTML=`<span class="dsh-atb-entry-icon"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M6 2v12M10 2v12"/></svg></span><span class="dsh-atb-entry-label">${W(`shared.entry.label`)}</span><span class="dsh-atb-entry-stats"></span>`,t.addEventListener(`click`,()=>{e.toggleBoard()}),t}function _t(e){let t=0,n=0,r=0;for(let i of e.getSnapshot().ledger.tasks)i.trashedAt===void 0&&(i.status===`todo`?t++:i.status===`in_progress`?n++:i.status===`in_review`&&r++);return[t,n,r]}function vt(e,t){let n=String(t);if(e.dataset.value===n)return;let r=e.dataset.value;if(e.dataset.value=n,e.style.minWidth=`${n.length}ch`,r===void 0){e.textContent=n;return}e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``);let i=document.createElement(`span`);i.className=`dsh-atb-rn`,i.textContent=r;let a=document.createElement(`span`);a.className=`dsh-atb-rn dsh-atb-rn-next`,a.textContent=n,e.replaceChildren(i,a),e.dataset.dir=t>Number(r)?`up`:`down`,e.dataset.busy=`1`,requestAnimationFrame(()=>{e.dataset.anim=`1`});let o=()=>{e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``,e.textContent=e.dataset.value??``)};e.addEventListener(`transitionend`,o,{once:!0}),setTimeout(o,400)}function yt(e,t){let n=e.querySelector(`.dsh-atb-entry-stats`);if(n===null)return()=>{};let r=[`todo`,`in_progress`,`in_review`],i=[];for(let e=0;e<3;e++){if(e>0){let e=document.createElement(`span`);e.className=`dsh-atb-entry-sep`,e.textContent=`|`,n.append(e)}let t=document.createElement(`span`);t.className=`dsh-atb-roll`,t.dataset.stat=r[e],n.append(t),i.push(t)}return()=>{let[e,r,a]=_t(t);vt(i[0],e),vt(i[1],r),vt(i[2],a),n.title=W(`shared.stats.title`,{todo:e,doing:r,review:a})}}function bt(e,t){let n=ht(e);if(n===void 0)return!1;if(t.parentElement!==e){let r=n.closest(`[class*="logoRow"]`),i=r!==null&&r.parentElement===e?r:n,a=Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.matches(`[data-dsh-atb-entry], [data-dsh-taskboard-entry], [data-dsh-ssh-entry]`)),o=a.length>0?a[0]??null:i.nextElementSibling??null;e.insertBefore(t,o)}return!0}function xt(e){let t=gt(e),n={attempts:0,found:!1,placed:!1},r=globalThis.location?.hostname;(r===`localhost`||r===`127.0.0.1`)&&(window.__atbDebug=n);let i,a=!1,o=()=>{if(n.attempts++,i!==void 0&&!i.isConnected&&(c.disconnect(),i=void 0,a=!1),a){if(document.body.contains(t))return;c.disconnect(),i=void 0,a=!1}i??=mt(),i!==void 0&&(n.found=ht(i)!==void 0,a=bt(i,t),n.placed=a,a&&c.observe(i,{childList:!0,subtree:!0}))},s=new MutationObserver(()=>{o()});s.observe(document.body,{childList:!0,subtree:!0});let c=new MutationObserver(()=>{if(i===void 0||!i.isConnected){a=!1,o();return}i.contains(t)||(a=bt(i,t))}),l=setInterval(()=>{o()},2e3),u=yt(t,e),d=()=>{e.getSnapshot().boardOpen?t.dataset.active=`true`:delete t.dataset.active,u()},f=e.subscribe(d),p=U.subscribe(()=>{t.setAttribute(`aria-label`,W(`shared.entry.aria`));let e=t.querySelector(`.dsh-atb-entry-label`);e!==null&&(e.textContent=W(`shared.entry.label`)),u()});return d(),o(),()=>{clearInterval(l),s.disconnect(),c.disconnect(),p(),f(),t.remove()}}const St=`data-dsh-atb-active`,Ct=[`data-dsh-taskboard-active`,`data-dsh-ssh-active`],wt=`dsh-panel-activate`,Tt=`dsh-taskboard`;function Et(){return document.querySelector(`[data-pane="conversation"], [class*="centerCol"], .dshDesktopConversationSurface`)??void 0}function Dt(e){let t,n,r,i=()=>{if(n!==void 0)return;let i=Et();i!==void 0&&(n=document.createElement(`div`),n.dataset.dshAtbView=``,n.className=`dsh-atb-view`,i.appendChild(n),r=pt(n),t=(0,d.createRoot)(n),t.render((0,l.jsx)(lt,{controller:e})))},a=new MutationObserver(()=>{i()});a.observe(document.body,{childList:!0,subtree:!0});let o=()=>{if(e.getSnapshot().boardOpen){for(let e of Ct)document.documentElement.removeAttribute(e);document.documentElement.setAttribute(St,``),document.dispatchEvent(new CustomEvent(wt,{detail:Tt}))}else document.documentElement.removeAttribute(St)},s=t=>{t.detail!==Tt&&e.getSnapshot().boardOpen&&e.closeBoard()},c=t=>{if(!e.getSnapshot().boardOpen)return;let n=t.target;n!==null&&n.closest(`[data-dsh-atb-entry]`)===null&&n.closest(`[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]`)!==null&&e.closeBoard()};document.addEventListener(`click`,c,!0),document.addEventListener(wt,s);let u=e.subscribe(o);return o(),i(),()=>{document.removeEventListener(`click`,c,!0),document.removeEventListener(wt,s),a.disconnect(),u(),r?.(),document.documentElement.removeAttribute(St),t?.unmount(),n?.remove()}}const Ot=`dsh-taskboard`;function kt(e){let t=e.get?.(`slots`)??e.slots;if(!(t===void 0||typeof t.register!=`function`||typeof t.inject!=`function`||typeof t.spec!=`function`)){try{if(t.spec(`main`)===void 0||t.spec(`sidebar.panellist`)===void 0)return}catch{return}return t}}function At(e){return function(){let t=c.useCallback(e=>{if(e===null)return;let t=`__dshAtbInsetDispose`,n=e;n[t]===void 0&&(n[t]=pt(e))},[]);return c.createElement(`div`,{"data-dsh-atb-panel-root":``,ref:t,className:`dsh-atb-panel-root`},c.createElement(lt,{controller:e}))}}function jt(e){return function(t){let n=typeof t.size==`number`?t.size:16,r=c.useCallback(t=>e.subscribe(t),[e]),i=c.useCallback(()=>e.getSnapshot(),[e]),a=c.useSyncExternalStore(r,i,i),o=c.useMemo(()=>{let e=0,t=0,n=0;for(let r of a.ledger.tasks)r.trashedAt===void 0&&(r.status===`todo`?e++:r.status===`in_progress`?t++:r.status===`in_review`&&n++);return{todo:e,doing:t,review:n}},[a.ledger]),[s,l]=c.useState(!0),u=c.useCallback(e=>{if(e===null||typeof ResizeObserver>`u`)return;let t=e.closest(`button`);t!==null&&new ResizeObserver(()=>{l(t.clientWidth>44)}).observe(t)},[]),d=W(`shared.stats.title`,{todo:o.todo,doing:o.doing,review:o.review});return c.createElement(`span`,{className:`dsh-atb-pglyph`,ref:u},c.createElement(`span`,{className:`dsh-atb-picon`},c.createElement(`svg`,{width:n,height:n,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":1.3,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":!0},c.createElement(`rect`,{x:2,y:2,width:12,height:12,rx:2}),c.createElement(`path`,{d:`M6 2v12M10 2v12`})),!s&&o.todo>0?c.createElement(`span`,{className:`dsh-atb-pbadge`,title:d,"aria-hidden":!0},o.todo):null),s&&(o.todo>0||o.doing>0||o.review>0)?c.createElement(`span`,{className:`dsh-atb-pstats`,title:d,"aria-hidden":!0},c.createElement(`span`,{"data-stat":`todo`},o.todo),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_progress`},o.doing),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_review`},o.review)):null)}}function Mt(e,t,n){let r=[];try{let i=t.get?.(`layout`)??t.layout;n.installCloseRequester(()=>{i?.selectPanel(null)}),r.push(()=>{n.installCloseRequester(void 0)});let a=At(n);r.push(e.inject(`main`,()=>e.register({name:`main`,key:Ot},a)));let o=jt(n);r.push(e.inject(`sidebar.panellist`,()=>e.register({name:`sidebar.panellist`,id:Ot,order:10,label:()=>W(`shared.entry.label`)},o))),console.info(`[dsh-taskboard] official panel mode: registered`,Ot)}catch(e){console.error(`[dsh-taskboard] official panel registration failed — falling back to legacy mounting:`,e);for(let e of r.splice(0))e();return}return()=>{for(let e of r.splice(0))e()}}function Nt(e,t){let n=kt(e);if(n!==void 0){let r=Mt(n,e,t);if(r!==void 0)return r}let r=[];r=[xt(t),Dt(t)];let i=0,a,o=setInterval(()=>{i++;let n=kt(e);if(n!==void 0){let i=Mt(n,e,t);if(i!==void 0){a=i,clearInterval(o);for(let e of r.splice(0))e();return}}i>=3&&clearInterval(o)},350);return()=>{clearInterval(o);for(let e of r.splice(0))e();a?.()}}function Pt(e){let t=(e,t,n)=>e.list.getSnapshot().byId[n]===void 0?`absent`:t?.list.getSnapshot().archivedSessionIds.includes(n)??!1?`archived`:`openable`,n=(t,n)=>{let r=e.getUiWorkspace?.();return typeof r?.openSession==`function`?(r.openSession(n),!0):typeof t.open==`function`?(t.open(n),!0):!1};return async r=>{let i=e.getSessions();if(i===void 0)return`unavailable`;try{let a=t(i,e.getWorkspaces(),r);if(a===`absent`){try{await i.refresh()}catch{}a=t(i,e.getWorkspaces(),r)}return a===`archived`?`archived`:a===`absent`?`missing`:n(i,r)?`opened`:`unavailable`}catch{return`unavailable`}}}const Ft=[`connection`];function It(e){try{me(),oe(e.get?.(`locale`),()=>e.get?.(`locale`));let t=_(),n=new fe(t);n.installModelCatalog(async()=>{try{let t=e.get?.(`modelDirectories`)??e.modelDirectories;if(t?.catalog?.load!==void 0){let e=await t.catalog.load();if(e?.groups!==void 0&&e.groups.length>0){let t=[];for(let n of e.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`remote`)??e.remote;if(t?.session?.modelCatalog!==void 0){let e=await t.session.modelCatalog();if(e.ok&&e.value?.groups!==void 0&&e.value.groups.length>0){let t=[];for(let n of e.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}if(t?.llm?.models!==void 0){let e=await t.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.llm?.models!==void 0){let e=await t.api.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let e=await t.modelCatalog();if(e.models!==void 0&&e.models.length>0)return e.models}catch{}return[]}),n.installPresetRoster(async()=>{try{let t=e.get?.(`remote`)??e.remote;if(t?.agentPresets?.list!==void 0){let e=await t.agentPresets.list(),n=e.ok===!0?e.value.presets:e.result?.ok===!0?e.result.value.presets:void 0;if(n!==void 0&&n.length>0){let e=n.map(e=>({id:e.id,name:e.name})),t=n.find(e=>e.isDefault);return{presets:e,...t===void 0?{}:{defaultId:t.id}}}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.agentPresets?.list!==void 0){let e=await t.api.agentPresets.list({});if(e.result.ok&&e.result.value?.presets!==void 0&&e.result.value.presets.length>0){let t=e.result.value.presets.map(e=>({id:e.id,name:e.name})),n=e.result.value.presets.find(e=>e.isDefault);return{presets:t,...n===void 0?{}:{defaultId:n.id}}}}}catch{}try{let e=await t.modelCatalog();if(e.presets!==void 0&&e.presets.length>0)return{presets:e.presets,...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{}return{presets:[]}}),n.installSessionJumper(Pt({getSessions:()=>e.get?.(`sessions`),getWorkspaces:()=>e.get?.(`workspaces`),getUiWorkspace:()=>e.get?.(`uiWorkspace`)})),n.installSessionTitles(()=>(e.get?.(`sessions`))?.list.getSnapshot().byId??{}),n.start();let r=[];try{r.push(Nt(e,n))}catch(e){console.error(`[dsh-taskboard] mount failed:`,e)}e.effect?.(()=>()=>{for(let e of r.splice(0))e();n.dispose(),se()},`dsh-taskboard: client mount`)}catch(e){console.error(`[dsh-taskboard] client half failed to start:`,e)}}exports.apply=It,exports.inject=Ft,exports.name=`dsh-taskboard/client`; +`,t=/\s/.test(e)||r===0,i=n.slice(r+1),a=!/\s/.test(i);if(t&&a){O(r),E(i),w(!0);return}}w(!1)},F=n=>{let r=g.current;if(r===null||D<0)return;let i=r.selectionStart,a=e.slice(0,D),o=e.slice(i),s=`/${n.name} `;t(a+s+o),w(!1),setTimeout(()=>{if(g.current!==null){let e=D+s.length;g.current.focus(),g.current.setSelectionRange(e,e)}},0)},I=e=>{if(C&&N.length>0){if(e.key===`ArrowDown`){e.preventDefault(),A(e=>(e+1)%N.length);return}if(e.key===`ArrowUp`){e.preventDefault(),A(e=>(e-1+N.length)%N.length);return}if(e.key===`Enter`||e.key===`Tab`){let t=N[k];if(t!==void 0){e.preventDefault(),F(t);return}}if(e.key===`Escape`){e.preventDefault(),w(!1);return}}},L=(0,c.useCallback)(()=>{let e=g.current;if(e===null)return;let t=e.getBoundingClientRect(),n=window.innerHeight,r=_.current?.offsetHeight??0,i=r>0?r:240,a=t.top-6-8,o=n-8-(t.bottom+6),s=o>a,c=Math.min(i,Math.max(s?o:a,120)),l=s?t.bottom+6:t.top-6-c;x(e=>e.left===t.left&&e.top===l&&e.width===t.width&&e.maxHeight===c?e:{position:`fixed`,left:t.left,top:l,width:t.width,maxHeight:c,zIndex:100})},[]);return(0,c.useLayoutEffect)(()=>{C&&L()}),(0,c.useEffect)(()=>{if(C)return window.addEventListener(`scroll`,L,!0),window.addEventListener(`resize`,L),()=>{window.removeEventListener(`scroll`,L,!0),window.removeEventListener(`resize`,L)}},[C,L]),(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-wrap ${d??``}`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-inner`,children:[(0,l.jsx)(`textarea`,{ref:g,className:`dsh-atb-prompt-input`,value:e,rows:i,maxLength:a,disabled:o||j,autoFocus:s,placeholder:r,"aria-label":f,onChange:e=>{t(e.target.value),P()},onKeyUp:P,onClick:P,onKeyDown:I,onPaste:e=>{let t=X(e.clipboardData.files);p&&t.length>0&&(e.preventDefault(),ne(t))},onDragOver:e=>{p&&e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);p&&t.length>0&&(e.preventDefault(),ne(t))}}),C&&N.length>0&&(0,u.createPortal)((0,l.jsxs)(`div`,{ref:_,className:`dsh-atb-slash-popup`,style:b,role:`listbox`,"aria-label":h(`slash.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-slash-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-title`,children:h(`slash.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-slash-hint`,children:h(`slash.hint`)})]}),(0,l.jsx)(`div`,{ref:v,className:`dsh-atb-slash-list`,children:N.map((e,t)=>(0,l.jsxs)(`div`,{role:`option`,"aria-selected":t===k,className:`dsh-atb-slash-item`,"data-active":t===k?`true`:void 0,"data-kind":e.kind,onClick:()=>F(e),onMouseEnter:()=>A(t),children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-badge`,"data-kind":e.kind,children:e.kind===`command`?h(`slash.badge.command`):h(`slash.badge.skill`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-slash-name`,children:[`/`,e.name]}),e.hint&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-param`,children:e.hint}),e.description&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-desc`,children:e.description})]},`${e.kind}-${e.name}`))})]}),document.body)]}),p&&(0,l.jsxs)(`div`,{className:`dsh-atb-image-actions`,children:[(0,l.jsx)(`input`,{ref:y,type:`file`,accept:`image/png,image/jpeg,image/gif,image/webp`,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&ne(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o||j,onClick:()=>y.current?.click(),children:h(j?`image.uploading`:`image.add`)}),(0,l.jsx)(`span`,{className:`dsh-atb-image-hint`,children:h(`image.hint`)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-prompt-foot`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-prompt-tip`,children:[h(`slash.tipA`),` `,(0,l.jsx)(`code`,{children:`/`}),` `,h(`slash.tipB`)]})})]})}const Je=`dsh-taskboard-last-model-v1`;function Ye(){try{let e=localStorage.getItem(Je);if(e===null)return;let t=JSON.parse(e);if(typeof t==`object`&&t){let{provider:e,model:n,reasoningEffort:r}=t;if(typeof e==`string`&&typeof n==`string`&&e.trim().length>0&&n.trim().length>0)return{provider:e.trim(),model:n.trim(),...typeof r==`string`&&r.trim().length>0?{reasoningEffort:r.trim()}:{}}}return}catch{return}}function Xe(e){try{e===void 0?localStorage.removeItem(Je):localStorage.setItem(Je,JSON.stringify(e))}catch{}}const Ze=e=>[{value:`urgent`,label:e(`form.urgency.urgent`)},{value:`normal`,label:e(`form.urgency.normal`)},{value:`relaxed`,label:e(`form.urgency.relaxed`)}],Qe=e=>[{label:e(`form.cron.daily`),cron:`0 9 * * *`},{label:e(`form.cron.hourly`),cron:`0 * * * *`},{label:e(`form.cron.every10min`),cron:`*/10 * * * *`},{label:e(`form.cron.weekly`),cron:`0 9 * * 1`}],$e=e=>[{value:`workspace-write`,label:e(`form.perm.write`),icon:`📁`},{value:`read-only`,label:e(`form.perm.readOnly`),icon:`🔒`},{value:`danger-full-access`,label:e(`form.perm.fullAccess`),icon:`⚡`}];function $({label:e,required:t=!1,full:n=!1,children:r}){return(0,l.jsxs)(`label`,{className:`dsh-atb-field`,"data-span":n?`full`:void 0,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-field-label`,children:[e,t&&(0,l.jsx)(`em`,{className:`dsh-atb-req`,children:`*`})]}),r]})}function et({rows:e,onChange:t,editing:n}){let r=W(),i=(n,r)=>{t(e.map((e,t)=>t===n?{...e,...r}:e))},a=e.filter(e=>e.checked).length;return(0,l.jsxs)(`div`,{className:`dsh-atb-cke`,children:[e.map((a,o)=>(0,l.jsxs)(`div`,{className:`dsh-atb-cke-row`,children:[n&&(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-cke-box`,checked:a.checked,title:r(`form.check.checkedTitle`,{who:a.checkedBy??r(`form.check.notCheckedYet`)}),onChange:e=>i(o,{checked:e.target.checked})}),(0,l.jsx)(`input`,{className:`dsh-atb-cke-text`,value:a.text,maxLength:200,placeholder:r(`form.check.itemPlaceholder`,{n:o+1}),spellCheck:!1,onChange:e=>i(o,{text:e.target.value})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-del`,title:r(`form.check.removeTitle`),onClick:()=>t(e.filter((e,t)=>t!==o)),children:`✕`})]},a.id??`new-${o}`)),e.length<30&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-add`,onClick:()=>t([...e,{text:``,checked:!1}]),children:r(`form.check.add`)}),e.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cke-hint`,children:n?r(`form.check.hintEdit`,{checked:a,total:e.length}):r(`form.check.hintCreate`,{n:e.length})})]})}function tt({controller:e,task:t}){let n=W(),r=e.getSnapshot(),i=r.templatePrefill,a=t!==void 0,[o,s]=(0,c.useState)(t?.title??i?.title??``),[u,d]=(0,c.useState)(t?.description??i?.description??``),[f,p]=(0,c.useState)(t?.prompt??i?.prompt??``),[m,h]=(0,c.useState)(t?.workspaceId??r.filters.workspaceId??r.workspaces[0]?.id??``),[g,_]=(0,c.useState)(t?.urgency??(i?.urgency===`urgent`||i?.urgency===`relaxed`?i.urgency:`normal`)),v=t?.execution??i?.execution,[y,b]=(0,c.useState)(v?.mode===`scheduled`?v.cron===void 0?`once`:`periodic`:`claim`),[x,te]=(0,c.useState)(t?.execution.cron??i?.execution?.cron??`0 9 * * *`),[w,T]=(0,c.useState)(t?.execution.periodicCompletion??i?.execution?.periodicCompletion??`spawn`),[E,D]=(0,c.useState)(t?.execution.reuseSessionId===void 0?t===void 0?i?.execution?.reuseSessionId===void 0?i?.execution?.sessionReuseMode??`fresh`:`selected:${i.execution.reuseSessionId}`:t.execution.sessionReuseMode??`reuse`:`selected:${t.execution.reuseSessionId}`),[k,j]=(0,c.useState)([]),[M,ne]=(0,c.useState)(!1),[N,P]=(0,c.useState)(!1),F=v?.mode===`scheduled`&&v.runAt!==void 0?v.runAt:void 0,[I,L]=(0,c.useState)(()=>{if(F===void 0)return``;let e=new Date(F);if(Number.isNaN(e.getTime()))return``;let t=e=>String(e).padStart(2,`0`);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`}),[R,re]=(0,c.useState)([]),z=a?t.model:i?.model??r.ledger.settings?.defaultModel??Ye(),[ie,B]=(0,c.useState)(z===void 0?``:JSON.stringify({provider:z.provider,model:z.model})),[ae,oe]=(0,c.useState)(z?.reasoningEffort??``),se=a?t.presetId??``:i?.presetId??r.ledger.settings?.defaultPresetId??``,[V,ce]=(0,c.useState)(se),[H,le]=(0,c.useState)([]),[U,ue]=(0,c.useState)(void 0),[G,de]=(0,c.useState)(t?.permission??(i?.permission?S(i.permission):C(r.ledger.settings))),[fe,pe]=(0,c.useState)(t?.isolation??(i?.isolation===`none`?`none`:i?.isolation===`worktree`?`worktree`:ee(r.ledger.settings))),[me,he]=(0,c.useState)(t?.checklist!==void 0&&t.checklist.length>0?t.checklist.map(e=>({...e})):(i?.checklist??[]).map(e=>({text:e,checked:!1}))),ge=(0,c.useRef)(null),[K,q]=(0,c.useState)(!1),[_e,ve]=(0,c.useState)(!1);(0,c.useEffect)(()=>{ge.current?.focus();let t=t=>{t.key===`Escape`&&e.closeForm()};return document.addEventListener(`keydown`,t),()=>document.removeEventListener(`keydown`,t)},[e]),(0,c.useEffect)(()=>{e.fetchModelCatalog().then(re).catch(()=>re([]))},[e]),(0,c.useEffect)(()=>{if(m===``)return;let t=!0;return ne(!0),P(!1),e.fetchProjectSessions(m).then(e=>{t&&j(e)}).catch(()=>{t&&(j([]),P(!0))}).finally(()=>{t&&ne(!1)}),()=>{t=!1}},[e,m]),(0,c.useEffect)(()=>{e.fetchPresetCatalog().then(e=>{le(e.presets),ue(e.defaultId),!a&&se===``&&e.defaultId!==void 0&&ce(e.defaultId)}).catch(()=>le([]))},[e,a,t?.presetId,se]);let ye=y===`periodic`?O(x.trim()):null,be=ye===null?null:A(ye,Date.now()),xe=y===`periodic`&&(ye===null||be===null),Se=y===`once`&&I!==``?new Date(I).getTime():NaN,Ce=y===`once`&&(I===``||Number.isNaN(Se)||Se<=Date.now()),we=o.trim().length>0&&m!==``&&!xe&&!Ce&&(y===`claim`||!E.startsWith(`selected:`)||!M&&k.some(e=>e.id===E.slice(9))),Te=()=>{let e=E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{sessionReuseMode:E===`reuse`?`reuse`:`fresh`};return y===`periodic`?{mode:`scheduled`,cron:x.trim(),periodicCompletion:w,...e}:y===`once`?{mode:`scheduled`,runAt:new Date(I).toISOString(),...E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{}}:{mode:`claim`}},Ee=a&&t.status===`in_progress`,Y=a&&((t.executions?.length??0)>0||t.status===`in_progress`),De=e.gitAvailable(m),Oe=e.repoCount(m),ke=Y||!De,Ae=()=>{if(De)return fe},je=()=>V.trim().length>0?V.trim():void 0,Me=()=>me.map(e=>({...e,text:e.text.trim()})).filter(e=>e.text.length>0),X=ie===``?void 0:JSON.parse(ie),Ne=X===void 0?void 0:R.find(e=>e.provider===X.provider&&e.model===X.model),Z=Ne?.reasoning,Pe=()=>{if(X===void 0)return;let e=ae.trim();return{provider:X.provider,model:X.model,...e.length>0?{reasoningEffort:e}:{}}},Fe=()=>{if(!we||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(a?e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null}):e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}})).catch(()=>void 0).finally(()=>q(!1))},Q=()=>{if(!we||Ee||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(async()=>{if(a)await e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null})&&await e.run(t.id);else{let t=await e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}});t!==void 0&&await e.run(t)}})().catch(()=>void 0).finally(()=>q(!1))},Ie=we?y===`periodic`&&be!==null?n(`form.hint.nextRun`,{time:J(be)}):y===`once`&&!Number.isNaN(Se)?n(`form.hint.onceAt`,{time:J(Se)}):a?n(`form.hint.saveVersion`,{v:t.version,next:t.version+1}):n(`form.hint.createClaim`):o.trim().length===0?n(`form.hint.needTitle`):n(m===``?`form.hint.needProject`:Ce?`form.hint.runAtBad`:`form.hint.cronBad`);return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-taskform-modal`,"data-mode":a?`edit`:`create`,role:`dialog`,"aria-modal":`true`,"aria-label":n(a?`form.title.edit`:`form.title.create`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:a?`✎`:`✚`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:n(a?`form.title.edit`:`form.title.create`)}),(0,l.jsx)(`p`,{children:n(a?`form.subtitle.edit`:`form.subtitle.create`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeForm(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body dsh-atb-taskform-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-left`,children:[(0,l.jsx)($,{label:n(`form.field.title`),required:!0,full:!0,children:(0,l.jsx)(`input`,{ref:ge,value:o,onChange:e=>s(e.target.value),placeholder:n(`form.field.titlePlaceholder`),maxLength:200})}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.project`),required:!0,children:(0,l.jsx)(`select`,{value:m,onChange:e=>{h(e.target.value),D(`fresh`),j([])},children:r.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))})}),(0,l.jsx)($,{label:n(`form.field.model`),children:(0,l.jsxs)(`select`,{value:ie,onChange:e=>{let t=e.target.value;if(B(t),t===``)oe(``);else{let e=JSON.parse(t),n=R.find(t=>t.provider===e.provider&&t.model===e.model);n?.reasoning?.defaultEffort===void 0?oe(``):oe(n.reasoning.defaultEffort)}},children:[(0,l.jsx)(`option`,{value:``,children:n(`form.field.modelDefault`)}),X!==void 0&&Ne===void 0&&(0,l.jsxs)(`option`,{value:ie,children:[X.model,` (`,X.provider,`)`]}),R.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:n(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})}),X!==void 0&&(0,l.jsx)($,{label:n(`form.field.effort`),children:(0,l.jsxs)(`select`,{value:ae,onChange:e=>oe(e.target.value),title:n(`form.field.effortTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.effort.follow`),Z?.defaultEffort===void 0?``:n(`shared.current`,{name:Z.efforts.find(e=>e.id===Z.defaultEffort)?.name??Z.defaultEffort})]}),Z!==void 0&&Z.efforts.length>0?Z.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id)):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`option`,{value:`low`,children:n(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:n(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:n(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:n(`form.effort.none`)})]})]})}),(H.length>0||V!==``)&&(0,l.jsx)($,{label:n(`form.field.preset`),children:(0,l.jsxs)(`select`,{value:V,onChange:e=>ce(e.target.value),title:n(`form.field.presetTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.preset.follow`),U===void 0?``:n(`shared.current`,{name:H.find(e=>e.id===U)?.name??U})]}),V!==``&&!H.some(e=>e.id===V)&&(0,l.jsx)(`option`,{value:V,children:V}),H.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===U?n(`form.preset.defaultTag`):``]},e.id))]})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.permission`),children:(0,l.jsx)(`select`,{className:`dsh-atb-permission-select`,value:G,onChange:e=>de(S(e.target.value)),children:$e(n).map(e=>(0,l.jsxs)(`option`,{value:e.value,children:[e.icon,` `,e.label,e.value===`workspace-write`?n(`form.perm.defaultTag`):``]},e.value))})}),(0,l.jsx)($,{label:n(`form.field.urgency`),children:(0,l.jsx)(`select`,{className:`dsh-atb-urgency-select`,value:g,onChange:e=>_(e.target.value),children:Ze(n).map(e=>(0,l.jsx)(`option`,{value:e.value,children:e.label},e.value))})})]}),(0,l.jsx)($,{label:n(`form.field.mode`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-exec":`true`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`claim`,onClick:()=>b(`claim`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.claim`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.claimHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`once`,onClick:()=>b(`once`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.once`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.onceHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`periodic`,onClick:()=>b(`periodic`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.periodic`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.periodicHint`)})]})]})}),y===`once`&&(0,l.jsx)($,{label:n(`form.field.runAt`),required:!0,full:!0,children:(0,l.jsx)(`input`,{type:`datetime-local`,className:Ce?`dsh-atb-input-bad`:void 0,value:I,onChange:e=>L(e.target.value),spellCheck:!1})}),y===`periodic`&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)($,{label:n(`form.field.cron`),required:!0,full:!0,children:[(0,l.jsx)(`input`,{className:xe?`dsh-atb-input-bad`:void 0,value:x,onChange:e=>te(e.target.value),placeholder:n(`form.cron.placeholder`),spellCheck:!1}),(0,l.jsxs)(`span`,{className:`dsh-atb-cron-presets`,children:[Qe(n).map(e=>(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cron-preset`,"data-on":x.trim()===e.cron,onClick:()=>te(e.cron),children:e.label},e.cron)),!xe&&be!==null&&(0,l.jsx)(`span`,{className:`dsh-atb-cron-next`,children:n(`form.cron.next`,{time:J(be)})})]})]}),(0,l.jsx)($,{label:n(`form.field.periodicCompletion`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`rearm`,onClick:()=>T(`rearm`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.rearm`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.rearmHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`spawn`,onClick:()=>T(`spawn`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.spawn`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.spawnHint`)})]})]})})]}),y===`periodic`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E,onChange:e=>D(e.target.value),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.fresh`)}),(0,l.jsx)(`option`,{value:`reuse`,children:n(`form.session.reuse`)}),k.map(e=>(0,l.jsx)(`option`,{value:`selected:${e.id}`,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),y===`once`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E.startsWith(`selected:`)?E:`fresh`,onChange:e=>D(e.target.value===`fresh`?`fresh`:`selected:${e.target.value}`),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.new`)}),k.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),(0,l.jsxs)($,{label:n(`form.field.isolation`),full:!0,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-disabled":ke?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`worktree`,disabled:ke,title:n(Y?`form.iso.locked`:De?`form.iso.worktreeTitle`:`form.iso.nonGit`),onClick:()=>pe(`worktree`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.worktree`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.worktreeHint`:`form.iso.nonGit`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`none`,disabled:ke,title:n(Y?`form.iso.locked`:`form.iso.noneTitle`),onClick:()=>pe(`none`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.none`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.noneHint`:`form.iso.noneHintNonGit`)})]})]}),!De&&!Y&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.iso.nonGitNote`)}),De&&!Y&&Oe>1&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,"data-mirror":`true`,children:n(`form.iso.mirrorNote`,{n:Oe})})]}),(0,l.jsx)($,{label:n(a?`form.field.checklist`:`form.field.checklistOptional`),full:!0,children:(0,l.jsx)(et,{rows:me,onChange:he,editing:a})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-right`,children:[(0,l.jsx)($,{label:n(a?`form.field.description`:`form.field.descriptionOptional`),full:!0,children:(0,l.jsx)(qe,{value:u,onChange:d,controller:e,rows:7,placeholder:n(`form.desc.placeholder`),allowImages:!0,onUploadingChange:ve})}),(0,l.jsx)($,{label:n(a?`form.field.prompt`:`form.field.promptOptional`),full:!0,children:(0,l.jsx)(qe,{value:f,onChange:p,controller:e,rows:7,placeholder:n(`form.prompt.placeholder`)})})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,"data-tone":we?void 0:`bad`,children:Ie}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeForm(),children:n(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!we||Ee||K||_e,title:n(Ee?`form.action.runBlockedTitle`:K||_e?`form.action.runBusyTitle`:`form.action.runTitle`),onClick:Q,children:n(`form.action.run`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!we||K||_e,onClick:Fe,children:n(a?`form.action.save`:`form.action.create`)})]})]})]})})}const nt=e=>[{value:`none`,name:e(`form.iso.none`),hint:e(`set.iso.noneHint`)},{value:`worktree`,name:e(`form.iso.worktree`),hint:e(`set.iso.worktreeHint`)}];function rt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.ledger.settings?.defaultIsolation??`none`,i=te(n.ledger.settings),a=C(n.ledger.settings),o=w(n.ledger.settings),s=T(n.ledger.settings),u=E(n.ledger.settings),d=D(n.ledger.settings),f=n.ledger.settings?.defaultModel,p=f===void 0?``:JSON.stringify({provider:f.provider,model:f.model}),m=f?.reasoningEffort??``,h=n.ledger.settings?.defaultPresetId??``,[g,_]=(0,c.useState)([]),[v,y]=(0,c.useState)([]),[b,x]=(0,c.useState)(),[S,ee]=(0,c.useState)(p),[O,k]=(0,c.useState)(m),[A,j]=(0,c.useState)(h),M=S===``?void 0:JSON.parse(S),ne=g.find(e=>e.provider===M?.provider&&e.model===M?.model),N=ne?.reasoning,[P,F]=(0,c.useState)(r),[I,L]=(0,c.useState)(i),[R,re]=(0,c.useState)(a),[z,ie]=(0,c.useState)(String(o)),[B,ae]=(0,c.useState)(String(s)),[oe,se]=(0,c.useState)(String(u)),[V,ce]=(0,c.useState)(String(d)),[H,le]=(0,c.useState)(n.storage?.currentDirectory??``),[U,ue]=(0,c.useState)(!1),[G,de]=(0,c.useState)(!1),fe=Number(z),pe=Number(B),me=Number(oe),he=Number(V),ge=Number.isSafeInteger(fe)&&fe>=1&&fe<=100&&Number.isSafeInteger(pe)&&pe>=1&&pe<=1440&&Number.isSafeInteger(me)&&me>=0&&me<=10080&&Number.isSafeInteger(he)&&he>=0&&he<=6e4,K=S!==p||O!==m||A!==h||P!==r||I!==i||R!==a||z!==String(o)||B!==String(s)||oe!==String(u)||V!==String(d),q=H.trim().length===0?n.storage?.defaultDirectory??``:H.trim(),_e=n.storage!==void 0&&q!==n.storage.currentDirectory;return(0,c.useEffect)(()=>{!U&&n.storage!==void 0&&le(n.storage.currentDirectory)},[n.storage,U]),(0,c.useEffect)(()=>{let t=!0;return e.fetchModelCatalog().then(e=>{t&&_(e)}).catch(()=>{}),e.fetchPresetCatalog().then(e=>{t&&(y(e.presets),x(e.defaultId))}).catch(()=>{}),()=>{t=!1}},[e]),(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeSettings()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-set`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`set.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`🛠`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`set.title`)}),(0,l.jsx)(`p`,{children:t(`set.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeSettings(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.iso.heading`)}),(0,l.jsx)(`div`,{className:`dsh-atb-mode-picker`,children:nt(t).map(e=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":P===e.value,title:e.hint,onClick:()=>F(e.value),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:e.name}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:e.hint})]},e.value))}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.iso.current`,{current:t(r===`worktree`?`form.iso.worktree`:`form.iso.none`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.sync.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":!I,title:t(`set.sync.off.title`),onClick:()=>L(!1),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.off.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.off.hint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":I,title:t(`set.sync.on.title`),onClick:()=>L(!0),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.on.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.on.hint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(i?`set.sync.stateOn`:`set.sync.stateOff`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.perm.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-perm-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`workspace-write`,onClick:()=>re(`workspace-write`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.writeName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.writeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`read-only`,onClick:()=>re(`read-only`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.readOnlyName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.readOnlyHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`danger-full-access`,onClick:()=>re(`danger-full-access`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.fullName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.fullHint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.perm.current`,{current:t(a===`read-only`?`set.perm.readOnlyName`:a===`danger-full-access`?`set.perm.fullName`:`set.perm.writeName`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.agent.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.agent.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.model`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:S,onChange:e=>{ee(e.target.value),k(``)},children:[(0,l.jsx)(`option`,{value:``,children:t(`form.field.modelDefault`)}),M!==void 0&&ne===void 0&&(0,l.jsxs)(`option`,{value:S,children:[M.model,` (`,M.provider,`)`]}),g.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:t(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.effort`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:O,disabled:M===void 0,onChange:e=>k(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.effort.follow`),N?.defaultEffort===void 0?``:t(`shared.current`,{name:N.efforts.find(e=>e.id===N.defaultEffort)?.name??N.defaultEffort})]}),O!==``&&N!==void 0&&!N.efforts.some(e=>e.id===O)&&(0,l.jsx)(`option`,{value:O,children:O}),N===void 0?(0,l.jsxs)(l.Fragment,{children:[![``,`low`,`medium`,`high`,`none`].includes(O)&&(0,l.jsx)(`option`,{value:O,children:O}),(0,l.jsx)(`option`,{value:`low`,children:t(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:t(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:t(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:t(`form.effort.none`)})]}):N.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.preset`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:A,onChange:e=>j(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.preset.follow`),b===void 0?``:t(`shared.current`,{name:v.find(e=>e.id===b)?.name??b})]}),A!==``&&!v.some(e=>e.id===A)&&(0,l.jsx)(`option`,{value:A,children:A}),v.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===b?t(`form.preset.defaultTag`):``]},e.id))]})]})]})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.schedule.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.schedule.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-settings-numbers`,children:[(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.concurrent`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`100`,step:`1`,value:z,onChange:e=>ie(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.missedAfter`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`1440`,step:`1`,value:B,onChange:e=>ae(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.queueMaxAge`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`10080`,step:`1`,value:oe,onChange:e=>se(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.dispatchInterval`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`60000`,step:`1`,value:V,onChange:e=>ce(e.target.value)})]})]}),!ge&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:t(`set.schedule.invalid`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.storage.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.storage.hint`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-storage-path`,value:H,disabled:n.storage===void 0||G,placeholder:n.storage?.defaultDirectory??t(`set.storage.loading`),onChange:t=>{le(t.target.value),ue(!0),e.dismissStorageNotice()}}),n.storageNotice!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-notice`,role:`status`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-storage-notice-ok`,children:[`✓ `,t(`set.storage.migrated`,{path:n.storageNotice.path})]}),n.storageNotice.warnings.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-notice-warn`,children:t(`set.storage.warnings`,{warnings:n.storageNotice.warnings.join(`; `)})})]}),n.storage!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-meta`,children:[(0,l.jsx)(`span`,{children:t(`set.storage.current`,{path:n.storage.currentDirectory})}),(0,l.jsx)(`span`,{children:t(`set.storage.assets`,{count:n.storage.assetCount,size:(n.storage.assetBytes/1024/1024).toFixed(1)})}),n.storage.error!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:n.storage.error})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-storage-actions`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G,onClick:()=>{le(n.storage?.defaultDirectory??``),ue(!0)},children:t(`set.storage.default`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G||q.length===0,onClick:()=>{de(!0),e.checkStorage(q).finally(()=>de(!1))},children:t(`set.storage.check`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!_e||G,onClick:()=>{window.confirm(t(`set.storage.confirm`,{from:n.storage?.currentDirectory??``,to:q}))&&(de(!0),e.migrateStorage(q).then(e=>{e&&(ue(!1),le(q))}).finally(()=>de(!1)))},children:t(G?`set.storage.migrating`:`set.storage.migrate`)})]})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(K?`set.foot.dirty`:`set.foot.clean`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeSettings(),children:t(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!K||!ge,onClick:()=>{e.updateSettings({defaultIsolation:P,syncExternalSessions:I,defaultPermission:R,...M===void 0?{}:{defaultModel:{...M,...O===``?{}:{reasoningEffort:O}}},...A===``?{}:{defaultPresetId:A},maxConcurrent:fe,scheduleMissedAfterMinutes:pe,queueMaxAgeMinutes:me,dispatchIntervalMs:he}).then(t=>{t&&e.closeSettings()})},children:t(`set.action.save`)})]})]})]})})}function it({row:e}){return(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,title:e.id,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.status})]})}function at({controller:e}){let t=W(),[n,r]=(0,c.useState)(``),[i,a]=(0,c.useState)(null),[o,s]=(0,c.useState)(void 0),[u,d]=(0,c.useState)(void 0),[f,p]=(0,c.useState)(`merge`),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(void 0),[v,y]=(0,c.useState)(!1),b=(0,c.useRef)(null),{alert:x,el:S}=Y(),ee=n=>{d(void 0),s(void 0),_(void 0),y(!1),r(``),a(null),n!==void 0&&n.text().then(i=>{try{let t=JSON.parse(i);a(t),r(n.name),e.importPreview(t).then(e=>{e!==void 0&&d(e)})}catch{s(t(`imp.parseError`))}})},te=()=>{if(!(i===null||u===void 0||m)){if(f===`replace`&&!v){y(!0);return}h(!0),e.importCommit(f,i).then(e=>{h(!1),y(!1),e!==void 0&&_(e.mode===`replace`?t(`imp.result.replace`,{n:e.created+e.overwritten,total:e.replacedTotal??0}):t(`imp.result.merge`,{n:e.created,m:e.overwritten}))})}},C=()=>e.closeImport();return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&C()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-imp`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`imp.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⬆`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`imp.title`)}),(0,l.jsx)(`p`,{children:t(`imp.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:C,children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-picker`,children:[(0,l.jsx)(`input`,{ref:b,type:`file`,accept:`.json,application/json`,onChange:e=>ee(e.target.files?.[0])}),n.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-imp-filename`,children:n})]}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-note`,children:t(`imp.note`)}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-error`,children:o}),u===void 0&&o===void 0&&n.length>0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`imp.previewing`)}),u!==void 0&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stats`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`ok`,children:[(0,l.jsx)(`b`,{children:u.create.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.create`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`warn`,children:[(0,l.jsx)(`b`,{children:u.overwrite.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.overwrite`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":u.invalid.length>0?`bad`:void 0,children:[(0,l.jsx)(`b`,{children:u.invalid.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.invalid`)})]})]}),u.create.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.create`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.create.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.overwrite.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.overwrite`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.overwrite.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.invalid.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.invalid`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.invalid.map((e,n)=>(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,"data-tone":`bad`,title:e.id??``,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.id??t(`imp.noId`)}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.reason})]},e.id??`invalid-${n}`))})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`merge`,onClick:()=>{p(`merge`),y(!1)},children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.merge`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.mergeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`replace`,onClick:()=>p(`replace`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.replace`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.replaceHint`)})]})]}),g!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-result`,children:g})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(f===`replace`?v?`imp.foot.replaceConfirm`:`imp.foot.replaceNeedConfirm`:`imp.foot.mergeHint`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:C,children:t(g===void 0?`shared.cancel`:`shared.close`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,"data-danger":f===`replace`&&v?`true`:void 0,disabled:u===void 0||m,onClick:te,children:t(f===`replace`&&v?`imp.action.confirmReplace`:`imp.action.run`)})]})]})]}),S]})}function ot({controller:e}){let t=W(),n=e.getSnapshot(),[r,i]=(0,c.useState)({}),[a,o]=(0,c.useState)(void 0),{alert:s,el:u}=Y(),d=()=>e.closeTemplateManager(),f=e=>r[e.id]??xe(e),p=e=>{if(e===void 0)return``;let n=q[e];return n===void 0?` · ${e}`:` · ${t(n)}`},m=(r,a)=>{let o=n.templates.find(e=>e.id===r);o===void 0||a===xe(o)||e.upsertTemplate({id:r,name:a,task:o.task}).then(e=>{e&&(i(e=>{let t={...e};return delete t[r],t}),s(t(`tpl.renamed`)))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&d()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-tplm`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`tpl.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⌗`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`tpl.title`)}),(0,l.jsx)(`p`,{children:t(`tpl.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:d,children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:n.templates.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`tpl.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-tplm-list`,children:n.templates.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-tplm-row`,children:[(0,l.jsx)(`input`,{className:`dsh-atb-tplm-name`,value:f(n),maxLength:60,spellCheck:!1,"aria-label":t(`tpl.name.aria`,{name:xe(n)}),onChange:e=>i(t=>({...t,[n.id]:e.target.value})),onKeyDown:e=>{e.key===`Enter`&&m(n.id,f(n))}}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-meta`,title:`${n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`)}${n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``}${p(n.task.urgency)}${n.task.permission===void 0?``:` · ${t(`shared.permission`)}: ${n.task.permission}`}`,children:[n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`),n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``,p(n.task.urgency),n.task.permission!==void 0&&n.task.permission!==`workspace-write`?` · ${n.task.permission===`read-only`?t(`tpl.meta.permReadOnly`):t(`tpl.meta.permFull`)}`:``]}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-btns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:f(n)===xe(n)||f(n).trim().length===0,title:t(`tpl.rename.title`),onClick:()=>m(n.id,f(n)),children:t(`tpl.rename.button`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`tpl.use.title`),onClick:()=>{d(),e.newFromTemplate(Se(n))},children:t(`tpl.use.button`)}),a===n.id?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{e.deleteTemplate(n.id),o(void 0)},children:t(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(void 0),children:t(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,title:t(`tpl.delete.title`),onClick:()=>o(n.id),children:`🗑`})]})]},n.id))})}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(`tpl.foot.hint`)}),(0,l.jsx)(`span`,{className:`dsh-atb-modal-footbtns`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:d,children:t(`shared.close`)})})]})]}),u]})}const st={urgent:0,normal:1,relaxed:2};function ct(e,t){let n=e.search.trim().toLowerCase(),r=t.filter(t=>(e.filters.workspaceId===void 0||t.workspaceId===e.filters.workspaceId)&&(e.filters.urgencies.length===0||e.filters.urgencies.includes(t.urgency))&&(n.length===0||t.title.toLowerCase().includes(n)||t.id.toLowerCase().includes(n)));if(e.sortBy==="default")return r;let i=[...r];return e.sortBy===`updated`?i.sort((e,t)=>t.updatedAt-e.updatedAt):e.sortBy===`created`?i.sort((e,t)=>t.createdAt-e.createdAt):e.sortBy===`urgency`?i.sort((e,t)=>st[e.urgency]-st[t.urgency]||t.updatedAt-e.updatedAt):e.sortBy===`title`&&i.sort((e,t)=>e.title.localeCompare(t.title,void 0,{numeric:!0})),i}function lt({controller:e}){let t=W(),n=(0,c.useSyncExternalStore)(t=>e.subscribe(t),()=>e.getSnapshot()),[r,i]=(0,c.useState)(()=>Date.now());(0,c.useEffect)(()=>{let e=setInterval(()=>i(Date.now()),6e4);return()=>clearInterval(e)},[]);let a=ct(n,n.ledger.tasks.filter(e=>e.trashedAt===void 0)),o=n.selectedId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.selectedId),{alert:s,el:u}=Y(),[d,f]=(0,c.useState)(!1),p=()=>f(!1),[m,h]=(0,c.useState)(!1),g=()=>h(!1);return(0,l.jsxs)(`div`,{className:`dsh-atb-board`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-toolbar`,children:[(0,l.jsx)(`h2`,{className:`dsh-atb-title`,children:t(`board.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-count`,children:t(`board.count.tasks`,{n:a.length,rev:n.ledger.revision})}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{let t=!d;f(t),t&&e.prepareTemplateMenu()},children:t(`board.action.newTask`)}),d&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:p}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.setComposer(!0)},children:t(`board.action.blankTask`)}),n.templates.map(t=>{let n=xe(t),r=Se(t);return(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:r.description!==void 0&&r.description.length>0?r.description.slice(0,120):n,onClick:()=>{p(),e.newFromTemplate(r)},children:n},t.id)}),(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-sep`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.openTemplateManager()},children:t(`board.action.manageTemplates`)})]})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-spacer`}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-search`,value:n.search,placeholder:t(`board.search.placeholder`),spellCheck:!1,onChange:t=>e.setSearch(t.target.value)}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.filters.workspaceId??``,onChange:t=>e.setWorkspaceFilter(t.target.value===``?void 0:t.target.value),children:[(0,l.jsx)(`option`,{value:``,children:t(`board.filter.allProjects`)}),n.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))]}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.sortBy,title:t(`board.sort.title`),onChange:t=>e.setSortBy(t.target.value),children:[(0,l.jsx)(`option`,{value:`default`,children:t(`board.sort.default`)}),(0,l.jsx)(`option`,{value:`updated`,children:t(`board.sort.updated`)}),(0,l.jsx)(`option`,{value:`urgency`,children:t(`board.sort.urgency`)}),(0,l.jsx)(`option`,{value:`created`,children:t(`board.sort.created`)}),(0,l.jsx)(`option`,{value:`title`,children:t(`board.sort.byTitle`)})]}),[`urgent`,`normal`,`relaxed`].map(r=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip`,"data-urgency":r,"data-on":n.filters.urgencies.includes(r),onClick:()=>e.toggleUrgency(r),children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-urgency":r}),t(q[r])]},r)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.toggleSecondary(),children:n.secondaryOpen?t(`board.action.backToBoard`):t(`board.action.otherTasks`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.settingsTitle`),onClick:()=>e.openSettings(),children:t(`board.action.settings`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.diagTitle`),onClick:()=>e.openDiagnostics(),children:t(`board.action.diag`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.importTitle`),onClick:()=>e.openImport(),children:t(`board.action.import`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.exportTitle`),onClick:()=>h(!m),children:t(`board.action.export`)}),m&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:g}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.jsonTitle`),onClick:()=>{g(),e.exportJson()},children:t(`board.export.json`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.csvTitle`),onClick:()=>{g(),e.exportCsv()},children:t(`board.export.csv`)})]})]})]}),(0,l.jsxs)(`a`,{className:`dsh-atb-ver`,href:`https://github.com/cloader/dsh-taskboard`,target:`_blank`,rel:`noopener noreferrer`,children:[`V`,`0.8.6`]})]}),n.error!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-error`,children:n.error}),n.secondaryOpen?(0,l.jsx)(ft,{controller:e,tasks:ct(n,n.ledger.tasks)}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(i=>{let o=a.filter(e=>e.status===i);return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,onDragOver:e=>{e.dataTransfer.types.includes(`application/x-dsh-atb-task`)&&(e.preventDefault(),e.dataTransfer.dropEffect=`move`,e.currentTarget.dataset.dragover=`true`)},onDragLeave:e=>{delete e.currentTarget.dataset.dragover},onDrop:r=>{r.preventDefault(),delete r.currentTarget.dataset.dragover;let a=r.dataTransfer.getData(we);if(a.length===0)return;let o=n.ledger.tasks.find(e=>e.id===a);if(!(o===void 0||o.status===i)){if(!x(o.status,i)){s(t(`board.drag.forbidden`,{from:t(he[o.status]),to:t(he[i])}));return}e.move(a,o.version,i)}},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":i}),t(he[i]),(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:o.length}),i===`in_progress`&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-queuechip`,title:t(`queue.panel.title`),"data-bad":(n.queue?.depth??0)>0?`true`:void 0,onClick:()=>e.openQueuePanel(),children:t(`board.queue.chip`,{n:n.queue?.depth??0})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[o.map(t=>(0,l.jsx)(Ee,{task:t,controller:e,draggable:!0,now:r,onAlert:s},t.id)),o.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:t(`board.empty`)})]})]},i)})}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-detailpanel`,children:(0,l.jsx)(We,{task:o,controller:e,now:r},o.id)}),n.composerOpen&&(0,l.jsx)(tt,{controller:e,task:n.editingId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.editingId)}),n.diagOpen&&(0,l.jsx)(dt,{controller:e}),n.queueOpen&&(0,l.jsx)(ut,{controller:e,onAlert:s}),n.settingsOpen&&(0,l.jsx)(rt,{controller:e}),n.importOpen&&(0,l.jsx)(at,{controller:e}),n.tplManagerOpen&&(0,l.jsx)(ot,{controller:e}),u]})}function ut({controller:e,onAlert:t}){let n=W(),r=e.getSnapshot().queue,[i,a]=(0,c.useState)(!1),[o,s]=(0,c.useState)(!1),u=r?.depth??0,d=r?.oldestWaitMinutes,f=async()=>{if(!i){a(!0);return}s(!0);let r=await e.clearQueue();s(!1),a(!1),r!==void 0&&(t(n(`queue.cleared`,{n:r.cleared})),e.closeQueuePanel())};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeQueuePanel()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-queuepanel`,role:`dialog`,"aria-modal":`true`,"aria-label":n(`queue.panel.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`📋`}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-headtext`,children:(0,l.jsx)(`h3`,{children:n(`queue.panel.title`)})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeQueuePanel(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":u>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:u}),(0,l.jsx)(`span`,{children:n(`queue.depth`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":d!==void 0&&d>5?`true`:void 0,children:[(0,l.jsxs)(`b`,{children:[d??0,`m`]}),(0,l.jsx)(`span`,{children:n(`queue.wait`)})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-queuepanel-actions`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:u===0||o,"data-armed":i?`true`:void 0,onClick:()=>{f()},children:n(i?`queue.confirm`:`queue.clear`)})})]})]})})}function dt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.diagnostics,i=e=>{let t=n.workspaces.find(t=>t.id===e);return t?.title??t?.path??e.slice(0,8)};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeDiagnostics()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-diag`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`diag.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⚙`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`diag.title`)}),(0,l.jsx)(`p`,{children:t(`diag.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeDiagnostics(),children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:r===void 0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`shared.loading`)}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.revision}),(0,l.jsx)(`span`,{children:t(`diag.revision`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.tasks}),(0,l.jsx)(`span`,{children:t(`diag.tasks`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.staleRunning>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.staleRunning}),(0,l.jsx)(`span`,{children:t(`diag.running`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.orphanWorktrees.length>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.orphanWorktrees.length}),(0,l.jsx)(`span`,{children:t(`diag.orphans`)})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.orphans.heading`)}),r.orphanWorktrees.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.orphanWorktrees.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-diag-orphan`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:n.path,children:[i(n.workspaceId),` · `,n.taskId]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void e.cleanupOrphan(n.workspaceId,n.taskId),children:t(`diag.orphans.cleanup`)})]},n.path))}),(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.hint`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.gitignore.heading`)}),(r.gitIgnoreSuggestions??[]).length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.gitignore.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.gitIgnoreSuggestions.map(e=>(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphan`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:e.workspacePath,children:[i(e.workspaceId),` · `,t(`diag.gitignore.suggestA`),` `,(0,l.jsx)(`code`,{children:`.dsh-worktrees/`}),t(`diag.gitignore.suggestB`)]})},e.workspaceId))})]})]})})]})})}function ft({controller:e,tasks:t}){let n=W(),[r,i]=(0,c.useState)(`none`),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(()=>new Set),d=t.filter(e=>e.trashedAt!==void 0),f=t.filter(e=>e.trashedAt===void 0&&e.status===`archived`),p=t.filter(e=>e.trashedAt===void 0&&e.status===`canceled`),m=e=>{u(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},h=e=>{u(t=>{let n=e.length>0&&e.every(e=>t.has(e.id)),r=new Set(t);for(let t of e)n?r.delete(t.id):r.add(t.id);return r})},g=async t=>{let n=(t===`trashArchived`?f:d).filter(e=>s.has(e.id));o(!0);try{for(let r of n){try{await e.remove(r.id,r.version,t===`purgeTrashed`)}catch{}u(e=>{let t=new Set(e);return t.delete(r.id),t})}}finally{o(!1),i(`none`),u(new Set)}},_=()=>{i(`none`),u(new Set)},v=[{label:n(`status.column.canceled`),dot:`canceled`,rows:p},{label:n(`status.column.archived`),dot:`archived`,rows:f,action:`trashArchived`},{label:n(`board.group.trashed`),dot:`trashed`,rows:d,action:`purgeTrashed`}];return d.length+f.length+p.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-secondary`,children:(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.secondary.empty`)})}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(t=>{let o=r===t.action,c=o?t.rows.filter(e=>s.has(e.id)).length:0,d=t.rows.length>0&&c===t.rows.length;return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":t.dot}),t.label,(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:t.rows.length}),t.action&&t.rows.length>0&&(o?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:n(`board.batch.confirmCancelTip`),onClick:_,children:n(`board.batch.cancel`)}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:a||c===0,title:n(c===0?`board.batch.noneSelected`:`board.batch.confirmTip`),onClick:()=>{g(t.action)},children:[n(`board.batch.confirm`),`(`,c,`)`]}),(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selectall`,checked:d,disabled:a,title:n(`board.batch.selectAll`),onClick:e=>e.stopPropagation(),onChange:()=>h(t.rows)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:t.action===`trashArchived`?n(`board.batch.trashArchivedTip`):n(`board.batch.purgeTrashedTip`),onClick:()=>{i(t.action),u(new Set)},children:t.action===`trashArchived`?n(`board.batch.trashArchived`):n(`board.batch.purgeTrashed`)}))]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[t.rows.map(n=>t.action&&o?(0,l.jsxs)(`div`,{className:`dsh-atb-selrow`,children:[(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selbox`,checked:s.has(n.id),disabled:a,onClick:e=>e.stopPropagation(),onChange:()=>m(n.id)}),(0,l.jsx)(Ee,{task:n,controller:e})]},n.id):(0,l.jsx)(Ee,{task:n,controller:e},n.id)),t.rows.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.empty`)})]})]},t.label)})})}function pt(e){let t=()=>{if((document.body.getAttribute(`data-dsh-desktop-platform`)??new URLSearchParams(window.location.search).get(`dsh-desktop-platform`))!==`win32`){delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`);return}e.dataset.dshAtbWindows=``,e.style.setProperty(`--dsh-atb-viewport-top`,`${e.getBoundingClientRect().top}px`)},n,r=()=>{n===void 0&&(n=requestAnimationFrame(()=>{n=void 0,t()}))},i=new MutationObserver(r);i.observe(document.body,{attributes:!0,childList:!0,subtree:!0,attributeFilter:[`class`,`data-dsh-desktop-platform`,`data-dsh-desktop-mode`,`data-details-collapsed`]}),i.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-dsh-atb-active`]});let a=typeof ResizeObserver>`u`?void 0:new ResizeObserver(r);return a?.observe(e),e.parentElement!==null&&a?.observe(e.parentElement),window.addEventListener(`resize`,r),r(),()=>{i.disconnect(),a?.disconnect(),window.removeEventListener(`resize`,r),n!==void 0&&cancelAnimationFrame(n),delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`)}}function mt(){let e=document.querySelector(`[data-pane="sidebar"], [class*="sidebarCol"], .dshDesktopUpstreamSidebar, .dshDesktopSidebarSurface`);if(e!==null)return e.querySelector(`[class*="logoRow"]`)?.parentElement??e.firstElementChild}function ht(e){let t=e.querySelector(`button[class*="newSession"]`);if(t!==null)return t;for(let t of e.children)if(t instanceof HTMLButtonElement&&!t.matches(`[data-dsh-atb-entry]`))return t;let n=e.querySelector(`button[aria-label="新建会话"], button[aria-label="New Session"], button[aria-label*="新会话"], button[aria-label*="new session" i]`);return n===null?Array.from(e.querySelectorAll(`button`)).find(e=>!e.matches(`[data-dsh-atb-entry]`)&&/新会话|新建会话|new session/i.test(e.textContent??``)):n}function gt(e){let t=document.createElement(`button`);return t.type=`button`,t.dataset.dshAtbEntry=``,t.className=`dsh-atb-entry`,t.setAttribute(`aria-label`,U(`shared.entry.aria`)),t.innerHTML=`<span class="dsh-atb-entry-icon"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M6 2v12M10 2v12"/></svg></span><span class="dsh-atb-entry-label">${U(`shared.entry.label`)}</span><span class="dsh-atb-entry-stats"></span>`,t.addEventListener(`click`,()=>{e.toggleBoard()}),t}function _t(e){let t=0,n=0,r=0;for(let i of e.getSnapshot().ledger.tasks)i.trashedAt===void 0&&(i.status===`todo`?t++:i.status===`in_progress`?n++:i.status===`in_review`&&r++);return[t,n,r]}function vt(e,t){let n=String(t);if(e.dataset.value===n)return;let r=e.dataset.value;if(e.dataset.value=n,e.style.minWidth=`${n.length}ch`,r===void 0){e.textContent=n;return}e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``);let i=document.createElement(`span`);i.className=`dsh-atb-rn`,i.textContent=r;let a=document.createElement(`span`);a.className=`dsh-atb-rn dsh-atb-rn-next`,a.textContent=n,e.replaceChildren(i,a),e.dataset.dir=t>Number(r)?`up`:`down`,e.dataset.busy=`1`,requestAnimationFrame(()=>{e.dataset.anim=`1`});let o=()=>{e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``,e.textContent=e.dataset.value??``)};e.addEventListener(`transitionend`,o,{once:!0}),setTimeout(o,400)}function yt(e,t){let n=e.querySelector(`.dsh-atb-entry-stats`);if(n===null)return()=>{};let r=[`todo`,`in_progress`,`in_review`],i=[];for(let e=0;e<3;e++){if(e>0){let e=document.createElement(`span`);e.className=`dsh-atb-entry-sep`,e.textContent=`|`,n.append(e)}let t=document.createElement(`span`);t.className=`dsh-atb-roll`,t.dataset.stat=r[e],n.append(t),i.push(t)}return()=>{let[e,r,a]=_t(t);vt(i[0],e),vt(i[1],r),vt(i[2],a),n.title=U(`shared.stats.title`,{todo:e,doing:r,review:a})}}function bt(e,t){let n=ht(e);if(n===void 0)return!1;if(t.parentElement!==e){let r=n.closest(`[class*="logoRow"]`),i=r!==null&&r.parentElement===e?r:n,a=Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.matches(`[data-dsh-atb-entry], [data-dsh-taskboard-entry], [data-dsh-ssh-entry]`)),o=a.length>0?a[0]??null:i.nextElementSibling??null;e.insertBefore(t,o)}return!0}function xt(e){let t=gt(e),n={attempts:0,found:!1,placed:!1},r=globalThis.location?.hostname;(r===`localhost`||r===`127.0.0.1`)&&(window.__atbDebug=n);let i,a=!1,o=()=>{if(n.attempts++,i!==void 0&&!i.isConnected&&(c.disconnect(),i=void 0,a=!1),a){if(document.body.contains(t))return;c.disconnect(),i=void 0,a=!1}i??=mt(),i!==void 0&&(n.found=ht(i)!==void 0,a=bt(i,t),n.placed=a,a&&c.observe(i,{childList:!0,subtree:!0}))},s=new MutationObserver(()=>{o()});s.observe(document.body,{childList:!0,subtree:!0});let c=new MutationObserver(()=>{if(i===void 0||!i.isConnected){a=!1,o();return}i.contains(t)||(a=bt(i,t))}),l=setInterval(()=>{o()},2e3),u=yt(t,e),d=()=>{e.getSnapshot().boardOpen?t.dataset.active=`true`:delete t.dataset.active,u()},f=e.subscribe(d),p=H.subscribe(()=>{t.setAttribute(`aria-label`,U(`shared.entry.aria`));let e=t.querySelector(`.dsh-atb-entry-label`);e!==null&&(e.textContent=U(`shared.entry.label`)),u()});return d(),o(),()=>{clearInterval(l),s.disconnect(),c.disconnect(),p(),f(),t.remove()}}const St=`data-dsh-atb-active`,Ct=[`data-dsh-taskboard-active`,`data-dsh-ssh-active`],wt=`dsh-panel-activate`,Tt=`dsh-taskboard`;function Et(){return document.querySelector(`[data-pane="conversation"], [class*="centerCol"], .dshDesktopConversationSurface`)??void 0}function Dt(e){let t,n,r,i=()=>{if(n!==void 0)return;let i=Et();i!==void 0&&(n=document.createElement(`div`),n.dataset.dshAtbView=``,n.className=`dsh-atb-view`,i.appendChild(n),r=pt(n),t=(0,d.createRoot)(n),t.render((0,l.jsx)(lt,{controller:e})))},a=new MutationObserver(()=>{i()});a.observe(document.body,{childList:!0,subtree:!0});let o=()=>{if(e.getSnapshot().boardOpen){for(let e of Ct)document.documentElement.removeAttribute(e);document.documentElement.setAttribute(St,``),document.dispatchEvent(new CustomEvent(wt,{detail:Tt}))}else document.documentElement.removeAttribute(St)},s=t=>{t.detail!==Tt&&e.getSnapshot().boardOpen&&e.closeBoard()},c=t=>{if(!e.getSnapshot().boardOpen)return;let n=t.target;n!==null&&n.closest(`[data-dsh-atb-entry]`)===null&&n.closest(`[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]`)!==null&&e.closeBoard()};document.addEventListener(`click`,c,!0),document.addEventListener(wt,s);let u=e.subscribe(o);return o(),i(),()=>{document.removeEventListener(`click`,c,!0),document.removeEventListener(wt,s),a.disconnect(),u(),r?.(),document.documentElement.removeAttribute(St),t?.unmount(),n?.remove()}}const Ot=`dsh-taskboard`;function kt(e){let t=e.get?.(`slots`)??e.slots;if(!(t===void 0||typeof t.register!=`function`||typeof t.inject!=`function`||typeof t.spec!=`function`)){try{if(t.spec(`main`)===void 0||t.spec(`sidebar.panellist`)===void 0)return}catch{return}return t}}function At(e){return function(){let t=c.useCallback(e=>{if(e===null)return;let t=`__dshAtbInsetDispose`,n=e;n[t]===void 0&&(n[t]=pt(e))},[]);return c.createElement(`div`,{"data-dsh-atb-panel-root":``,ref:t,className:`dsh-atb-panel-root`},c.createElement(lt,{controller:e}))}}function jt(e){return function(t){let n=typeof t.size==`number`?t.size:16,r=c.useCallback(t=>e.subscribe(t),[e]),i=c.useCallback(()=>e.getSnapshot(),[e]),a=c.useSyncExternalStore(r,i,i),o=c.useMemo(()=>{let e=0,t=0,n=0;for(let r of a.ledger.tasks)r.trashedAt===void 0&&(r.status===`todo`?e++:r.status===`in_progress`?t++:r.status===`in_review`&&n++);return{todo:e,doing:t,review:n}},[a.ledger]),[s,l]=c.useState(!0),u=c.useCallback(e=>{if(e===null||typeof ResizeObserver>`u`)return;let t=e.closest(`button`);t!==null&&new ResizeObserver(()=>{l(t.clientWidth>44)}).observe(t)},[]),d=U(`shared.stats.title`,{todo:o.todo,doing:o.doing,review:o.review});return c.createElement(`span`,{className:`dsh-atb-pglyph`,ref:u},c.createElement(`span`,{className:`dsh-atb-picon`},c.createElement(`svg`,{width:n,height:n,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":1.3,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":!0},c.createElement(`rect`,{x:2,y:2,width:12,height:12,rx:2}),c.createElement(`path`,{d:`M6 2v12M10 2v12`})),!s&&o.todo>0?c.createElement(`span`,{className:`dsh-atb-pbadge`,title:d,"aria-hidden":!0},o.todo):null),s&&(o.todo>0||o.doing>0||o.review>0)?c.createElement(`span`,{className:`dsh-atb-pstats`,title:d,"aria-hidden":!0},c.createElement(`span`,{"data-stat":`todo`},o.todo),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_progress`},o.doing),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_review`},o.review)):null)}}function Mt(e,t,n){let r=[];try{let i=t.get?.(`layout`)??t.layout;n.installCloseRequester(()=>{i?.selectPanel(null)}),r.push(()=>{n.installCloseRequester(void 0)});let a=At(n);r.push(e.inject(`main`,()=>e.register({name:`main`,key:Ot},a)));let o=jt(n);r.push(e.inject(`sidebar.panellist`,()=>e.register({name:`sidebar.panellist`,id:Ot,order:10,label:()=>U(`shared.entry.label`)},o))),console.info(`[dsh-taskboard] official panel mode: registered`,Ot)}catch(e){console.error(`[dsh-taskboard] official panel registration failed — falling back to legacy mounting:`,e);for(let e of r.splice(0))e();return}return()=>{for(let e of r.splice(0))e()}}function Nt(e,t){let n=kt(e);if(n!==void 0){let r=Mt(n,e,t);if(r!==void 0)return r}let r=[];r=[xt(t),Dt(t)];let i=0,a,o=setInterval(()=>{i++;let n=kt(e);if(n!==void 0){let i=Mt(n,e,t);if(i!==void 0){a=i,clearInterval(o);for(let e of r.splice(0))e();return}}i>=3&&clearInterval(o)},350);return()=>{clearInterval(o);for(let e of r.splice(0))e();a?.()}}function Pt(e){let t=(e,t,n)=>e.list.getSnapshot().byId[n]===void 0?`absent`:t?.list.getSnapshot().archivedSessionIds.includes(n)??!1?`archived`:`openable`,n=(t,n)=>{let r=e.getUiWorkspace?.();return typeof r?.openSession==`function`?(r.openSession(n),!0):typeof t.open==`function`?(t.open(n),!0):!1};return async r=>{let i=e.getSessions();if(i===void 0)return`unavailable`;try{let a=t(i,e.getWorkspaces(),r);if(a===`absent`){try{await i.refresh()}catch{}a=t(i,e.getWorkspaces(),r)}return a===`archived`?`archived`:a===`absent`?`missing`:n(i,r)?`opened`:`unavailable`}catch{return`unavailable`}}}const Ft=[`connection`];function It(e){try{me(),V(e.get?.(`locale`),()=>e.get?.(`locale`));let t=_(),n=new fe(t);n.installModelCatalog(async()=>{try{let t=e.get?.(`modelDirectories`)??e.modelDirectories;if(t?.catalog?.load!==void 0){let e=await t.catalog.load();if(e?.groups!==void 0&&e.groups.length>0){let t=[];for(let n of e.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`remote`)??e.remote;if(t?.session?.modelCatalog!==void 0){let e=await t.session.modelCatalog();if(e.ok&&e.value?.groups!==void 0&&e.value.groups.length>0){let t=[];for(let n of e.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}if(t?.llm?.models!==void 0){let e=await t.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.llm?.models!==void 0){let e=await t.api.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let e=await t.modelCatalog();if(e.models!==void 0&&e.models.length>0)return e.models}catch{}return[]}),n.installPresetRoster(async()=>{try{let t=e.get?.(`remote`)??e.remote;if(t?.agentPresets?.list!==void 0){let e=await t.agentPresets.list(),n=e.ok===!0?e.value.presets:e.result?.ok===!0?e.result.value.presets:void 0;if(n!==void 0&&n.length>0){let e=n.map(e=>({id:e.id,name:e.name})),t=n.find(e=>e.isDefault);return{presets:e,...t===void 0?{}:{defaultId:t.id}}}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.agentPresets?.list!==void 0){let e=await t.api.agentPresets.list({});if(e.result.ok&&e.result.value?.presets!==void 0&&e.result.value.presets.length>0){let t=e.result.value.presets.map(e=>({id:e.id,name:e.name})),n=e.result.value.presets.find(e=>e.isDefault);return{presets:t,...n===void 0?{}:{defaultId:n.id}}}}}catch{}try{let e=await t.modelCatalog();if(e.presets!==void 0&&e.presets.length>0)return{presets:e.presets,...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{}return{presets:[]}}),n.installSessionJumper(Pt({getSessions:()=>e.get?.(`sessions`),getWorkspaces:()=>e.get?.(`workspaces`),getUiWorkspace:()=>e.get?.(`uiWorkspace`)})),n.installSessionTitles(()=>(e.get?.(`sessions`))?.list.getSnapshot().byId??{}),n.start();let r=[];try{r.push(Nt(e,n))}catch(e){console.error(`[dsh-taskboard] mount failed:`,e)}e.effect?.(()=>()=>{for(let e of r.splice(0))e();n.dispose(),ce()},`dsh-taskboard: client mount`)}catch(e){console.error(`[dsh-taskboard] client half failed to start:`,e)}}exports.apply=It,exports.inject=Ft,exports.name=`dsh-taskboard/client`; return module.exports; } }); diff --git a/lib/host/routes.js b/lib/host/routes.js index 3a6abe7..a3a3683 100644 --- a/lib/host/routes.js +++ b/lib/host/routes.js @@ -587,10 +587,12 @@ function registerTaskboardRoutes(ctx, options) { const execution = normalizeExecution(body.execution ?? {}, options.now()); if (execution.mode === "scheduled" && execution.cron !== void 0 && execution.reuseSessionId === void 0 && execution.sessionReuseMode === void 0) execution.sessionReuseMode = "fresh"; if (execution.reuseSessionId !== void 0 && !workspaces.sessionIds?.(workspaceId)?.includes(execution.reuseSessionId)) throw new Error("Error: invalid_input: selected session is not available in this workspace"); - const model = body.model === void 0 ? void 0 : checkModel(body.model, options.modelProviders); + const settings = store.snapshot().settings; + const modelRaw = body.model === void 0 ? settings?.defaultModel : body.model; + const model = modelRaw == null ? void 0 : checkModel(modelRaw, options.modelProviders); const isolationRaw = str(body, "isolation"); const isolation = isolationRaw === null ? defaultIsolationOf(store.snapshot().settings) : asIsolation(isolationRaw); - const presetId = normalizePresetId(str(body, "presetId")); + const presetId = body.presetId === void 0 ? settings?.defaultPresetId : normalizePresetId(str(body, "presetId")); const permissionRaw = str(body, "permission"); const permission = permissionRaw === null ? defaultPermissionOf(store.snapshot().settings) : asPermission(permissionRaw); let checklist = void 0; diff --git a/lib/host/routes.js.map b/lib/host/routes.js.map index 5ab3431..51d10e9 100644 --- a/lib/host/routes.js.map +++ b/lib/host/routes.js.map @@ -1 +1 @@ -{"version":3,"file":"routes.js","names":[],"sources":["../../src/host/routes.ts"],"sourcesContent":["import { archiveTaskSessions } from './archive-sessions.ts'\n/**\n * /dsh-taskboard routes on the shared DSH webserver: a JSON API for the\n * GUI's human operations (create/update/move/comment/delete — actor `user`,\n * the done move IS allowed here) plus an SSE stream mirroring every\n * committed ledger mutation.\n *\n * All domain validation goes through the shared protocol pure functions; the\n * route layer only maps transport to envelope.\n *\n * @module dsh-taskboard/host/routes\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { readdir, rm } from 'node:fs/promises'\nimport { join, resolve, sep } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only: pulls the webServer Context merge (ctx.webServer).\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport {\n asBoardSettings,\n asIsolation,\n asPermission,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n defaultPermissionOf,\n isValidRelRepoPath,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeChecklist,\n normalizeExecution,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n validateLedgerImport,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n type SystemCommentRow,\n} from '../shared/protocol.ts'\nimport { WORKTREE_DIR, worktreePathOf, type GitFace } from './git.ts'\nimport { removeMirror, repoMainPath } from './isolation.ts'\nimport { createRepoScanner, type RepoScanner } from './repos.ts'\nimport { activeHostLocale } from './locale.ts'\nimport type { CatalogModelItem, CatalogPresetItem, MergeRepoResult, TaskTemplate } from '../shared/api.ts'\nimport type { TemplateStore } from './templates.ts'\nimport { MAX_ASSET_BYTES, type AssetStore } from './assets.ts'\nimport type { StorageCoordinator } from './storage.ts'\nimport { ROUTE_PREFIX, SSE_PATH, type ApiFail, type ApiResult } from '../shared/api.ts'\nimport type { TaskStore } from './store.ts'\nimport { ERR, ToolError } from './tools.ts'\nimport type { WorkspaceFace } from './tools.ts'\n\n/** Heartbeat cadence for the SSE stream. */\nconst HEARTBEAT_MS = 20_000\n\n/** Max accepted JSON body bytes (S8: unbounded buffering is a local OOM vector). */\nconst MAX_BODY_BYTES = 5 * 1024 * 1024\n\n/** Route shapes (T2: compiled once at module load, not on every request). */\nconst TASK_DIFF_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)/diff$`)\nconst TASK_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)$`)\nconst TASK_ACTION_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)/([\\\\w-]+)$`)\nconst ASSET_RE = new RegExp(`^${ROUTE_PREFIX}/assets/([a-f0-9]{64}\\\\.(?:png|jpg|gif|webp))$`)\n\n/** How long a workspace git-detection result stays cached (fail-soft). */\nconst GIT_DETECT_TTL_MS = 60_000\n\n/** The workspaces face routes need (same narrow shape as tools). */\nexport type RoutesWorkspaceFace = WorkspaceFace\n\n/** Options. */\nexport interface TaskboardRoutesOptions {\n store: TaskStore\n workspaces: RoutesWorkspaceFace\n now: () => number\n /** Current scheduler concurrency limit, for queue observability only. */\n maxConcurrent?: () => number\n /** Drop every durable queue entry (board queue panel); absent → 501. */\n clearQueue?: () => Promise<number>\n /** Manual-run hook (the execution service); absent → 501. Options carry `reuseWorktree` (续跑). */\n run?: (taskId: string, options?: { reuseWorktree?: boolean }) => Promise<{ ok: true; executionId: string; sessionId: string } | { ok: false; error: string }>\n /** Cancel hook (the execution service); absent → 501. */\n cancel?: (taskId: string) => Promise<{ ok: true; executionId: string } | { ok: false; error: string }>\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable.\n */\n modelProviders?: () => string[] | undefined\n /** Git face for worktree actions + workspace git detection; absent → 501 on git actions. */\n git?: GitFace\n /** Nested-repo scanner for mirror removal; absent → a default one is built on first use. */\n scanner?: RepoScanner\n /** Task-template store (0.4.0); absent → 501 on template actions. */\n templates?: TemplateStore\n /** Durable image attachment store (0.7.0); absent → attachment routes unavailable. */\n assets?: AssetStore\n /** Configurable data-directory coordinator (0.7.0). */\n storage?: StorageCoordinator\n /** Initial storage/ledger readiness barrier. */\n ready?: () => Promise<void>\n /** Prompt completions face (0.5.5; dynamically discovers skills & commands). */\n promptCompletions?: () => Promise<{\n skills?: Array<{ name: string; description?: string }>\n commands?: Array<{ name: string; description?: string; hint?: string }>\n }>\n /** Model and preset catalog face (0.5.5; dynamically discovers models & presets). */\n modelCatalog?: () => Promise<{\n models?: CatalogModelItem[]\n presets?: CatalogPresetItem[]\n defaultPresetId?: string\n }>\n}\n\n/** Validate a template's task spec (routes-side, unknown → invalid_input). */\nfunction normalizeTemplateSpec(raw: unknown, now: number): TaskTemplate['task'] {\n if (typeof raw !== 'object' || raw === null) throw new Error('Error: invalid_input: task must be an object')\n const e = raw as Record<string, unknown>\n const spec: TaskTemplate['task'] = {}\n const str = (key: string): string | undefined => {\n const v = e[key]\n if (v === undefined) return undefined\n if (typeof v !== 'string') throw new Error(`Error: invalid_input: task.${key} must be a string`)\n return v\n }\n const title = str('title')\n const description = str('description')\n const prompt = str('prompt')\n const urgency = str('urgency')\n const isolation = str('isolation')\n const presetId = str('presetId')\n const permission = str('permission')\n if (title !== undefined) spec.title = normalizeTitle(title)\n if (description !== undefined) spec.description = description\n if (prompt !== undefined) spec.prompt = normalizePrompt(prompt)\n if (urgency !== undefined) spec.urgency = asUrgency(urgency)\n if (isolation !== undefined) spec.isolation = asIsolation(isolation)\n if (presetId !== undefined && presetId.trim().length > 0) spec.presetId = presetId.trim()\n if (permission !== undefined && permission.trim().length > 0) spec.permission = asPermission(permission)\n if (e.execution !== undefined) {\n spec.execution = normalizeExecution(e.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown }, now)\n }\n if (e.model !== undefined) spec.model = normalizeModel(e.model)\n if (e.checklist !== undefined) {\n if (!Array.isArray(e.checklist) || e.checklist.some(c => typeof c !== 'string')) {\n throw new Error('Error: invalid_input: task.checklist must be an array of strings')\n }\n checklistFromTexts(e.checklist as string[]) // validates count + texts\n spec.checklist = e.checklist as string[]\n }\n return spec\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(raw: unknown, modelProviders?: () => string[] | undefined): TaskModel {\n const model = normalizeModel(raw)\n const providers = modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new Error(`Error: invalid_input: model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** JSON-envelope writer. */\nfunction json(res: ServerResponse, payload: ApiResult<unknown>, status = 200): void {\n const body = JSON.stringify(payload)\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })\n res.end(body)\n}\n\n/** Domain failure → envelope + HTTP status. */\nfunction fail(code: ApiFail['error']['code'], message: string): { res: ApiFail; status: number } {\n const status = code === 'invalid_input' || code === 'invalid_transition' ? 400\n : code === 'not_found' ? 404\n : code === 'version_conflict' ? 409\n : code === 'forbidden' ? 403\n : 500\n return { res: { ok: false, error: { code, message } }, status }\n}\n\n/**\n * Read one JSON body (null on parse failure). S8: rejects bodies over\n * MAX_BODY_BYTES by throwing — the local, unauthenticated HTTP surface must\n * not be an unbounded memory sink.\n */\nasync function readBody(req: IncomingMessage): Promise<Record<string, unknown> | null> {\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of req) {\n total += (chunk as Buffer).length\n if (total > MAX_BODY_BYTES) throw new Error('body too large')\n chunks.push(chunk as Buffer)\n }\n if (chunks.length === 0) return {}\n try {\n const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n return typeof parsed === 'object' && parsed !== null ? parsed as Record<string, unknown> : null\n } catch {\n return null\n }\n}\n\n/** Read one bounded binary upload without ever buffering beyond the file cap. */\nasync function readBytes(req: IncomingMessage, limit: number): Promise<Buffer> {\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of req) {\n const bytes = chunk as Buffer\n total += bytes.length\n if (total > limit) throw new Error('body too large')\n chunks.push(bytes)\n }\n return Buffer.concat(chunks)\n}\n\n/** String field accessor (null when absent/not a string). */\nfunction str(body: Record<string, unknown>, key: string): string | null {\n const v = body[key]\n return typeof v === 'string' ? v : null\n}\n\n/** Number field accessor (undefined when absent; null when present but not a number). */\nfunction num(body: Record<string, unknown>, key: string): number | undefined | null {\n const v = body[key]\n if (v === undefined) return undefined\n return typeof v === 'number' && Number.isFinite(v) ? v : null\n}\n\n/** Find a live task INSIDE a mutator (R1: guards run on the fresh draft). */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0 || ledger.tasks[index]!.trashedAt !== undefined) throw new Error('Error: not_found: no such task')\n return { index, task: ledger.tasks[index]! }\n}\n\n/** Normalize an agent preset id: trimmed, non-empty; empty string → undefined. */\nfunction normalizePresetId(raw: string | null): string | undefined {\n const t = (raw ?? '').trim()\n return t.length === 0 ? undefined : t\n}\n\n/** Map a thrown domain error to the envelope. */\nfunction toFail(error: unknown): { res: ApiFail; status: number } {\n const message = error instanceof Error ? error.message : String(error)\n // Structured path first (review P2): ToolError carries its code — no need\n // to parse the 'Error: <code>: …' prefix it also renders into the message.\n if (error instanceof ToolError) {\n const mapped = error.code === ERR.workspaceMismatch ? 'forbidden' : error.code\n const known: ApiFail['error']['code'][] = ['invalid_input', 'not_found', 'version_conflict', 'invalid_transition', 'forbidden', 'internal']\n if ((known as string[]).includes(mapped)) {\n return fail(mapped as ApiFail['error']['code'], message.slice(7 + error.code.length + 2))\n }\n }\n const code = message.startsWith('Error: ') ? message.slice(7).split(':')[0] : undefined\n const known2: ApiFail['error']['code'][] = ['invalid_input', 'not_found', 'version_conflict', 'invalid_transition', 'forbidden', 'internal']\n if (code !== undefined && (known2 as string[]).includes(code)) {\n return fail(code as ApiFail['error']['code'], message.slice(7 + code.length + 2))\n }\n if (code === 'workspace_mismatch') return fail('forbidden', message.slice(7 + code.length + 2))\n return fail('invalid_input', message)\n}\n\n/**\n * Register the taskboard routes.\n * @param ctx - context carrying the webServer service.\n * @param options - store + workspaces + clock.\n * @returns the disposer.\n */\nexport function registerTaskboardRoutes(ctx: Context, options: TaskboardRoutesOptions): () => void {\n const { store, workspaces } = options\n const subscribers = new Set<ServerResponse>()\n let heartbeat: NodeJS.Timeout | undefined\n\n const queueSummary = (ledger: TaskLedger) => {\n let depth = 0\n let dispatching = 0\n let oldestQueuedAt: number | undefined\n for (const task of ledger.tasks) {\n if (task.execution.queuedRunAt !== undefined) {\n depth += 1\n if (task.execution.queuedAt !== undefined && (oldestQueuedAt === undefined || task.execution.queuedAt < oldestQueuedAt)) oldestQueuedAt = task.execution.queuedAt\n }\n if (task.execution.dispatchingRunAt !== undefined) dispatching += 1\n }\n const now = options.now()\n return {\n depth,\n dispatching,\n ...(oldestQueuedAt === undefined ? {} : { oldestQueuedAt, oldestWaitMinutes: Math.max(0, Math.floor((now - oldestQueuedAt) / 60_000)) }),\n maxConcurrent: options.maxConcurrent?.() ?? 0,\n }\n }\n\n /** R4③: a cleanup/purge target must resolve INSIDE <ws>/.dsh-worktrees — string joining alone is never trusted with an rm. */\n const insideWorktreeScope = (wsPath: string, target: string): boolean => {\n const scope = resolve(wsPath, WORKTREE_DIR)\n const resolved = resolve(target)\n return resolved === scope || resolved.startsWith(scope + sep)\n }\n\n const broadcast = (change: { revision: number; kind: string; tasks: readonly TaskRecord[] }): void => {\n const frame = `event: change\\ndata: ${JSON.stringify({ revision: change.revision, kind: change.kind, tasks: change.tasks.map(summarize) })}\\n\\n`\n for (const res of subscribers) res.write(frame)\n }\n const unsubscribeBroadcast = store.subscribe(broadcast)\n\n // Workspace git detection, TTL-cached and fail-soft (false on any error):\n // feeds the create-form isolation toggle and the diagnostics panel.\n const gitCache = new Map<string, { value: boolean; at: number; repoCount?: number }>()\n const gitHinted = new Set<string>()\n\n /** Whether <root>/.gitignore (missing file counts as missing) ignores our worktree dir. */\n const gitignoreMissing = async (path: string): Promise<boolean> => {\n try {\n const { readFile } = await import('node:fs/promises')\n const ignore = await readFile(join(path, '.gitignore'), 'utf8')\n return !ignore.split('\\n').some(l => {\n const t = l.trim().replace(/\\/+$/, '')\n return t === WORKTREE_DIR || t === `/${WORKTREE_DIR}`\n })\n } catch {\n return true // no .gitignore at all (or unreadable) → suggest creating one\n }\n }\n\n // One scanner for every route-side discovery (the injected one or a\n // shared real-IO fallback whose TTL cache actually helps — 0.6.3).\n const sharedScanner: RepoScanner = options.scanner ?? createRepoScanner()\n\n /**\n * Whether the workspace root itself is a git repo (the .gitignore-suggestion\n * gate — a plain container has no repo that could ignore anything).\n */\n const rootIsRepo = async (path: string): Promise<boolean> => {\n try {\n return (await options.git?.detect(path)) === true\n } catch {\n return false // fail-soft\n }\n }\n\n /**\n * Workspace repo facts for the form (0.6.3): `gitAvailable` gates the\n * worktree option, `repoCount` feeds the mirror badge. Availability now\n * covers PARALLEL MULTI-REPO workspaces too: a root repo qualifies as\n * before, and a workspace whose root is NOT a repo still qualifies when\n * the scanner finds nested repos — prepareMirror isolates exactly that\n * container shape (mirror root = plain dir, one worktree per nested repo),\n * so the form must not lock the capability away.\n */\n const workspaceRepos = async (path: string): Promise<{ gitAvailable: boolean; repoCount: number }> => {\n if (options.git === undefined) return { gitAvailable: false, repoCount: 0 }\n const hit = gitCache.get(path)\n if (hit !== undefined && options.now() - hit.at < GIT_DETECT_TTL_MS) {\n return { gitAvailable: hit.value, repoCount: hit.repoCount ?? (hit.value ? 1 : 0) }\n }\n let rootRepo = false\n try {\n rootRepo = await options.git.detect(path)\n } catch { /* fail-soft → false */ }\n // gitignore 建议 (plan §3.2): suggest (never write) ignoring our\n // worktree directory, once per workspace per host run. Root repos only.\n // The line is localized from the DSH locale preference (see host/locale.ts).\n if (rootRepo && !gitHinted.has(path)) {\n gitHinted.add(path)\n if (await gitignoreMissing(path)) {\n const file = `${path}/.gitignore`\n const hint = activeHostLocale(ctx) === 'zh'\n ? `建议在 ${file} 加入一行 ${WORKTREE_DIR}/ 以隐藏任务 worktree 目录(不会自动修改)`\n : `suggests adding one line to ${file}: ${WORKTREE_DIR}/ to hide the task worktree directory (no automatic edits)`\n console.info(`[dsh-taskboard] ${hint}`)\n }\n }\n // The nested scan always runs: repoCount needs it even when the root\n // is a repo (root repo + nested repos = the mirror shape). The scanner's\n // own TTL cache keeps repeated workspace polls cheap.\n let nestedCount = 0\n try {\n nestedCount = (await sharedScanner.findNestedRepos(path)).length\n } catch { /* fail-soft → 0 */ }\n const value = rootRepo || nestedCount > 0\n const repoCount = (rootRepo ? 1 : 0) + nestedCount\n gitCache.set(path, { value, at: options.now(), repoCount })\n return { gitAvailable: value, repoCount }\n }\n\n /** List orphan worktree dirs: entries under <ws>/.dsh-worktrees owned by no ledger task. */\n const listOrphanWorktrees = async (): Promise<Array<{ workspaceId: string; workspacePath: string; taskId: string; path: string }>> => {\n const orphans: Array<{ workspaceId: string; workspacePath: string; taskId: string; path: string }> = []\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n for (const ws of workspaces.list()) {\n let entries: string[] = []\n try {\n const dirents = await readdir(join(ws.path, WORKTREE_DIR), { withFileTypes: true })\n entries = dirents.filter(e => e.isDirectory()).map(e => e.name)\n } catch { /* no worktrees dir → nothing to do */ }\n for (const taskId of entries) {\n if (!known.has(taskId)) orphans.push({ workspaceId: ws.id, workspacePath: ws.path, taskId, path: worktreePathOf(ws.path, taskId) })\n }\n }\n return orphans\n }\n\n /** Git-enabled workspaces whose .gitignore does not cover the worktree dir. */\n const listGitignoreSuggestions = async (): Promise<Array<{ workspaceId: string; workspacePath: string }>> => {\n const suggestions: Array<{ workspaceId: string; workspacePath: string }> = []\n for (const ws of workspaces.list()) {\n // Root repos only: a plain container has no .gitignore any repo reads.\n if (!(await rootIsRepo(ws.path))) continue\n if (await gitignoreMissing(ws.path)) suggestions.push({ workspaceId: ws.id, workspacePath: ws.path })\n }\n return suggestions\n }\n\n const handler = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n const url = new URL(req.url ?? '/', 'http://x')\n const pathname = url.pathname\n\n // ---------------------------------------------------------------- GET\n if (req.method === 'GET') {\n if (pathname === `${ROUTE_PREFIX}/storage`) {\n if (options.storage === undefined) { res.writeHead(501); res.end(); return }\n json(res, { ok: true, value: await options.storage.status() })\n return\n }\n await options.ready?.()\n const assetMatch = pathname.match(ASSET_RE)\n if (assetMatch !== null) {\n const asset = await options.assets?.read(assetMatch[1]!)\n if (asset === undefined) { res.writeHead(404); res.end(); return }\n res.writeHead(200, {\n 'content-type': asset.mime,\n 'content-length': asset.bytes.length,\n 'cache-control': 'public, max-age=31536000, immutable',\n 'x-content-type-options': 'nosniff',\n })\n res.end(asset.bytes)\n return\n }\n if (pathname === `${ROUTE_PREFIX}/state`) {\n await store.load()\n const ledger = store.snapshot()\n json(res, { ok: true, value: { ...ledger, capabilities: { archiveSessions: typeof workspaces.archiveSession === 'function' }, queue: queueSummary(ledger) } })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/workspaces`) {\n const list = workspaces.list()\n const info = await Promise.all(list.map(ws => workspaceRepos(ws.path)))\n json(res, {\n ok: true,\n value: list.map((ws, i) => ({ ...ws, sessionCount: 0, gitAvailable: info[i]!.gitAvailable, repoCount: info[i]!.repoCount })),\n })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/project-sessions`) {\n const workspaceId = url.searchParams.get('workspaceId') ?? ''\n if (workspaces.get(workspaceId) === undefined) {\n const f = fail('not_found', 'unknown workspace')\n json(res, f.res, f.status)\n return\n }\n json(res, { ok: true, value: { sessions: (workspaces.sessionIds?.(workspaceId) ?? []).map(id => ({ id })) } })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/diagnostics`) {\n const ledger = store.snapshot()\n const queue = queueSummary(ledger)\n let staleRunning = 0\n for (const t of ledger.tasks) {\n for (const e of t.executions) if (e.outcome === 'running') staleRunning += 1\n }\n json(res, {\n ok: true,\n value: {\n revision: ledger.revision,\n tasks: ledger.tasks.length,\n staleRunning,\n queue,\n orphanWorktrees: await listOrphanWorktrees(),\n gitIgnoreSuggestions: await listGitignoreSuggestions(),\n },\n })\n return\n }\n // Diff viewer (0.4.0): read-only git show/diff for one execution's\n // commit or changed path. Prefers the live worktree (uncommitted\n // view), falls back to the main repo.\n const diffMatch = pathname.match(TASK_DIFF_RE)\n if (diffMatch !== null) {\n try {\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n const task = store.get(diffMatch[1]!)\n if (task === undefined) throw new Error('Error: not_found: no such task')\n const execution = task.executions.find(e => e.id === url.searchParams.get('execution'))\n if (execution === undefined) throw new Error('Error: not_found: no such execution')\n const commit = url.searchParams.get('commit')\n const filePath = url.searchParams.get('path')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n // 0.6.3: `?repo=` picks one repo of a multi-repo mirror ('' = the\n // root); without it the legacy flat fields decide, byte-identical.\n const repoParam = url.searchParams.get('repo')\n let cwd = execution.worktreePath ?? ws.path\n let mainRepo = ws.path\n let baseCommit = execution.baseCommit\n if (repoParam !== null) {\n const entry = execution.repos?.find(r => r.repo === repoParam)\n if (entry === undefined) throw new Error('Error: invalid_input: 该执行没有此仓库的镜像记录')\n cwd = entry.worktreePath\n mainRepo = repoMainPath(ws.path, entry.repo)\n baseCommit = entry.baseCommit\n }\n let result = commit !== null\n ? await options.git.showCommit(cwd, commit)\n : filePath !== null ? await options.git.showPathDiff(cwd, filePath, baseCommit) : undefined\n // Fallback: the worktree may be gone — commits and committed\n // ranges still resolve in the main repo.\n if (result === undefined && cwd !== mainRepo) {\n result = commit !== null\n ? await options.git.showCommit(mainRepo, commit)\n : filePath !== null && baseCommit !== undefined\n ? await options.git.showPathDiff(mainRepo, filePath, baseCommit)\n : undefined\n }\n if (result === undefined) {\n throw new Error('Error: invalid_input: 无法获取 diff(git 报错、对象不存在,或仅存于已删除的 worktree 且无基线)')\n }\n json(res, { ok: true, value: { diff: result.text, truncated: result.truncated } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // Templates listing (0.4.0).\n if (pathname === `${ROUTE_PREFIX}/templates`) {\n if (options.templates === undefined) {\n const f = fail('invalid_input', 'template store unavailable')\n json(res, f.res, 501)\n return\n }\n json(res, { ok: true, value: { templates: await options.templates.list() } })\n return\n }\n\n // Board settings (0.5.0): absent fields follow factory defaults.\n if (pathname === `${ROUTE_PREFIX}/settings`) {\n await store.load()\n json(res, { ok: true, value: store.snapshot().settings ?? {} })\n return\n }\n\n // Prompt completions (0.5.5; dynamically discovers skills & commands).\n if (pathname === `${ROUTE_PREFIX}/prompt-completions`) {\n const completions = await options.promptCompletions?.().catch(() => undefined)\n json(res, {\n ok: true,\n value: {\n commands: completions?.commands ?? [],\n skills: completions?.skills ?? [],\n },\n })\n return\n }\n\n // Model catalog (0.5.5; dynamically discovers models & presets from runtime).\n if (pathname === `${ROUTE_PREFIX}/model-catalog`) {\n const catalog = await options.modelCatalog?.().catch(() => undefined)\n json(res, {\n ok: true,\n value: {\n models: catalog?.models ?? [],\n presets: catalog?.presets ?? [],\n ...(catalog?.defaultPresetId !== undefined ? { defaultPresetId: catalog.defaultPresetId } : {}),\n },\n })\n return\n }\n\n const taskMatch = pathname.match(TASK_RE)\n if (taskMatch !== null) {\n const task = store.get(taskMatch[1]!)\n if (task === undefined) { const f = fail('not_found', 'no such task'); json(res, f.res, f.status); return }\n json(res, { ok: true, value: task })\n return\n }\n res.writeHead(404)\n res.end()\n return\n }\n\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST' })\n res.end()\n return\n }\n // Content-addressed image upload. The custom header makes this a\n // non-simple cross-origin request, preserving the JSON routes' CSRF fence.\n if (pathname === `${ROUTE_PREFIX}/assets`) {\n await options.ready?.()\n if (options.assets === undefined) {\n const f = fail('invalid_input', 'image attachments unavailable')\n json(res, f.res, 501)\n return\n }\n if (req.headers['x-dsh-taskboard-upload'] !== '1') {\n const f = fail('forbidden', 'missing upload header')\n json(res, f.res, 403)\n return\n }\n const declaredMime = String(req.headers['content-type'] ?? '').split(';', 1)[0]!.trim().toLowerCase()\n try {\n const bytes = await readBytes(req, MAX_ASSET_BYTES)\n await options.assets.cleanup(JSON.stringify(store.snapshot()))\n const asset = await options.assets.put(bytes, declaredMime)\n json(res, { ok: true, value: asset }, 201)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const status = message.includes('1..') ? 413 : message.includes('quota') ? 507 : 400\n const f = fail('invalid_input', message)\n json(res, f.res, status)\n }\n return\n }\n // CSRF fence: cross-site simple requests cannot set application/json.\n const contentType = req.headers['content-type'] ?? ''\n if (!contentType.toLowerCase().startsWith('application/json')) {\n const f = fail('invalid_input', 'content-type must be application/json')\n json(res, f.res, 415)\n return\n }\n let body: Record<string, unknown> | null\n try {\n body = await readBody(req)\n } catch {\n const f = fail('invalid_input', `request body exceeds ${MAX_BODY_BYTES} bytes`)\n json(res, f.res, 413)\n return\n }\n if (body === null) {\n const f = fail('invalid_input', 'body is not a JSON object')\n json(res, f.res, 400)\n return\n }\n\n // Storage location is bootstrap metadata outside the ledger. Keep it on\n // separate routes so JSON imports and whole-ledger replacement cannot\n // redirect host filesystem writes.\n if (pathname === `${ROUTE_PREFIX}/storage/check` || pathname === `${ROUTE_PREFIX}/storage/migrate`) {\n if (options.storage === undefined) {\n const f = fail('invalid_input', 'storage configuration unavailable')\n json(res, f.res, 501)\n return\n }\n try {\n const directory = str(body, 'directory') ?? ''\n const value = pathname.endsWith('/check')\n ? await options.storage.check(directory)\n : await options.storage.migrate(directory)\n json(res, { ok: true, value })\n } catch (error) {\n const f = fail('invalid_input', error instanceof Error ? error.message : String(error))\n json(res, f.res, f.status)\n }\n return\n }\n await options.ready?.()\n\n // ------------------------------------------------- POST /tasks (create)\n if (pathname === `${ROUTE_PREFIX}/tasks`) {\n try {\n const title = normalizeTitle(str(body, 'title') ?? '')\n const workspaceId = str(body, 'workspaceId') ?? ''\n if (workspaces.get(workspaceId) === undefined) throw new Error('Error: not_found: unknown workspace')\n const urgency = asUrgency(str(body, 'urgency') ?? '')\n const status = str(body, 'status') === null ? 'todo' as const : asStatus(str(body, 'status')!)\n if (status !== 'backlog' && status !== 'todo') {\n throw new Error('Error: invalid_transition: a new task must start as backlog or todo')\n }\n const execution = normalizeExecution((body.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown } | undefined) ?? {}, options.now())\n // New API-created scheduled tasks follow the same explicit default\n // as the form: create a fresh conversation on each trigger. Only\n // pre-existing ledgers with an omitted policy retain #26 behavior.\n if (execution.mode === 'scheduled' && execution.cron !== undefined\n && execution.reuseSessionId === undefined && execution.sessionReuseMode === undefined) {\n execution.sessionReuseMode = 'fresh'\n }\n if (execution.reuseSessionId !== undefined && !workspaces.sessionIds?.(workspaceId)?.includes(execution.reuseSessionId)) {\n throw new Error('Error: invalid_input: selected session is not available in this workspace')\n }\n const model = body.model === undefined ? undefined : checkModel(body.model, options.modelProviders)\n const isolationRaw = str(body, 'isolation')\n // 0.5.0: an omitted isolation is MATERIALIZED from the board\n // setting (看板设置) at creation, so later setting changes never\n // rewrite existing tasks.\n const isolation = isolationRaw === null ? defaultIsolationOf(store.snapshot().settings) : asIsolation(isolationRaw)\n const presetId = normalizePresetId(str(body, 'presetId'))\n const permissionRaw = str(body, 'permission')\n const permission = permissionRaw === null ? defaultPermissionOf(store.snapshot().settings) : asPermission(permissionRaw)\n let checklist: TaskRecord['checklist'] = undefined\n if (body.checklist !== undefined) {\n if (!Array.isArray(body.checklist) || body.checklist.some(c => typeof c !== 'string')) {\n throw new Error('Error: invalid_input: checklist must be an array of strings')\n }\n const texts = (body.checklist as string[]).map(c => c.trim()).filter(c => c.length > 0)\n if (texts.length > 0) checklist = checklistFromTexts(texts)\n }\n const now = options.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (str(body, 'description') ?? '').trim(),\n prompt: normalizePrompt(str(body, 'prompt') ?? undefined),\n workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n ...(presetId !== undefined ? { presetId } : {}),\n permission,\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: { kind: 'user' },\n updatedBy: { kind: 'user' },\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n json(res, { ok: true, value: summarize(task) }, 201)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------- POST /tasks/:id/{action}\n // (\\w+ after the id would not match hyphenated actions like\n // worktree-remove, hence the explicit class in the hoisted pattern.)\n const actionMatch = pathname.match(TASK_ACTION_RE)\n if (actionMatch !== null) {\n const id = actionMatch[1]!\n const action = actionMatch[2]!\n try {\n const task = store.get(id)\n if (task === undefined) throw new Error('Error: not_found: no such task')\n if (action === 'archive-sessions') {\n if (task.trashedAt !== undefined || task.status !== 'archived') throw new Error('Error: invalid_transition: only archived live tasks can retry session archiving')\n const result = await archiveTaskSessions(task, workspaces.archiveSession)\n json(res, { ok: true, value: result })\n return\n }\n if (action === 'update') {\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n // R1: version guard + write inside the mutation, on the fresh draft.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (task.status === 'archived') throw new Error('Error: invalid_transition: archived tasks are immutable')\n next = structuredClone(task)\n const title = str(body, 'title')\n if (title !== null) next.title = normalizeTitle(title)\n const description = str(body, 'description')\n if (description !== null) next.description = description.trim()\n const prompt = str(body, 'prompt')\n if (prompt !== null) next.prompt = normalizePrompt(prompt)\n const urgency = str(body, 'urgency')\n if (urgency !== null) next.urgency = asUrgency(urgency)\n // GUI-only rebind to another project; validated against the workspace registry.\n const workspaceId = str(body, 'workspaceId')\n if (workspaceId !== null) {\n if (workspaces.get(workspaceId) === undefined) throw new Error('Error: not_found: unknown workspace')\n next.workspaceId = workspaceId\n }\n if (typeof body.blocked === 'boolean') next.blocked = body.blocked\n // The GUI (task owner surface) may edit model/execution; null clears the model.\n if (body.execution !== undefined) {\n const previousExecution = next.execution\n next.execution = normalizeExecution(body.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown }, options.now())\n // The client never owns the learned continuation pointer. Keep\n // it when the task remains on the same auto-reuse policy.\n if (next.execution.reuseSessionId === undefined && next.execution.sessionReuseMode === 'reuse'\n && previousExecution.reuseSessionId === undefined && previousExecution.sessionReuseMode !== 'fresh') {\n next.execution.autoReuseSessionId = previousExecution.autoReuseSessionId\n next.execution.autoReuseSessionKey = previousExecution.autoReuseSessionKey\n }\n }\n if (next.execution.reuseSessionId !== undefined && !workspaces.sessionIds?.(next.workspaceId)?.includes(next.execution.reuseSessionId)) {\n throw new Error('Error: invalid_input: selected session is not available in this workspace')\n }\n if (body.model === null) next.model = undefined\n else if (body.model !== undefined) next.model = checkModel(body.model, options.modelProviders)\n // Isolation may change only before the first execution (分支与基线\n // 取决于该选择 — plan §3.1: 执行开始后锁定).\n const isolationRaw = str(body, 'isolation')\n if (isolationRaw !== null) {\n if (task.executions.length > 0 || task.status === 'in_progress') {\n throw new Error('Error: invalid_input: isolation 已锁定(任务已有执行记录),不可修改')\n }\n next.isolation = asIsolation(isolationRaw)\n }\n // Preset may change any time: each run composes fresh.\n if (body.presetId === null) delete next.presetId\n else if (body.presetId !== undefined) next.presetId = normalizePresetId(str(body, 'presetId'))!\n // Permission (0.5.5): 'workspace-write' | 'read-only' | 'danger-full-access'\n if (body.permission === null) delete next.permission\n else if (body.permission !== undefined) next.permission = asPermission(body.permission)\n // Checklist (0.4.0): the GUI replaces the whole list; null clears.\n if (body.checklist === null) delete next.checklist\n else if (body.checklist !== undefined) {\n const items = normalizeChecklist(body.checklist)\n if (items.length > 0) next.checklist = items\n else delete next.checklist\n }\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: summarize(next!) })\n return\n }\n if (action === 'move') {\n const ifVersion = num(body, 'ifVersion')\n const status = str(body, 'status') ?? ''\n const archiveSessions = body.archiveSessions === true\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n const to = asStatus(status)\n let next: TaskRecord | undefined\n let beforeTask: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (!canTransition(task.status, to)) throw new Error(`Error: invalid_transition: illegal transition ${task.status} → ${to}`)\n beforeTask = task\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n if (task.status === 'todo' && to === 'in_progress') next.blocked = false\n // A user move records no holder; leaving in_progress releases any hold.\n syncClaim(next, to, options.now())\n ledger.tasks[index] = next\n return [next]\n })\n const sessionArchive = to === 'archived' && archiveSessions\n ? await archiveTaskSessions(beforeTask ?? next!, workspaces.archiveSession)\n : undefined\n json(res, { ok: true, value: { ...summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) } })\n return\n }\n if (action === 'reject') {\n // Card quick-reject: back to todo + optional user comment in one\n // atomic mutation (a failed move never strands an orphan comment).\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n const commentText = str(body, 'body') ?? ''\n let next: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (task.status !== 'in_review') throw new Error(`Error: invalid_transition: reject requires in_review (current ${task.status})`)\n if (!canTransition(task.status, 'todo')) throw new Error(`Error: invalid_transition: illegal transition ${task.status} → todo`)\n next = structuredClone(task)\n next.status = 'todo'\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n syncClaim(next, 'todo', options.now())\n if (commentText.trim().length > 0) {\n next.comments.push({ id: newCommentId(), body: normalizeBody(commentText), version: 1, createdAt: options.now() })\n }\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: summarize(next!) })\n return\n }\n if (action === 'comment') {\n const bodyText = str(body, 'body') ?? ''\n const comment = { id: newCommentId(), body: normalizeBody(bodyText), version: 1, createdAt: options.now() }\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (task.status === 'archived') throw new Error('Error: invalid_transition: archived tasks are immutable')\n const next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = options.now()\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: comment }, 201)\n return\n }\n if (action === 'delete') {\n const purge = body.purge === true\n if (purge) {\n if (task.trashedAt === undefined) throw new Error('Error: invalid_input: purge requires a trashed task (soft-delete first)')\n // Worktree safety before purge (plan §3.3, 0.3.1): refuse while\n // uncommitted work remains; otherwise clean the worktree and\n // the task branch along with the ledger entry.\n if (options.git !== undefined) {\n const ws = workspaces.get(task.workspaceId)\n if (ws !== undefined) {\n const path = worktreePathOf(ws.path, id)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: the whole mirror (every repo worktree) goes\n // through the aggregated children-first removal — one\n // dirty repo refuses BEFORE anything is deleted.\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId: id },\n )\n // Leftovers unknown to git ('unregistered' worktrees, or a\n // plain mirror dir when the workspace root is no repo).\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n // Structured classification (review P2): git tags its dirty\n // rejections with a code; the keyword stays as a fallback.\n const dirty = (error as { code?: string }).code === 'dirty-worktree'\n || (error as { code?: string }).code === 'dirty-mirror'\n || message.includes('未提交修改')\n if (dirty) {\n throw new Error(`Error: invalid_input: ${message};请先处理这些改动(提交、续跑或手动保存)再物理清除任务`)\n }\n throw new Error(`Error: invalid_input: ${message}`)\n }\n // Branches die with the task (best effort, per repo).\n const branchTargets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) branchTargets.push({ repo, branch })\n }\n if (task.branch !== undefined && !branchTargets.some(t => t.repo === '')) branchTargets.push({ repo: '', branch: task.branch })\n for (const target of branchTargets) {\n try {\n await options.git.deleteBranch(repoMainPath(ws.path, target.repo), target.branch)\n } catch { /* best effort: the branch may outlive the task */ }\n }\n }\n }\n await store.mutate('task-deleted', ledger => {\n ledger.tasks = ledger.tasks.filter(t => t.id !== id)\n return []\n })\n json(res, { ok: true, value: { purged: true } })\n return\n }\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n // S5: a running execution keeps writing to the task — refuse the\n // soft-delete until it is cancelled or settled. T8: clear residue.\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new Error('Error: invalid_input: 任务有正在运行的执行,请先取消或等它结束再删除')\n }\n const next = structuredClone(task)\n next.trashedAt = options.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: { trashed: true } })\n return\n }\n if (action === 'run') {\n if (options.run === undefined) {\n const f = fail('invalid_input', 'execution service unavailable')\n json(res, f.res, 501)\n return\n }\n // `reuse: true` = 续跑: keep a live worktree/branch as-is instead\n // of resetting to a fresh baseline (0.3.1).\n const runOptions = body.reuse === true ? { reuseWorktree: true } : undefined\n const result = await options.run(id, runOptions)\n if (result.ok) json(res, { ok: true, value: result }, 202)\n else {\n const f = fail('invalid_input', result.error)\n json(res, f.res, f.status)\n }\n return\n }\n if (action === 'cancel') {\n if (options.cancel === undefined) {\n const f = fail('invalid_input', 'execution service unavailable')\n json(res, f.res, 501)\n return\n }\n const result = await options.cancel(id)\n if (result.ok) json(res, { ok: true, value: { cancelled: true, executionId: result.executionId } }, 202)\n else {\n const f = fail('invalid_input', result.error)\n json(res, f.res, f.status)\n }\n return\n }\n if (action === 'merge') {\n // ⇥ 合并 (detail page, user-only): merge the task branch(es) into\n // the main worktree(s) with --no-ff; conflicts are reported\n // verbatim. 0.6.3: multi-repo mirror tasks merge PER REPO —\n // sequentially, a failed repo never blocking the others.\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n if (task.status === 'in_progress') throw new Error('Error: invalid_input: 任务执行中,不能合并')\n if (task.executions.some(e => e.outcome === 'running')) throw new Error('Error: invalid_input: 任务执行中,不能合并')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n const targets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) targets.push({ repo, branch })\n }\n if (task.branch !== undefined && !targets.some(t => t.repo === '')) targets.unshift({ repo: '', branch: task.branch })\n if (targets.length === 0) throw new Error('Error: invalid_input: 该任务还没有 worktree 分支(未隔离执行过)')\n const multi = task.branches !== undefined\n const results: MergeRepoResult[] = []\n for (const target of targets) {\n if (!isValidRelRepoPath(target.repo)) throw new Error('Error: invalid_input: 非法的仓库路径')\n const repoRoot = repoMainPath(ws.path, target.repo)\n // No-op detection (0.3.1): a branch with no commits over HEAD\n // merges as \"already up to date\" — report that instead of landing\n // a bogus 已合并 comment.\n let noop = false\n try {\n noop = await options.git.isAncestor(repoRoot, target.branch)\n } catch { /* fail-soft: proceed to the real merge */ }\n if (noop) {\n results.push({ repo: target.repo, branch: target.branch, outcome: 'noop' })\n continue\n }\n try {\n // 0.6.3 review fix: parallel repos are self-governing — their\n // content AND their gitlink entries in the ROOT main checkout\n // (e.g. a container repo tracking `dsh-taskboard` as an embedded\n // repo) don't gate the root repo's merge. Child repos merge with\n // no extra exemption (their own .dsh-worktrees rule suffices).\n const exempt = target.repo === ''\n ? await (options.scanner ?? createRepoScanner()).findNestedRepos(ws.path).then(rs => rs.map(r => r.relPath))\n : undefined\n await options.git.merge(repoRoot, target.branch, exempt)\n results.push({ repo: target.repo, branch: target.branch, outcome: 'merged' })\n } catch (error) {\n results.push({\n repo: target.repo,\n branch: target.branch,\n outcome: 'failed',\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n // R1: the git merges above are slow — re-find the FRESH task inside\n // the mutation so a concurrent comment is never overwritten.\n const pushComment = (body: string, system?: { key: string; params?: Record<string, string>; rows?: SystemCommentRow[] }): Promise<void> =>\n store.mutate('comment-added', ledger => {\n const { index, task: fresh } = liveTaskAt(ledger, id)\n const next = structuredClone(fresh)\n next.comments.push({\n id: newCommentId(),\n body: normalizeBody(body),\n ...(system !== undefined ? {\n systemKey: system.key,\n ...(system.params !== undefined ? { systemParams: system.params } : {}),\n ...(system.rows !== undefined ? { systemRows: system.rows } : {}),\n } : {}),\n version: 1,\n createdAt: options.now(),\n })\n next.version = fresh.version + 1\n next.updatedAt = options.now()\n ledger.tasks[index] = next\n return [next]\n }).then(() => undefined)\n if (!multi) {\n // Legacy single-repo shape, byte-identical to 0.3.x–0.6.x.\n const root = results.find(r => r.repo === '')\n if (root === undefined) throw new Error('Error: invalid_input: 该任务还没有 worktree 分支(未隔离执行过)')\n if (root.outcome === 'noop') {\n json(res, { ok: true, value: { merged: false, noop: true, branch: root.branch } })\n return\n }\n if (root.outcome === 'failed') {\n throw new Error(`Error: invalid_input: ${root.error ?? '合并失败'}`)\n }\n await pushComment(`[系统] 分支 ${root.branch} 已合并到主工作区(--no-ff)。`, { key: 'sys.mergeSingle', params: { branch: root.branch } })\n json(res, { ok: true, value: { merged: true, branch: root.branch } })\n return\n }\n const labelOf = (repo: string): string => repo === '' ? '根仓库' : repo\n const mergedCount = results.filter(r => r.outcome === 'merged').length\n const failedCount = results.filter(r => r.outcome === 'failed').length\n const summary = results\n .map(r => r.outcome === 'merged'\n ? `${labelOf(r.repo)} ✓ 已合并`\n : r.outcome === 'noop' ? `${labelOf(r.repo)} ⟲ 无新提交` : `${labelOf(r.repo)} ✗ ${(r.error ?? '合并失败').slice(0, 150)}`)\n .join(';')\n await pushComment(`[系统] 分支已按仓库合并(--no-ff):${summary}`, {\n key: 'sys.mergeMulti',\n rows: results.map(r => ({ repo: r.repo, outcome: r.outcome, ...(r.error !== undefined ? { error: r.error.slice(0, 150) } : {}) })),\n })\n json(res, {\n ok: true,\n value: {\n merged: mergedCount > 0,\n ...(mergedCount === 0 && failedCount === 0 ? { noop: true } : {}),\n results,\n },\n })\n return\n }\n if (action === 'worktree-remove') {\n // 🗑 删除 worktree (detail page): refuses uncommitted changes;\n // optionally deletes the task branch after the worktree is gone.\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n if (task.executions.some(e => e.outcome === 'running')) throw new Error('Error: invalid_input: 任务执行中,不能删除 worktree')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n const path = worktreePathOf(ws.path, id)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: aggregated children-first mirror removal (see purge).\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId: id },\n )\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n throw new Error(`Error: invalid_input: ${error instanceof Error ? error.message : String(error)}`)\n }\n let branchDeleted = false\n let branchError: string | undefined\n if (body.deleteBranch === true) {\n const branchTargets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) branchTargets.push({ repo, branch })\n }\n if (task.branch !== undefined && !branchTargets.some(t => t.repo === '')) branchTargets.push({ repo: '', branch: task.branch })\n let failures = 0\n for (const target of branchTargets) {\n try {\n await options.git.deleteBranch(repoMainPath(ws.path, target.repo), target.branch)\n } catch (error) {\n failures += 1\n const label = target.repo === '' ? '根仓库' : target.repo\n branchError = `${label}:${error instanceof Error ? error.message : String(error)}`\n }\n }\n branchDeleted = failures === 0\n }\n json(res, { ok: true, value: { removed: true, branchDeleted, ...(branchError !== undefined ? { branchError } : {}) } })\n return\n }\n const f = fail('not_found', `unknown action ${action}`)\n json(res, f.res, f.status)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ----------------------------------- POST /queue/clear (board queue panel)\n if (pathname === `${ROUTE_PREFIX}/queue/clear`) {\n if (options.clearQueue === undefined) {\n const f = fail('invalid_input', 'scheduler queue clear unavailable')\n json(res, f.res, 501)\n return\n }\n try {\n const cleared = await options.clearQueue()\n json(res, { ok: true, value: { cleared } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // -------------------------------------- POST /worktree-cleanup (⚙ 诊断)\n if (pathname === `${ROUTE_PREFIX}/worktree-cleanup`) {\n try {\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n const workspaceId = str(body, 'workspaceId') ?? ''\n const taskId = str(body, 'taskId') ?? ''\n const ws = workspaces.get(workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n // Only dirs owned by NO ledger task may be cleaned here; live tasks\n // remove their worktree from the detail page.\n if (store.get(taskId) !== undefined) throw new Error('Error: invalid_input: 任务仍在看板中,请从任务详情页删除其 worktree')\n // worktreePathOf throws on an illegal id charset (R4②); the resolved\n // target must additionally stay inside the plugin's own worktree\n // scope — the taskId is fully attacker-controlled body input (R4③).\n const path = worktreePathOf(ws.path, taskId)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: an orphan mirror may hold several repo worktrees —\n // aggregated children-first removal, then the leftover fs rm\n // (scope-verified above).\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId },\n )\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n throw new Error(`Error: invalid_input: ${error instanceof Error ? error.message : String(error)}`)\n }\n json(res, { ok: true, value: { cleaned: true, path } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ---------------------------------------------- POST /import/preview\n // (0.4.0) Dry-run: classify every task in the uploaded ledger file\n // against the live one; nothing is written.\n if (pathname === `${ROUTE_PREFIX}/import/preview`) {\n try {\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n const plan = validateLedgerImport(body, known, options.now())\n json(res, {\n ok: true,\n value: {\n plan: {\n create: plan.create.map(t => ({ id: t.id, title: t.title, status: t.status })),\n overwrite: plan.overwrite.map(t => ({ id: t.id, title: t.title, status: t.status })),\n invalid: plan.invalid,\n },\n },\n })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------------------ POST /import\n // (0.4.0) Commit an import. mode=merge upserts (create + overwrite by\n // id); mode=replace swaps the WHOLE ledger (invalid entries dropped)\n // after writing a timestamped backup of the current one.\n if (pathname === `${ROUTE_PREFIX}/import`) {\n try {\n const mode = str(body, 'mode') === 'replace' ? 'replace' as const : 'merge' as const\n const raw = body.ledger\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n const plan = validateLedgerImport(raw, known, options.now())\n const imported = [...plan.create, ...plan.overwrite]\n if (mode === 'replace' && imported.length === 0) {\n throw new Error('Error: invalid_input: 导入文件没有可导入的任务,已拒绝整册替换')\n }\n let backupFile: string | undefined\n if (mode === 'replace' && store.snapshot().tasks.length > 0) {\n backupFile = await store.backup()\n }\n let replacedTotal: number | undefined\n await store.mutate('ledger-replaced', ledger => {\n if (mode === 'replace') {\n // S6: a whole-ledger swap must not strand live executions — the\n // running sessions keep working with no task to settle into.\n if (ledger.tasks.some(t => t.executions.some(e => e.outcome === 'running'))) {\n throw new Error('Error: invalid_input: 有任务正在执行,不能整册替换(请先取消或等待结束)')\n }\n replacedTotal = ledger.tasks.length\n ledger.tasks = structuredClone(imported)\n // Replace is a whole-ledger swap (0.5.0): board settings ride\n // along when the file carries them; merge keeps the live ones.\n if (plan.settings !== undefined) ledger.settings = structuredClone(plan.settings)\n else delete ledger.settings\n return ledger.tasks\n }\n const byId = new Map(ledger.tasks.map(t => [t.id, t]))\n for (const task of imported) {\n // S6: merging over a task whose execution is live would orphan\n // that run the same way — refuse the overwrite.\n const existing = byId.get(task.id)\n if (existing !== undefined && existing.executions.some(e => e.outcome === 'running')) {\n throw new Error(`Error: invalid_input: 任务 ${task.id} 正在执行,不能被导入覆盖`)\n }\n byId.set(task.id, structuredClone(task))\n }\n ledger.tasks = [...byId.values()]\n return structuredClone(imported)\n })\n json(res, {\n ok: true,\n value: {\n mode,\n created: plan.create.length,\n overwritten: plan.overwrite.length,\n ...(mode === 'replace' ? { replacedTotal } : {}),\n ...(backupFile !== undefined ? { backupFile } : {}),\n },\n })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------- POST /templates (+delete)\n if (pathname === `${ROUTE_PREFIX}/templates` || pathname === `${ROUTE_PREFIX}/templates/delete`) {\n try {\n if (options.templates === undefined) {\n const f = fail('invalid_input', 'template store unavailable')\n json(res, f.res, 501)\n return\n }\n if (pathname.endsWith('/delete')) {\n const id = str(body, 'id') ?? ''\n if (id.length === 0) throw new Error('Error: invalid_input: id required')\n const deleted = await options.templates.remove(id)\n json(res, { ok: true, value: { deleted } })\n return\n }\n const name = str(body, 'name') ?? ''\n if (name.trim().length === 0) throw new Error('Error: invalid_input: name required')\n const template = await options.templates.upsert({\n id: str(body, 'id') ?? undefined,\n name,\n task: normalizeTemplateSpec(body.task, options.now()),\n })\n json(res, { ok: true, value: template }, 201)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------ POST /settings/update\n // (0.5.0) Whole-object replace semantics: omitted fields fall back to\n // their factory defaults. Affects only tasks created AFTER the change.\n if (pathname === `${ROUTE_PREFIX}/settings/update`) {\n try {\n const next = asBoardSettings(body)\n await store.mutate('settings-updated', ledger => {\n ledger.settings = next\n return []\n })\n json(res, { ok: true, value: next })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n res.writeHead(404)\n res.end()\n } catch (error) {\n const f = fail('internal', error instanceof Error ? error.message : String(error))\n json(res, f.res, f.status)\n }\n }\n\n const sse = (req: IncomingMessage, res: ServerResponse): void => {\n res.writeHead(200, {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n })\n res.write('retry: 2000\\n\\n')\n // Baseline frame: the client reconciles by revision and refetches state on gaps.\n res.write(`event: hello\\ndata: ${JSON.stringify({ revision: store.snapshot().revision })}\\n\\n`)\n subscribers.add(res)\n // S2: a socket that dies between 'close' detection and the next write\n // would emit 'error' on the response — unhandled, that escalates to an\n // uncaughtException. Drop the subscriber instead.\n res.on('error', () => { subscribers.delete(res) })\n if (heartbeat === undefined) {\n heartbeat = setInterval(() => {\n for (const current of subscribers) current.write(': ping\\n\\n')\n }, HEARTBEAT_MS)\n }\n req.on('close', () => {\n subscribers.delete(res)\n if (subscribers.size === 0 && heartbeat !== undefined) {\n clearInterval(heartbeat)\n heartbeat = undefined\n }\n })\n }\n\n const disposers = [\n ctx.webServer.register({ kind: 'prefix', path: ROUTE_PREFIX, handler }),\n ctx.webServer.register({ kind: 'exact', path: SSE_PATH, handler: sse }),\n ]\n return () => {\n unsubscribeBroadcast()\n for (const dispose of disposers) dispose()\n if (heartbeat !== undefined) clearInterval(heartbeat)\n for (const res of subscribers) res.end()\n subscribers.clear()\n }\n}\n"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"routes.js","names":[],"sources":["../../src/host/routes.ts"],"sourcesContent":["import { archiveTaskSessions } from './archive-sessions.ts'\n/**\n * /dsh-taskboard routes on the shared DSH webserver: a JSON API for the\n * GUI's human operations (create/update/move/comment/delete — actor `user`,\n * the done move IS allowed here) plus an SSE stream mirroring every\n * committed ledger mutation.\n *\n * All domain validation goes through the shared protocol pure functions; the\n * route layer only maps transport to envelope.\n *\n * @module dsh-taskboard/host/routes\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { readdir, rm } from 'node:fs/promises'\nimport { join, resolve, sep } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only: pulls the webServer Context merge (ctx.webServer).\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport {\n asBoardSettings,\n asIsolation,\n asPermission,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n defaultPermissionOf,\n isValidRelRepoPath,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeChecklist,\n normalizeExecution,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n validateLedgerImport,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n type SystemCommentRow,\n} from '../shared/protocol.ts'\nimport { WORKTREE_DIR, worktreePathOf, type GitFace } from './git.ts'\nimport { removeMirror, repoMainPath } from './isolation.ts'\nimport { createRepoScanner, type RepoScanner } from './repos.ts'\nimport { activeHostLocale } from './locale.ts'\nimport type { CatalogModelItem, CatalogPresetItem, MergeRepoResult, TaskTemplate } from '../shared/api.ts'\nimport type { TemplateStore } from './templates.ts'\nimport { MAX_ASSET_BYTES, type AssetStore } from './assets.ts'\nimport type { StorageCoordinator } from './storage.ts'\nimport { ROUTE_PREFIX, SSE_PATH, type ApiFail, type ApiResult } from '../shared/api.ts'\nimport type { TaskStore } from './store.ts'\nimport { ERR, ToolError } from './tools.ts'\nimport type { WorkspaceFace } from './tools.ts'\n\n/** Heartbeat cadence for the SSE stream. */\nconst HEARTBEAT_MS = 20_000\n\n/** Max accepted JSON body bytes (S8: unbounded buffering is a local OOM vector). */\nconst MAX_BODY_BYTES = 5 * 1024 * 1024\n\n/** Route shapes (T2: compiled once at module load, not on every request). */\nconst TASK_DIFF_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)/diff$`)\nconst TASK_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)$`)\nconst TASK_ACTION_RE = new RegExp(`^${ROUTE_PREFIX}/tasks/([^/]+)/([\\\\w-]+)$`)\nconst ASSET_RE = new RegExp(`^${ROUTE_PREFIX}/assets/([a-f0-9]{64}\\\\.(?:png|jpg|gif|webp))$`)\n\n/** How long a workspace git-detection result stays cached (fail-soft). */\nconst GIT_DETECT_TTL_MS = 60_000\n\n/** The workspaces face routes need (same narrow shape as tools). */\nexport type RoutesWorkspaceFace = WorkspaceFace\n\n/** Options. */\nexport interface TaskboardRoutesOptions {\n store: TaskStore\n workspaces: RoutesWorkspaceFace\n now: () => number\n /** Current scheduler concurrency limit, for queue observability only. */\n maxConcurrent?: () => number\n /** Drop every durable queue entry (board queue panel); absent → 501. */\n clearQueue?: () => Promise<number>\n /** Manual-run hook (the execution service); absent → 501. Options carry `reuseWorktree` (续跑). */\n run?: (taskId: string, options?: { reuseWorktree?: boolean }) => Promise<{ ok: true; executionId: string; sessionId: string } | { ok: false; error: string }>\n /** Cancel hook (the execution service); absent → 501. */\n cancel?: (taskId: string) => Promise<{ ok: true; executionId: string } | { ok: false; error: string }>\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable.\n */\n modelProviders?: () => string[] | undefined\n /** Git face for worktree actions + workspace git detection; absent → 501 on git actions. */\n git?: GitFace\n /** Nested-repo scanner for mirror removal; absent → a default one is built on first use. */\n scanner?: RepoScanner\n /** Task-template store (0.4.0); absent → 501 on template actions. */\n templates?: TemplateStore\n /** Durable image attachment store (0.7.0); absent → attachment routes unavailable. */\n assets?: AssetStore\n /** Configurable data-directory coordinator (0.7.0). */\n storage?: StorageCoordinator\n /** Initial storage/ledger readiness barrier. */\n ready?: () => Promise<void>\n /** Prompt completions face (0.5.5; dynamically discovers skills & commands). */\n promptCompletions?: () => Promise<{\n skills?: Array<{ name: string; description?: string }>\n commands?: Array<{ name: string; description?: string; hint?: string }>\n }>\n /** Model and preset catalog face (0.5.5; dynamically discovers models & presets). */\n modelCatalog?: () => Promise<{\n models?: CatalogModelItem[]\n presets?: CatalogPresetItem[]\n defaultPresetId?: string\n }>\n}\n\n/** Validate a template's task spec (routes-side, unknown → invalid_input). */\nfunction normalizeTemplateSpec(raw: unknown, now: number): TaskTemplate['task'] {\n if (typeof raw !== 'object' || raw === null) throw new Error('Error: invalid_input: task must be an object')\n const e = raw as Record<string, unknown>\n const spec: TaskTemplate['task'] = {}\n const str = (key: string): string | undefined => {\n const v = e[key]\n if (v === undefined) return undefined\n if (typeof v !== 'string') throw new Error(`Error: invalid_input: task.${key} must be a string`)\n return v\n }\n const title = str('title')\n const description = str('description')\n const prompt = str('prompt')\n const urgency = str('urgency')\n const isolation = str('isolation')\n const presetId = str('presetId')\n const permission = str('permission')\n if (title !== undefined) spec.title = normalizeTitle(title)\n if (description !== undefined) spec.description = description\n if (prompt !== undefined) spec.prompt = normalizePrompt(prompt)\n if (urgency !== undefined) spec.urgency = asUrgency(urgency)\n if (isolation !== undefined) spec.isolation = asIsolation(isolation)\n if (presetId !== undefined && presetId.trim().length > 0) spec.presetId = presetId.trim()\n if (permission !== undefined && permission.trim().length > 0) spec.permission = asPermission(permission)\n if (e.execution !== undefined) {\n spec.execution = normalizeExecution(e.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown }, now)\n }\n if (e.model !== undefined) spec.model = normalizeModel(e.model)\n if (e.checklist !== undefined) {\n if (!Array.isArray(e.checklist) || e.checklist.some(c => typeof c !== 'string')) {\n throw new Error('Error: invalid_input: task.checklist must be an array of strings')\n }\n checklistFromTexts(e.checklist as string[]) // validates count + texts\n spec.checklist = e.checklist as string[]\n }\n return spec\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(raw: unknown, modelProviders?: () => string[] | undefined): TaskModel {\n const model = normalizeModel(raw)\n const providers = modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new Error(`Error: invalid_input: model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** JSON-envelope writer. */\nfunction json(res: ServerResponse, payload: ApiResult<unknown>, status = 200): void {\n const body = JSON.stringify(payload)\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })\n res.end(body)\n}\n\n/** Domain failure → envelope + HTTP status. */\nfunction fail(code: ApiFail['error']['code'], message: string): { res: ApiFail; status: number } {\n const status = code === 'invalid_input' || code === 'invalid_transition' ? 400\n : code === 'not_found' ? 404\n : code === 'version_conflict' ? 409\n : code === 'forbidden' ? 403\n : 500\n return { res: { ok: false, error: { code, message } }, status }\n}\n\n/**\n * Read one JSON body (null on parse failure). S8: rejects bodies over\n * MAX_BODY_BYTES by throwing — the local, unauthenticated HTTP surface must\n * not be an unbounded memory sink.\n */\nasync function readBody(req: IncomingMessage): Promise<Record<string, unknown> | null> {\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of req) {\n total += (chunk as Buffer).length\n if (total > MAX_BODY_BYTES) throw new Error('body too large')\n chunks.push(chunk as Buffer)\n }\n if (chunks.length === 0) return {}\n try {\n const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n return typeof parsed === 'object' && parsed !== null ? parsed as Record<string, unknown> : null\n } catch {\n return null\n }\n}\n\n/** Read one bounded binary upload without ever buffering beyond the file cap. */\nasync function readBytes(req: IncomingMessage, limit: number): Promise<Buffer> {\n const chunks: Buffer[] = []\n let total = 0\n for await (const chunk of req) {\n const bytes = chunk as Buffer\n total += bytes.length\n if (total > limit) throw new Error('body too large')\n chunks.push(bytes)\n }\n return Buffer.concat(chunks)\n}\n\n/** String field accessor (null when absent/not a string). */\nfunction str(body: Record<string, unknown>, key: string): string | null {\n const v = body[key]\n return typeof v === 'string' ? v : null\n}\n\n/** Number field accessor (undefined when absent; null when present but not a number). */\nfunction num(body: Record<string, unknown>, key: string): number | undefined | null {\n const v = body[key]\n if (v === undefined) return undefined\n return typeof v === 'number' && Number.isFinite(v) ? v : null\n}\n\n/** Find a live task INSIDE a mutator (R1: guards run on the fresh draft). */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0 || ledger.tasks[index]!.trashedAt !== undefined) throw new Error('Error: not_found: no such task')\n return { index, task: ledger.tasks[index]! }\n}\n\n/** Normalize an agent preset id: trimmed, non-empty; empty string → undefined. */\nfunction normalizePresetId(raw: string | null): string | undefined {\n const t = (raw ?? '').trim()\n return t.length === 0 ? undefined : t\n}\n\n/** Map a thrown domain error to the envelope. */\nfunction toFail(error: unknown): { res: ApiFail; status: number } {\n const message = error instanceof Error ? error.message : String(error)\n // Structured path first (review P2): ToolError carries its code — no need\n // to parse the 'Error: <code>: …' prefix it also renders into the message.\n if (error instanceof ToolError) {\n const mapped = error.code === ERR.workspaceMismatch ? 'forbidden' : error.code\n const known: ApiFail['error']['code'][] = ['invalid_input', 'not_found', 'version_conflict', 'invalid_transition', 'forbidden', 'internal']\n if ((known as string[]).includes(mapped)) {\n return fail(mapped as ApiFail['error']['code'], message.slice(7 + error.code.length + 2))\n }\n }\n const code = message.startsWith('Error: ') ? message.slice(7).split(':')[0] : undefined\n const known2: ApiFail['error']['code'][] = ['invalid_input', 'not_found', 'version_conflict', 'invalid_transition', 'forbidden', 'internal']\n if (code !== undefined && (known2 as string[]).includes(code)) {\n return fail(code as ApiFail['error']['code'], message.slice(7 + code.length + 2))\n }\n if (code === 'workspace_mismatch') return fail('forbidden', message.slice(7 + code.length + 2))\n return fail('invalid_input', message)\n}\n\n/**\n * Register the taskboard routes.\n * @param ctx - context carrying the webServer service.\n * @param options - store + workspaces + clock.\n * @returns the disposer.\n */\nexport function registerTaskboardRoutes(ctx: Context, options: TaskboardRoutesOptions): () => void {\n const { store, workspaces } = options\n const subscribers = new Set<ServerResponse>()\n let heartbeat: NodeJS.Timeout | undefined\n\n const queueSummary = (ledger: TaskLedger) => {\n let depth = 0\n let dispatching = 0\n let oldestQueuedAt: number | undefined\n for (const task of ledger.tasks) {\n if (task.execution.queuedRunAt !== undefined) {\n depth += 1\n if (task.execution.queuedAt !== undefined && (oldestQueuedAt === undefined || task.execution.queuedAt < oldestQueuedAt)) oldestQueuedAt = task.execution.queuedAt\n }\n if (task.execution.dispatchingRunAt !== undefined) dispatching += 1\n }\n const now = options.now()\n return {\n depth,\n dispatching,\n ...(oldestQueuedAt === undefined ? {} : { oldestQueuedAt, oldestWaitMinutes: Math.max(0, Math.floor((now - oldestQueuedAt) / 60_000)) }),\n maxConcurrent: options.maxConcurrent?.() ?? 0,\n }\n }\n\n /** R4③: a cleanup/purge target must resolve INSIDE <ws>/.dsh-worktrees — string joining alone is never trusted with an rm. */\n const insideWorktreeScope = (wsPath: string, target: string): boolean => {\n const scope = resolve(wsPath, WORKTREE_DIR)\n const resolved = resolve(target)\n return resolved === scope || resolved.startsWith(scope + sep)\n }\n\n const broadcast = (change: { revision: number; kind: string; tasks: readonly TaskRecord[] }): void => {\n const frame = `event: change\\ndata: ${JSON.stringify({ revision: change.revision, kind: change.kind, tasks: change.tasks.map(summarize) })}\\n\\n`\n for (const res of subscribers) res.write(frame)\n }\n const unsubscribeBroadcast = store.subscribe(broadcast)\n\n // Workspace git detection, TTL-cached and fail-soft (false on any error):\n // feeds the create-form isolation toggle and the diagnostics panel.\n const gitCache = new Map<string, { value: boolean; at: number; repoCount?: number }>()\n const gitHinted = new Set<string>()\n\n /** Whether <root>/.gitignore (missing file counts as missing) ignores our worktree dir. */\n const gitignoreMissing = async (path: string): Promise<boolean> => {\n try {\n const { readFile } = await import('node:fs/promises')\n const ignore = await readFile(join(path, '.gitignore'), 'utf8')\n return !ignore.split('\\n').some(l => {\n const t = l.trim().replace(/\\/+$/, '')\n return t === WORKTREE_DIR || t === `/${WORKTREE_DIR}`\n })\n } catch {\n return true // no .gitignore at all (or unreadable) → suggest creating one\n }\n }\n\n // One scanner for every route-side discovery (the injected one or a\n // shared real-IO fallback whose TTL cache actually helps — 0.6.3).\n const sharedScanner: RepoScanner = options.scanner ?? createRepoScanner()\n\n /**\n * Whether the workspace root itself is a git repo (the .gitignore-suggestion\n * gate — a plain container has no repo that could ignore anything).\n */\n const rootIsRepo = async (path: string): Promise<boolean> => {\n try {\n return (await options.git?.detect(path)) === true\n } catch {\n return false // fail-soft\n }\n }\n\n /**\n * Workspace repo facts for the form (0.6.3): `gitAvailable` gates the\n * worktree option, `repoCount` feeds the mirror badge. Availability now\n * covers PARALLEL MULTI-REPO workspaces too: a root repo qualifies as\n * before, and a workspace whose root is NOT a repo still qualifies when\n * the scanner finds nested repos — prepareMirror isolates exactly that\n * container shape (mirror root = plain dir, one worktree per nested repo),\n * so the form must not lock the capability away.\n */\n const workspaceRepos = async (path: string): Promise<{ gitAvailable: boolean; repoCount: number }> => {\n if (options.git === undefined) return { gitAvailable: false, repoCount: 0 }\n const hit = gitCache.get(path)\n if (hit !== undefined && options.now() - hit.at < GIT_DETECT_TTL_MS) {\n return { gitAvailable: hit.value, repoCount: hit.repoCount ?? (hit.value ? 1 : 0) }\n }\n let rootRepo = false\n try {\n rootRepo = await options.git.detect(path)\n } catch { /* fail-soft → false */ }\n // gitignore 建议 (plan §3.2): suggest (never write) ignoring our\n // worktree directory, once per workspace per host run. Root repos only.\n // The line is localized from the DSH locale preference (see host/locale.ts).\n if (rootRepo && !gitHinted.has(path)) {\n gitHinted.add(path)\n if (await gitignoreMissing(path)) {\n const file = `${path}/.gitignore`\n const hint = activeHostLocale(ctx) === 'zh'\n ? `建议在 ${file} 加入一行 ${WORKTREE_DIR}/ 以隐藏任务 worktree 目录(不会自动修改)`\n : `suggests adding one line to ${file}: ${WORKTREE_DIR}/ to hide the task worktree directory (no automatic edits)`\n console.info(`[dsh-taskboard] ${hint}`)\n }\n }\n // The nested scan always runs: repoCount needs it even when the root\n // is a repo (root repo + nested repos = the mirror shape). The scanner's\n // own TTL cache keeps repeated workspace polls cheap.\n let nestedCount = 0\n try {\n nestedCount = (await sharedScanner.findNestedRepos(path)).length\n } catch { /* fail-soft → 0 */ }\n const value = rootRepo || nestedCount > 0\n const repoCount = (rootRepo ? 1 : 0) + nestedCount\n gitCache.set(path, { value, at: options.now(), repoCount })\n return { gitAvailable: value, repoCount }\n }\n\n /** List orphan worktree dirs: entries under <ws>/.dsh-worktrees owned by no ledger task. */\n const listOrphanWorktrees = async (): Promise<Array<{ workspaceId: string; workspacePath: string; taskId: string; path: string }>> => {\n const orphans: Array<{ workspaceId: string; workspacePath: string; taskId: string; path: string }> = []\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n for (const ws of workspaces.list()) {\n let entries: string[] = []\n try {\n const dirents = await readdir(join(ws.path, WORKTREE_DIR), { withFileTypes: true })\n entries = dirents.filter(e => e.isDirectory()).map(e => e.name)\n } catch { /* no worktrees dir → nothing to do */ }\n for (const taskId of entries) {\n if (!known.has(taskId)) orphans.push({ workspaceId: ws.id, workspacePath: ws.path, taskId, path: worktreePathOf(ws.path, taskId) })\n }\n }\n return orphans\n }\n\n /** Git-enabled workspaces whose .gitignore does not cover the worktree dir. */\n const listGitignoreSuggestions = async (): Promise<Array<{ workspaceId: string; workspacePath: string }>> => {\n const suggestions: Array<{ workspaceId: string; workspacePath: string }> = []\n for (const ws of workspaces.list()) {\n // Root repos only: a plain container has no .gitignore any repo reads.\n if (!(await rootIsRepo(ws.path))) continue\n if (await gitignoreMissing(ws.path)) suggestions.push({ workspaceId: ws.id, workspacePath: ws.path })\n }\n return suggestions\n }\n\n const handler = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n const url = new URL(req.url ?? '/', 'http://x')\n const pathname = url.pathname\n\n // ---------------------------------------------------------------- GET\n if (req.method === 'GET') {\n if (pathname === `${ROUTE_PREFIX}/storage`) {\n if (options.storage === undefined) { res.writeHead(501); res.end(); return }\n json(res, { ok: true, value: await options.storage.status() })\n return\n }\n await options.ready?.()\n const assetMatch = pathname.match(ASSET_RE)\n if (assetMatch !== null) {\n const asset = await options.assets?.read(assetMatch[1]!)\n if (asset === undefined) { res.writeHead(404); res.end(); return }\n res.writeHead(200, {\n 'content-type': asset.mime,\n 'content-length': asset.bytes.length,\n 'cache-control': 'public, max-age=31536000, immutable',\n 'x-content-type-options': 'nosniff',\n })\n res.end(asset.bytes)\n return\n }\n if (pathname === `${ROUTE_PREFIX}/state`) {\n await store.load()\n const ledger = store.snapshot()\n json(res, { ok: true, value: { ...ledger, capabilities: { archiveSessions: typeof workspaces.archiveSession === 'function' }, queue: queueSummary(ledger) } })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/workspaces`) {\n const list = workspaces.list()\n const info = await Promise.all(list.map(ws => workspaceRepos(ws.path)))\n json(res, {\n ok: true,\n value: list.map((ws, i) => ({ ...ws, sessionCount: 0, gitAvailable: info[i]!.gitAvailable, repoCount: info[i]!.repoCount })),\n })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/project-sessions`) {\n const workspaceId = url.searchParams.get('workspaceId') ?? ''\n if (workspaces.get(workspaceId) === undefined) {\n const f = fail('not_found', 'unknown workspace')\n json(res, f.res, f.status)\n return\n }\n json(res, { ok: true, value: { sessions: (workspaces.sessionIds?.(workspaceId) ?? []).map(id => ({ id })) } })\n return\n }\n if (pathname === `${ROUTE_PREFIX}/diagnostics`) {\n const ledger = store.snapshot()\n const queue = queueSummary(ledger)\n let staleRunning = 0\n for (const t of ledger.tasks) {\n for (const e of t.executions) if (e.outcome === 'running') staleRunning += 1\n }\n json(res, {\n ok: true,\n value: {\n revision: ledger.revision,\n tasks: ledger.tasks.length,\n staleRunning,\n queue,\n orphanWorktrees: await listOrphanWorktrees(),\n gitIgnoreSuggestions: await listGitignoreSuggestions(),\n },\n })\n return\n }\n // Diff viewer (0.4.0): read-only git show/diff for one execution's\n // commit or changed path. Prefers the live worktree (uncommitted\n // view), falls back to the main repo.\n const diffMatch = pathname.match(TASK_DIFF_RE)\n if (diffMatch !== null) {\n try {\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n const task = store.get(diffMatch[1]!)\n if (task === undefined) throw new Error('Error: not_found: no such task')\n const execution = task.executions.find(e => e.id === url.searchParams.get('execution'))\n if (execution === undefined) throw new Error('Error: not_found: no such execution')\n const commit = url.searchParams.get('commit')\n const filePath = url.searchParams.get('path')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n // 0.6.3: `?repo=` picks one repo of a multi-repo mirror ('' = the\n // root); without it the legacy flat fields decide, byte-identical.\n const repoParam = url.searchParams.get('repo')\n let cwd = execution.worktreePath ?? ws.path\n let mainRepo = ws.path\n let baseCommit = execution.baseCommit\n if (repoParam !== null) {\n const entry = execution.repos?.find(r => r.repo === repoParam)\n if (entry === undefined) throw new Error('Error: invalid_input: 该执行没有此仓库的镜像记录')\n cwd = entry.worktreePath\n mainRepo = repoMainPath(ws.path, entry.repo)\n baseCommit = entry.baseCommit\n }\n let result = commit !== null\n ? await options.git.showCommit(cwd, commit)\n : filePath !== null ? await options.git.showPathDiff(cwd, filePath, baseCommit) : undefined\n // Fallback: the worktree may be gone — commits and committed\n // ranges still resolve in the main repo.\n if (result === undefined && cwd !== mainRepo) {\n result = commit !== null\n ? await options.git.showCommit(mainRepo, commit)\n : filePath !== null && baseCommit !== undefined\n ? await options.git.showPathDiff(mainRepo, filePath, baseCommit)\n : undefined\n }\n if (result === undefined) {\n throw new Error('Error: invalid_input: 无法获取 diff(git 报错、对象不存在,或仅存于已删除的 worktree 且无基线)')\n }\n json(res, { ok: true, value: { diff: result.text, truncated: result.truncated } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // Templates listing (0.4.0).\n if (pathname === `${ROUTE_PREFIX}/templates`) {\n if (options.templates === undefined) {\n const f = fail('invalid_input', 'template store unavailable')\n json(res, f.res, 501)\n return\n }\n json(res, { ok: true, value: { templates: await options.templates.list() } })\n return\n }\n\n // Board settings (0.5.0): absent fields follow factory defaults.\n if (pathname === `${ROUTE_PREFIX}/settings`) {\n await store.load()\n json(res, { ok: true, value: store.snapshot().settings ?? {} })\n return\n }\n\n // Prompt completions (0.5.5; dynamically discovers skills & commands).\n if (pathname === `${ROUTE_PREFIX}/prompt-completions`) {\n const completions = await options.promptCompletions?.().catch(() => undefined)\n json(res, {\n ok: true,\n value: {\n commands: completions?.commands ?? [],\n skills: completions?.skills ?? [],\n },\n })\n return\n }\n\n // Model catalog (0.5.5; dynamically discovers models & presets from runtime).\n if (pathname === `${ROUTE_PREFIX}/model-catalog`) {\n const catalog = await options.modelCatalog?.().catch(() => undefined)\n json(res, {\n ok: true,\n value: {\n models: catalog?.models ?? [],\n presets: catalog?.presets ?? [],\n ...(catalog?.defaultPresetId !== undefined ? { defaultPresetId: catalog.defaultPresetId } : {}),\n },\n })\n return\n }\n\n const taskMatch = pathname.match(TASK_RE)\n if (taskMatch !== null) {\n const task = store.get(taskMatch[1]!)\n if (task === undefined) { const f = fail('not_found', 'no such task'); json(res, f.res, f.status); return }\n json(res, { ok: true, value: task })\n return\n }\n res.writeHead(404)\n res.end()\n return\n }\n\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST' })\n res.end()\n return\n }\n // Content-addressed image upload. The custom header makes this a\n // non-simple cross-origin request, preserving the JSON routes' CSRF fence.\n if (pathname === `${ROUTE_PREFIX}/assets`) {\n await options.ready?.()\n if (options.assets === undefined) {\n const f = fail('invalid_input', 'image attachments unavailable')\n json(res, f.res, 501)\n return\n }\n if (req.headers['x-dsh-taskboard-upload'] !== '1') {\n const f = fail('forbidden', 'missing upload header')\n json(res, f.res, 403)\n return\n }\n const declaredMime = String(req.headers['content-type'] ?? '').split(';', 1)[0]!.trim().toLowerCase()\n try {\n const bytes = await readBytes(req, MAX_ASSET_BYTES)\n await options.assets.cleanup(JSON.stringify(store.snapshot()))\n const asset = await options.assets.put(bytes, declaredMime)\n json(res, { ok: true, value: asset }, 201)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const status = message.includes('1..') ? 413 : message.includes('quota') ? 507 : 400\n const f = fail('invalid_input', message)\n json(res, f.res, status)\n }\n return\n }\n // CSRF fence: cross-site simple requests cannot set application/json.\n const contentType = req.headers['content-type'] ?? ''\n if (!contentType.toLowerCase().startsWith('application/json')) {\n const f = fail('invalid_input', 'content-type must be application/json')\n json(res, f.res, 415)\n return\n }\n let body: Record<string, unknown> | null\n try {\n body = await readBody(req)\n } catch {\n const f = fail('invalid_input', `request body exceeds ${MAX_BODY_BYTES} bytes`)\n json(res, f.res, 413)\n return\n }\n if (body === null) {\n const f = fail('invalid_input', 'body is not a JSON object')\n json(res, f.res, 400)\n return\n }\n\n // Storage location is bootstrap metadata outside the ledger. Keep it on\n // separate routes so JSON imports and whole-ledger replacement cannot\n // redirect host filesystem writes.\n if (pathname === `${ROUTE_PREFIX}/storage/check` || pathname === `${ROUTE_PREFIX}/storage/migrate`) {\n if (options.storage === undefined) {\n const f = fail('invalid_input', 'storage configuration unavailable')\n json(res, f.res, 501)\n return\n }\n try {\n const directory = str(body, 'directory') ?? ''\n const value = pathname.endsWith('/check')\n ? await options.storage.check(directory)\n : await options.storage.migrate(directory)\n json(res, { ok: true, value })\n } catch (error) {\n const f = fail('invalid_input', error instanceof Error ? error.message : String(error))\n json(res, f.res, f.status)\n }\n return\n }\n await options.ready?.()\n\n // ------------------------------------------------- POST /tasks (create)\n if (pathname === `${ROUTE_PREFIX}/tasks`) {\n try {\n const title = normalizeTitle(str(body, 'title') ?? '')\n const workspaceId = str(body, 'workspaceId') ?? ''\n if (workspaces.get(workspaceId) === undefined) throw new Error('Error: not_found: unknown workspace')\n const urgency = asUrgency(str(body, 'urgency') ?? '')\n const status = str(body, 'status') === null ? 'todo' as const : asStatus(str(body, 'status')!)\n if (status !== 'backlog' && status !== 'todo') {\n throw new Error('Error: invalid_transition: a new task must start as backlog or todo')\n }\n const execution = normalizeExecution((body.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown } | undefined) ?? {}, options.now())\n // New API-created scheduled tasks follow the same explicit default\n // as the form: create a fresh conversation on each trigger. Only\n // pre-existing ledgers with an omitted policy retain #26 behavior.\n if (execution.mode === 'scheduled' && execution.cron !== undefined\n && execution.reuseSessionId === undefined && execution.sessionReuseMode === undefined) {\n execution.sessionReuseMode = 'fresh'\n }\n if (execution.reuseSessionId !== undefined && !workspaces.sessionIds?.(workspaceId)?.includes(execution.reuseSessionId)) {\n throw new Error('Error: invalid_input: selected session is not available in this workspace')\n }\n const settings = store.snapshot().settings\n const modelRaw = body.model === undefined ? settings?.defaultModel : body.model\n const model = modelRaw == null ? undefined : checkModel(modelRaw, options.modelProviders)\n const isolationRaw = str(body, 'isolation')\n // 0.5.0: an omitted isolation is MATERIALIZED from the board\n // setting (看板设置) at creation, so later setting changes never\n // rewrite existing tasks.\n const isolation = isolationRaw === null ? defaultIsolationOf(store.snapshot().settings) : asIsolation(isolationRaw)\n const presetId = body.presetId === undefined ? settings?.defaultPresetId : normalizePresetId(str(body, 'presetId'))\n const permissionRaw = str(body, 'permission')\n const permission = permissionRaw === null ? defaultPermissionOf(store.snapshot().settings) : asPermission(permissionRaw)\n let checklist: TaskRecord['checklist'] = undefined\n if (body.checklist !== undefined) {\n if (!Array.isArray(body.checklist) || body.checklist.some(c => typeof c !== 'string')) {\n throw new Error('Error: invalid_input: checklist must be an array of strings')\n }\n const texts = (body.checklist as string[]).map(c => c.trim()).filter(c => c.length > 0)\n if (texts.length > 0) checklist = checklistFromTexts(texts)\n }\n const now = options.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (str(body, 'description') ?? '').trim(),\n prompt: normalizePrompt(str(body, 'prompt') ?? undefined),\n workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n ...(presetId !== undefined ? { presetId } : {}),\n permission,\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: { kind: 'user' },\n updatedBy: { kind: 'user' },\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n json(res, { ok: true, value: summarize(task) }, 201)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------- POST /tasks/:id/{action}\n // (\\w+ after the id would not match hyphenated actions like\n // worktree-remove, hence the explicit class in the hoisted pattern.)\n const actionMatch = pathname.match(TASK_ACTION_RE)\n if (actionMatch !== null) {\n const id = actionMatch[1]!\n const action = actionMatch[2]!\n try {\n const task = store.get(id)\n if (task === undefined) throw new Error('Error: not_found: no such task')\n if (action === 'archive-sessions') {\n if (task.trashedAt !== undefined || task.status !== 'archived') throw new Error('Error: invalid_transition: only archived live tasks can retry session archiving')\n const result = await archiveTaskSessions(task, workspaces.archiveSession)\n json(res, { ok: true, value: result })\n return\n }\n if (action === 'update') {\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n // R1: version guard + write inside the mutation, on the fresh draft.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (task.status === 'archived') throw new Error('Error: invalid_transition: archived tasks are immutable')\n next = structuredClone(task)\n const title = str(body, 'title')\n if (title !== null) next.title = normalizeTitle(title)\n const description = str(body, 'description')\n if (description !== null) next.description = description.trim()\n const prompt = str(body, 'prompt')\n if (prompt !== null) next.prompt = normalizePrompt(prompt)\n const urgency = str(body, 'urgency')\n if (urgency !== null) next.urgency = asUrgency(urgency)\n // GUI-only rebind to another project; validated against the workspace registry.\n const workspaceId = str(body, 'workspaceId')\n if (workspaceId !== null) {\n if (workspaces.get(workspaceId) === undefined) throw new Error('Error: not_found: unknown workspace')\n next.workspaceId = workspaceId\n }\n if (typeof body.blocked === 'boolean') next.blocked = body.blocked\n // The GUI (task owner surface) may edit model/execution; null clears the model.\n if (body.execution !== undefined) {\n const previousExecution = next.execution\n next.execution = normalizeExecution(body.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown }, options.now())\n // The client never owns the learned continuation pointer. Keep\n // it when the task remains on the same auto-reuse policy.\n if (next.execution.reuseSessionId === undefined && next.execution.sessionReuseMode === 'reuse'\n && previousExecution.reuseSessionId === undefined && previousExecution.sessionReuseMode !== 'fresh') {\n next.execution.autoReuseSessionId = previousExecution.autoReuseSessionId\n next.execution.autoReuseSessionKey = previousExecution.autoReuseSessionKey\n }\n }\n if (next.execution.reuseSessionId !== undefined && !workspaces.sessionIds?.(next.workspaceId)?.includes(next.execution.reuseSessionId)) {\n throw new Error('Error: invalid_input: selected session is not available in this workspace')\n }\n if (body.model === null) next.model = undefined\n else if (body.model !== undefined) next.model = checkModel(body.model, options.modelProviders)\n // Isolation may change only before the first execution (分支与基线\n // 取决于该选择 — plan §3.1: 执行开始后锁定).\n const isolationRaw = str(body, 'isolation')\n if (isolationRaw !== null) {\n if (task.executions.length > 0 || task.status === 'in_progress') {\n throw new Error('Error: invalid_input: isolation 已锁定(任务已有执行记录),不可修改')\n }\n next.isolation = asIsolation(isolationRaw)\n }\n // Preset may change any time: each run composes fresh.\n if (body.presetId === null) delete next.presetId\n else if (body.presetId !== undefined) next.presetId = normalizePresetId(str(body, 'presetId'))!\n // Permission (0.5.5): 'workspace-write' | 'read-only' | 'danger-full-access'\n if (body.permission === null) delete next.permission\n else if (body.permission !== undefined) next.permission = asPermission(body.permission)\n // Checklist (0.4.0): the GUI replaces the whole list; null clears.\n if (body.checklist === null) delete next.checklist\n else if (body.checklist !== undefined) {\n const items = normalizeChecklist(body.checklist)\n if (items.length > 0) next.checklist = items\n else delete next.checklist\n }\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: summarize(next!) })\n return\n }\n if (action === 'move') {\n const ifVersion = num(body, 'ifVersion')\n const status = str(body, 'status') ?? ''\n const archiveSessions = body.archiveSessions === true\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n const to = asStatus(status)\n let next: TaskRecord | undefined\n let beforeTask: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (!canTransition(task.status, to)) throw new Error(`Error: invalid_transition: illegal transition ${task.status} → ${to}`)\n beforeTask = task\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n if (task.status === 'todo' && to === 'in_progress') next.blocked = false\n // A user move records no holder; leaving in_progress releases any hold.\n syncClaim(next, to, options.now())\n ledger.tasks[index] = next\n return [next]\n })\n const sessionArchive = to === 'archived' && archiveSessions\n ? await archiveTaskSessions(beforeTask ?? next!, workspaces.archiveSession)\n : undefined\n json(res, { ok: true, value: { ...summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) } })\n return\n }\n if (action === 'reject') {\n // Card quick-reject: back to todo + optional user comment in one\n // atomic mutation (a failed move never strands an orphan comment).\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n const commentText = str(body, 'body') ?? ''\n let next: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n if (task.status !== 'in_review') throw new Error(`Error: invalid_transition: reject requires in_review (current ${task.status})`)\n if (!canTransition(task.status, 'todo')) throw new Error(`Error: invalid_transition: illegal transition ${task.status} → todo`)\n next = structuredClone(task)\n next.status = 'todo'\n next.version = task.version + 1\n next.updatedAt = options.now()\n next.updatedBy = { kind: 'user' }\n syncClaim(next, 'todo', options.now())\n if (commentText.trim().length > 0) {\n next.comments.push({ id: newCommentId(), body: normalizeBody(commentText), version: 1, createdAt: options.now() })\n }\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: summarize(next!) })\n return\n }\n if (action === 'comment') {\n const bodyText = str(body, 'body') ?? ''\n const comment = { id: newCommentId(), body: normalizeBody(bodyText), version: 1, createdAt: options.now() }\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (task.status === 'archived') throw new Error('Error: invalid_transition: archived tasks are immutable')\n const next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = options.now()\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: comment }, 201)\n return\n }\n if (action === 'delete') {\n const purge = body.purge === true\n if (purge) {\n if (task.trashedAt === undefined) throw new Error('Error: invalid_input: purge requires a trashed task (soft-delete first)')\n // Worktree safety before purge (plan §3.3, 0.3.1): refuse while\n // uncommitted work remains; otherwise clean the worktree and\n // the task branch along with the ledger entry.\n if (options.git !== undefined) {\n const ws = workspaces.get(task.workspaceId)\n if (ws !== undefined) {\n const path = worktreePathOf(ws.path, id)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: the whole mirror (every repo worktree) goes\n // through the aggregated children-first removal — one\n // dirty repo refuses BEFORE anything is deleted.\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId: id },\n )\n // Leftovers unknown to git ('unregistered' worktrees, or a\n // plain mirror dir when the workspace root is no repo).\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n // Structured classification (review P2): git tags its dirty\n // rejections with a code; the keyword stays as a fallback.\n const dirty = (error as { code?: string }).code === 'dirty-worktree'\n || (error as { code?: string }).code === 'dirty-mirror'\n || message.includes('未提交修改')\n if (dirty) {\n throw new Error(`Error: invalid_input: ${message};请先处理这些改动(提交、续跑或手动保存)再物理清除任务`)\n }\n throw new Error(`Error: invalid_input: ${message}`)\n }\n // Branches die with the task (best effort, per repo).\n const branchTargets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) branchTargets.push({ repo, branch })\n }\n if (task.branch !== undefined && !branchTargets.some(t => t.repo === '')) branchTargets.push({ repo: '', branch: task.branch })\n for (const target of branchTargets) {\n try {\n await options.git.deleteBranch(repoMainPath(ws.path, target.repo), target.branch)\n } catch { /* best effort: the branch may outlive the task */ }\n }\n }\n }\n await store.mutate('task-deleted', ledger => {\n ledger.tasks = ledger.tasks.filter(t => t.id !== id)\n return []\n })\n json(res, { ok: true, value: { purged: true } })\n return\n }\n const ifVersion = num(body, 'ifVersion')\n if (ifVersion === undefined || ifVersion === null) throw new Error('Error: version_conflict: ifVersion required')\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, id)\n if (ifVersion !== task.version) throw new Error(`Error: version_conflict: stale version ${ifVersion} (current ${task.version})`)\n // S5: a running execution keeps writing to the task — refuse the\n // soft-delete until it is cancelled or settled. T8: clear residue.\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new Error('Error: invalid_input: 任务有正在运行的执行,请先取消或等它结束再删除')\n }\n const next = structuredClone(task)\n next.trashedAt = options.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n json(res, { ok: true, value: { trashed: true } })\n return\n }\n if (action === 'run') {\n if (options.run === undefined) {\n const f = fail('invalid_input', 'execution service unavailable')\n json(res, f.res, 501)\n return\n }\n // `reuse: true` = 续跑: keep a live worktree/branch as-is instead\n // of resetting to a fresh baseline (0.3.1).\n const runOptions = body.reuse === true ? { reuseWorktree: true } : undefined\n const result = await options.run(id, runOptions)\n if (result.ok) json(res, { ok: true, value: result }, 202)\n else {\n const f = fail('invalid_input', result.error)\n json(res, f.res, f.status)\n }\n return\n }\n if (action === 'cancel') {\n if (options.cancel === undefined) {\n const f = fail('invalid_input', 'execution service unavailable')\n json(res, f.res, 501)\n return\n }\n const result = await options.cancel(id)\n if (result.ok) json(res, { ok: true, value: { cancelled: true, executionId: result.executionId } }, 202)\n else {\n const f = fail('invalid_input', result.error)\n json(res, f.res, f.status)\n }\n return\n }\n if (action === 'merge') {\n // ⇥ 合并 (detail page, user-only): merge the task branch(es) into\n // the main worktree(s) with --no-ff; conflicts are reported\n // verbatim. 0.6.3: multi-repo mirror tasks merge PER REPO —\n // sequentially, a failed repo never blocking the others.\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n if (task.status === 'in_progress') throw new Error('Error: invalid_input: 任务执行中,不能合并')\n if (task.executions.some(e => e.outcome === 'running')) throw new Error('Error: invalid_input: 任务执行中,不能合并')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n const targets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) targets.push({ repo, branch })\n }\n if (task.branch !== undefined && !targets.some(t => t.repo === '')) targets.unshift({ repo: '', branch: task.branch })\n if (targets.length === 0) throw new Error('Error: invalid_input: 该任务还没有 worktree 分支(未隔离执行过)')\n const multi = task.branches !== undefined\n const results: MergeRepoResult[] = []\n for (const target of targets) {\n if (!isValidRelRepoPath(target.repo)) throw new Error('Error: invalid_input: 非法的仓库路径')\n const repoRoot = repoMainPath(ws.path, target.repo)\n // No-op detection (0.3.1): a branch with no commits over HEAD\n // merges as \"already up to date\" — report that instead of landing\n // a bogus 已合并 comment.\n let noop = false\n try {\n noop = await options.git.isAncestor(repoRoot, target.branch)\n } catch { /* fail-soft: proceed to the real merge */ }\n if (noop) {\n results.push({ repo: target.repo, branch: target.branch, outcome: 'noop' })\n continue\n }\n try {\n // 0.6.3 review fix: parallel repos are self-governing — their\n // content AND their gitlink entries in the ROOT main checkout\n // (e.g. a container repo tracking `dsh-taskboard` as an embedded\n // repo) don't gate the root repo's merge. Child repos merge with\n // no extra exemption (their own .dsh-worktrees rule suffices).\n const exempt = target.repo === ''\n ? await (options.scanner ?? createRepoScanner()).findNestedRepos(ws.path).then(rs => rs.map(r => r.relPath))\n : undefined\n await options.git.merge(repoRoot, target.branch, exempt)\n results.push({ repo: target.repo, branch: target.branch, outcome: 'merged' })\n } catch (error) {\n results.push({\n repo: target.repo,\n branch: target.branch,\n outcome: 'failed',\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n // R1: the git merges above are slow — re-find the FRESH task inside\n // the mutation so a concurrent comment is never overwritten.\n const pushComment = (body: string, system?: { key: string; params?: Record<string, string>; rows?: SystemCommentRow[] }): Promise<void> =>\n store.mutate('comment-added', ledger => {\n const { index, task: fresh } = liveTaskAt(ledger, id)\n const next = structuredClone(fresh)\n next.comments.push({\n id: newCommentId(),\n body: normalizeBody(body),\n ...(system !== undefined ? {\n systemKey: system.key,\n ...(system.params !== undefined ? { systemParams: system.params } : {}),\n ...(system.rows !== undefined ? { systemRows: system.rows } : {}),\n } : {}),\n version: 1,\n createdAt: options.now(),\n })\n next.version = fresh.version + 1\n next.updatedAt = options.now()\n ledger.tasks[index] = next\n return [next]\n }).then(() => undefined)\n if (!multi) {\n // Legacy single-repo shape, byte-identical to 0.3.x–0.6.x.\n const root = results.find(r => r.repo === '')\n if (root === undefined) throw new Error('Error: invalid_input: 该任务还没有 worktree 分支(未隔离执行过)')\n if (root.outcome === 'noop') {\n json(res, { ok: true, value: { merged: false, noop: true, branch: root.branch } })\n return\n }\n if (root.outcome === 'failed') {\n throw new Error(`Error: invalid_input: ${root.error ?? '合并失败'}`)\n }\n await pushComment(`[系统] 分支 ${root.branch} 已合并到主工作区(--no-ff)。`, { key: 'sys.mergeSingle', params: { branch: root.branch } })\n json(res, { ok: true, value: { merged: true, branch: root.branch } })\n return\n }\n const labelOf = (repo: string): string => repo === '' ? '根仓库' : repo\n const mergedCount = results.filter(r => r.outcome === 'merged').length\n const failedCount = results.filter(r => r.outcome === 'failed').length\n const summary = results\n .map(r => r.outcome === 'merged'\n ? `${labelOf(r.repo)} ✓ 已合并`\n : r.outcome === 'noop' ? `${labelOf(r.repo)} ⟲ 无新提交` : `${labelOf(r.repo)} ✗ ${(r.error ?? '合并失败').slice(0, 150)}`)\n .join(';')\n await pushComment(`[系统] 分支已按仓库合并(--no-ff):${summary}`, {\n key: 'sys.mergeMulti',\n rows: results.map(r => ({ repo: r.repo, outcome: r.outcome, ...(r.error !== undefined ? { error: r.error.slice(0, 150) } : {}) })),\n })\n json(res, {\n ok: true,\n value: {\n merged: mergedCount > 0,\n ...(mergedCount === 0 && failedCount === 0 ? { noop: true } : {}),\n results,\n },\n })\n return\n }\n if (action === 'worktree-remove') {\n // 🗑 删除 worktree (detail page): refuses uncommitted changes;\n // optionally deletes the task branch after the worktree is gone.\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n if (task.executions.some(e => e.outcome === 'running')) throw new Error('Error: invalid_input: 任务执行中,不能删除 worktree')\n const ws = workspaces.get(task.workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n const path = worktreePathOf(ws.path, id)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: aggregated children-first mirror removal (see purge).\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId: id },\n )\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n throw new Error(`Error: invalid_input: ${error instanceof Error ? error.message : String(error)}`)\n }\n let branchDeleted = false\n let branchError: string | undefined\n if (body.deleteBranch === true) {\n const branchTargets: Array<{ repo: string; branch: string }> = []\n if (task.branches !== undefined) {\n for (const [repo, branch] of Object.entries(task.branches)) branchTargets.push({ repo, branch })\n }\n if (task.branch !== undefined && !branchTargets.some(t => t.repo === '')) branchTargets.push({ repo: '', branch: task.branch })\n let failures = 0\n for (const target of branchTargets) {\n try {\n await options.git.deleteBranch(repoMainPath(ws.path, target.repo), target.branch)\n } catch (error) {\n failures += 1\n const label = target.repo === '' ? '根仓库' : target.repo\n branchError = `${label}:${error instanceof Error ? error.message : String(error)}`\n }\n }\n branchDeleted = failures === 0\n }\n json(res, { ok: true, value: { removed: true, branchDeleted, ...(branchError !== undefined ? { branchError } : {}) } })\n return\n }\n const f = fail('not_found', `unknown action ${action}`)\n json(res, f.res, f.status)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ----------------------------------- POST /queue/clear (board queue panel)\n if (pathname === `${ROUTE_PREFIX}/queue/clear`) {\n if (options.clearQueue === undefined) {\n const f = fail('invalid_input', 'scheduler queue clear unavailable')\n json(res, f.res, 501)\n return\n }\n try {\n const cleared = await options.clearQueue()\n json(res, { ok: true, value: { cleared } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // -------------------------------------- POST /worktree-cleanup (⚙ 诊断)\n if (pathname === `${ROUTE_PREFIX}/worktree-cleanup`) {\n try {\n if (options.git === undefined) {\n const f = fail('invalid_input', 'git integration unavailable')\n json(res, f.res, 501)\n return\n }\n const workspaceId = str(body, 'workspaceId') ?? ''\n const taskId = str(body, 'taskId') ?? ''\n const ws = workspaces.get(workspaceId)\n if (ws === undefined) throw new Error('Error: not_found: unknown workspace')\n // Only dirs owned by NO ledger task may be cleaned here; live tasks\n // remove their worktree from the detail page.\n if (store.get(taskId) !== undefined) throw new Error('Error: invalid_input: 任务仍在看板中,请从任务详情页删除其 worktree')\n // worktreePathOf throws on an illegal id charset (R4②); the resolved\n // target must additionally stay inside the plugin's own worktree\n // scope — the taskId is fully attacker-controlled body input (R4③).\n const path = worktreePathOf(ws.path, taskId)\n if (!insideWorktreeScope(ws.path, path)) {\n throw new Error('Error: invalid_input: 非法的清除路径(不在任务工作目录范围内)')\n }\n try {\n // 0.6.3: an orphan mirror may hold several repo worktrees —\n // aggregated children-first removal, then the leftover fs rm\n // (scope-verified above).\n await removeMirror(\n { git: options.git, scanner: options.scanner ?? createRepoScanner() },\n { workspacePath: ws.path, taskId },\n )\n await rm(path, { recursive: true, force: true })\n } catch (error) {\n throw new Error(`Error: invalid_input: ${error instanceof Error ? error.message : String(error)}`)\n }\n json(res, { ok: true, value: { cleaned: true, path } })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ---------------------------------------------- POST /import/preview\n // (0.4.0) Dry-run: classify every task in the uploaded ledger file\n // against the live one; nothing is written.\n if (pathname === `${ROUTE_PREFIX}/import/preview`) {\n try {\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n const plan = validateLedgerImport(body, known, options.now())\n json(res, {\n ok: true,\n value: {\n plan: {\n create: plan.create.map(t => ({ id: t.id, title: t.title, status: t.status })),\n overwrite: plan.overwrite.map(t => ({ id: t.id, title: t.title, status: t.status })),\n invalid: plan.invalid,\n },\n },\n })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------------------ POST /import\n // (0.4.0) Commit an import. mode=merge upserts (create + overwrite by\n // id); mode=replace swaps the WHOLE ledger (invalid entries dropped)\n // after writing a timestamped backup of the current one.\n if (pathname === `${ROUTE_PREFIX}/import`) {\n try {\n const mode = str(body, 'mode') === 'replace' ? 'replace' as const : 'merge' as const\n const raw = body.ledger\n const known = new Set(store.snapshot().tasks.map(t => t.id))\n const plan = validateLedgerImport(raw, known, options.now())\n const imported = [...plan.create, ...plan.overwrite]\n if (mode === 'replace' && imported.length === 0) {\n throw new Error('Error: invalid_input: 导入文件没有可导入的任务,已拒绝整册替换')\n }\n let backupFile: string | undefined\n if (mode === 'replace' && store.snapshot().tasks.length > 0) {\n backupFile = await store.backup()\n }\n let replacedTotal: number | undefined\n await store.mutate('ledger-replaced', ledger => {\n if (mode === 'replace') {\n // S6: a whole-ledger swap must not strand live executions — the\n // running sessions keep working with no task to settle into.\n if (ledger.tasks.some(t => t.executions.some(e => e.outcome === 'running'))) {\n throw new Error('Error: invalid_input: 有任务正在执行,不能整册替换(请先取消或等待结束)')\n }\n replacedTotal = ledger.tasks.length\n ledger.tasks = structuredClone(imported)\n // Replace is a whole-ledger swap (0.5.0): board settings ride\n // along when the file carries them; merge keeps the live ones.\n if (plan.settings !== undefined) ledger.settings = structuredClone(plan.settings)\n else delete ledger.settings\n return ledger.tasks\n }\n const byId = new Map(ledger.tasks.map(t => [t.id, t]))\n for (const task of imported) {\n // S6: merging over a task whose execution is live would orphan\n // that run the same way — refuse the overwrite.\n const existing = byId.get(task.id)\n if (existing !== undefined && existing.executions.some(e => e.outcome === 'running')) {\n throw new Error(`Error: invalid_input: 任务 ${task.id} 正在执行,不能被导入覆盖`)\n }\n byId.set(task.id, structuredClone(task))\n }\n ledger.tasks = [...byId.values()]\n return structuredClone(imported)\n })\n json(res, {\n ok: true,\n value: {\n mode,\n created: plan.create.length,\n overwritten: plan.overwrite.length,\n ...(mode === 'replace' ? { replacedTotal } : {}),\n ...(backupFile !== undefined ? { backupFile } : {}),\n },\n })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------- POST /templates (+delete)\n if (pathname === `${ROUTE_PREFIX}/templates` || pathname === `${ROUTE_PREFIX}/templates/delete`) {\n try {\n if (options.templates === undefined) {\n const f = fail('invalid_input', 'template store unavailable')\n json(res, f.res, 501)\n return\n }\n if (pathname.endsWith('/delete')) {\n const id = str(body, 'id') ?? ''\n if (id.length === 0) throw new Error('Error: invalid_input: id required')\n const deleted = await options.templates.remove(id)\n json(res, { ok: true, value: { deleted } })\n return\n }\n const name = str(body, 'name') ?? ''\n if (name.trim().length === 0) throw new Error('Error: invalid_input: name required')\n const template = await options.templates.upsert({\n id: str(body, 'id') ?? undefined,\n name,\n task: normalizeTemplateSpec(body.task, options.now()),\n })\n json(res, { ok: true, value: template }, 201)\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n // ------------------------------------------ POST /settings/update\n // (0.5.0) Whole-object replace semantics: omitted fields fall back to\n // their factory defaults. Affects only tasks created AFTER the change.\n if (pathname === `${ROUTE_PREFIX}/settings/update`) {\n try {\n const next = asBoardSettings(body)\n await store.mutate('settings-updated', ledger => {\n ledger.settings = next\n return []\n })\n json(res, { ok: true, value: next })\n } catch (error) {\n const f = toFail(error)\n json(res, f.res, f.status)\n }\n return\n }\n\n res.writeHead(404)\n res.end()\n } catch (error) {\n const f = fail('internal', error instanceof Error ? error.message : String(error))\n json(res, f.res, f.status)\n }\n }\n\n const sse = (req: IncomingMessage, res: ServerResponse): void => {\n res.writeHead(200, {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n })\n res.write('retry: 2000\\n\\n')\n // Baseline frame: the client reconciles by revision and refetches state on gaps.\n res.write(`event: hello\\ndata: ${JSON.stringify({ revision: store.snapshot().revision })}\\n\\n`)\n subscribers.add(res)\n // S2: a socket that dies between 'close' detection and the next write\n // would emit 'error' on the response — unhandled, that escalates to an\n // uncaughtException. Drop the subscriber instead.\n res.on('error', () => { subscribers.delete(res) })\n if (heartbeat === undefined) {\n heartbeat = setInterval(() => {\n for (const current of subscribers) current.write(': ping\\n\\n')\n }, HEARTBEAT_MS)\n }\n req.on('close', () => {\n subscribers.delete(res)\n if (subscribers.size === 0 && heartbeat !== undefined) {\n clearInterval(heartbeat)\n heartbeat = undefined\n }\n })\n }\n\n const disposers = [\n ctx.webServer.register({ kind: 'prefix', path: ROUTE_PREFIX, handler }),\n ctx.webServer.register({ kind: 'exact', path: SSE_PATH, handler: sse }),\n ]\n return () => {\n unsubscribeBroadcast()\n for (const dispose of disposers) dispose()\n if (heartbeat !== undefined) clearInterval(heartbeat)\n for (const res of subscribers) res.end()\n subscribers.clear()\n }\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/lib/host/tools.js b/lib/host/tools.js index 212cd3c..258372f 100644 --- a/lib/host/tools.js +++ b/lib/host/tools.js @@ -328,7 +328,7 @@ function registerTaskboardTools(ctx, deps) { model: { type: "object", additionalProperties: false, - description: "Pin executions to one configured model: { provider, model, reasoningEffort? }. Omit to use the default model.", + description: "Pin executions to one configured model: { provider, model, reasoningEffort? }. Omitted → board default model and effort, then deployment default.", properties: { provider: { type: "string", @@ -354,7 +354,7 @@ function registerTaskboardTools(ctx, deps) { }, presetId: { type: "string", - description: "Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional." + description: "Agent preset for execution sessions (tool set / persona). Omitted → board default, then deployment default; empty string follows deployment default." }, checklist: { type: "array", @@ -381,10 +381,11 @@ function registerTaskboardTools(ctx, deps) { const status = args.status === void 0 ? "todo" : asStatus(args.status); if (status !== "backlog" && status !== "todo") throw new ToolError(ERR.invalidTransition, "a new task must start as backlog or todo (in_progress requires claiming the task)"); const execution = normalizeExecution(args.execution ?? {}, deps.now()); - const model = args.model !== void 0 ? checkModel(deps, args.model) : void 0; + const modelRaw = args.model ?? store.snapshot().settings?.defaultModel; + const model = modelRaw !== void 0 ? checkModel(deps, modelRaw) : void 0; const isolation = args.isolation === void 0 ? defaultIsolationOf(store.snapshot().settings) : asIsolation(args.isolation); const permission = args.permission === void 0 ? defaultPermissionOf(store.snapshot().settings) : asPermission(args.permission); - const presetId = args.presetId?.trim() || void 0; + const presetId = args.presetId === void 0 ? store.snapshot().settings?.defaultPresetId : args.presetId.trim() || void 0; const checklistTexts = args.checklist?.map((c) => c.trim()).filter((c) => c.length > 0); const checklist = checklistTexts !== void 0 && checklistTexts.length > 0 ? checklistFromTexts(checklistTexts) : void 0; const now = deps.now(); diff --git a/lib/host/tools.js.map b/lib/host/tools.js.map index ea24ca4..ab86d7f 100644 --- a/lib/host/tools.js.map +++ b/lib/host/tools.js.map @@ -1 +1 @@ -{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["import type { SessionArchiveResult } from '../shared/api.ts'\nimport { archiveTaskSessions } from './archive-sessions.ts'\n/**\n * The ten `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n * - checklist items may be added/checked by agents, but checking never\n * completes the task (done stays user-only)\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n MAX_CHECKLIST_ITEMS,\n asIsolation,\n asPermission,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n defaultPermissionOf,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newChecklistItemId,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeExecutionReport,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type ChecklistItem,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n checklist?: { done: number; total: number }\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.checklist !== undefined && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` 定期 cron=${t.execution.cron}` : ''}${t.execution.runAt !== undefined ? ` 定时(一次) runAt=${new Date(t.execution.runAt).toISOString()}` : ''}`,\n `隔离: ${t.isolation === 'none' ? '关闭(原目录执行)' : 'Git Worktree'}${t.branch !== undefined ? `(分支 ${t.branch})` : ''}${t.branches !== undefined ? `(多仓库镜像 ${Object.keys(t.branches).length + (t.branch !== undefined ? 1 : 0)} 个仓库)` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}${t.model.reasoningEffort !== undefined ? ` (思考强度: ${t.model.reasoningEffort})` : ''}`)\n if (t.presetId !== undefined) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.checklist !== undefined && t.checklist.length > 0) {\n const done = t.checklist.filter(i => i.checked).length\n lines.push(`验收清单 (${done}/${t.checklist.length}):`)\n for (const [index, item] of t.checklist.entries()) {\n const mark = item.checked ? '☑' : '☐'\n const who = item.checkedBy === undefined ? '' : item.checkedBy === 'user' ? ' ·用户勾选' : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`\n const note = item.note !== undefined ? ` ·证据: ${item.note}` : ''\n // Carry the checklist item id so `taskboard_checklist check/uncheck` can\n // address it without guessing — a terse render starves the agent (render\n // is fed to the model as result.content). Mirrors the index+id carried\n // by the taskboard_checklist tool output.\n lines.push(` ${mark} [${index + 1}] ${item.text}${who}${note} id=${item.id}`)\n }\n }\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n const report = e.report !== undefined ? ' [已交报告]' : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : t.updatedBy.kind === 'system' ? 'system' : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notReady: 'taskboard_not_ready',\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. The code\n * is also carried structurally so the routes layer can map failures without\n * re-parsing messages (review P2). */\nexport class ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n /** Unarchived sessions owned by this project, in registry order. */\n sessionIds?(workspaceId: string): string[] | undefined\n /** Archive one session durably (when supported by runtime workspaceRegistry). */\n archiveSession?(sessionId: string): Promise<void>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n sessionIds: id => {\n const ws = registry.get(id as never)\n if (ws === undefined) return undefined\n const archived = new Set<string>(registry.archivedSessionIds)\n return ws.sessionIds.filter(sessionId => !archived.has(sessionId))\n },\n ...(typeof registry.archiveSession === 'function'\n ? { archiveSession: (sessionId: string) => registry.archiveSession(sessionId as Parameters<WorkspaceRegistry['archiveSession']>[0]) }\n : {}),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /** Shared startup barrier; tool definitions stay registered while services initialize. */\n ready?: () => Promise<void>\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/**\n * Find a live (non-trashed) task INSIDE a mutator (R1: every guard must run\n * on the fresh draft the serial queue hands us, never on a pre-read clone —\n * a pre-read can pass its version check and then blind-overwrite a task that\n * changed while the caller awaited). Throws not_found for missing/trashed.\n */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0) throw new ToolError(ERR.notFound, `no task ${id}`)\n const task = ledger.tasks[index]!\n if (task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return { index, task }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all ten tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n await deps.ready?.()\n if (process.env.ATB_TRACE === '1') console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n if (process.env.ATB_TRACE === '1') console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n if (process.env.ATB_TRACE === '1') console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim | scheduled+cron 定期重复 | scheduled+runAt 定时一次), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Extra execution instructions; the session receives title+description+this prompt.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) | { mode: \"scheduled\", cron } periodic (定期, repeats) | { mode: \"scheduled\", runAt } one-shot (定时, fires once).',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled periodic only).' },\n runAt: { type: 'string', description: 'One-shot trigger time: epoch ms or ISO string (scheduled one-shot only; must be in the future).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omit to use the default model.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n reasoningEffort: { type: 'string', description: 'Optional thinking intensity / reasoning effort (e.g. low, medium, high).' },\n },\n },\n isolation: {\n type: 'string',\n description: 'Code isolation for executions: \"worktree\" (each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction). Omitted → the board default (看板设置 → 默认执行隔离; factory default \"none\").',\n },\n permission: {\n type: 'string',\n description: 'Execution permission: \"read-only\", \"workspace-write\", or \"danger-full-access\". Omitted → the board default (看板设置 → 默认权限; factory default \"workspace-write\").',\n },\n presetId: {\n type: 'string',\n description: 'Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional.',\n },\n checklist: {\n type: 'array',\n description: `Acceptance checklist (DoD) item texts (≤${MAX_CHECKLIST_ITEMS} × 200 chars); agents check them off at handoff, the user reviews.`,\n items: { type: 'string' },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number }\n model?: { provider?: string; model?: string }\n isolation?: string\n permission?: string\n presetId?: string\n checklist?: string[]\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status !== 'backlog' && status !== 'todo') {\n throw new ToolError(ERR.invalidTransition, 'a new task must start as backlog or todo (in_progress requires claiming the task)')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const model = args.model !== undefined ? checkModel(deps, args.model) : undefined\n // 0.5.0: an omitted isolation is MATERIALIZED from the board setting\n // (看板设置) at creation, so later setting changes never rewrite\n // existing tasks.\n const isolation = args.isolation === undefined ? defaultIsolationOf(store.snapshot().settings) : asIsolation(args.isolation)\n // Match the GUI create route: freeze the current board default onto\n // the task so later settings changes do not silently alter a schedule.\n const permission = args.permission === undefined ? defaultPermissionOf(store.snapshot().settings) : asPermission(args.permission)\n const presetId = args.presetId?.trim() || undefined\n // T9: match the GUI create route — trim and drop blank lines instead\n // of failing the whole call over one empty string.\n const checklistTexts = args.checklist?.map(c => c.trim()).filter(c => c.length > 0)\n const checklist = checklistTexts !== undefined && checklistTexts.length > 0 ? checklistFromTexts(checklistTexts) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n permission,\n ...(presetId !== undefined ? { presetId } : {}),\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked/permission. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n permission: { type: 'string', description: 'Execution permission: read-only | workspace-write | danger-full-access.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n permission?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: lookup + version guard + write run inside the serial-queue\n // mutation, on the fresh draft — a pre-read clone could pass its\n // version check and then blind-overwrite a concurrent writer.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n if (args.permission !== undefined) next.permission = asPermission(args.permission)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n return json({ task: summarize(next!) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n archiveSessions: { type: 'boolean', description: 'When moving to archived: whether to archive associated execution sessions as well. Defaults to false.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。${v.sessionArchive === undefined ? '' : ` 会话归档结果:${JSON.stringify(v.sessionArchive)}`}` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number; archiveSessions?: boolean }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n // Claim boundary (policy gate): resolving the caller's workspace is\n // async, so it happens BEFORE the mutation — the comparison runs INSIDE\n // against the FRESH task (stronger than the old stale-clone compare).\n const callerWsId = to === 'in_progress'\n ? await callerWorkspace(deps, exec as ToolRunContext)\n : undefined\n // R1: every state guard + the write itself run inside the mutation.\n let next: TaskRecord | undefined\n let beforeTask: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n beforeTask = task\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to) && callerWsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n ledger.tasks[index] = next\n return [next]\n })\n const sessionArchive = to === 'archived' && args.archiveSessions === true\n ? await archiveTaskSessions(beforeTask ?? next!, deps.workspaces.archiveSession)\n : undefined\n return json({ task: summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n // R1: find + append inside the mutation (no ifVersion by design —\n // comments are append-only — but the write must not clobber a task\n // that changed while we were queued).\n let next: TaskRecord | undefined\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n ledger.tasks[index] = next\n return [next]\n })\n return json({ comment, task: { id: next!.id, version: next!.version, status: next!.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n // R1: guards inside the mutation. S5: a running execution keeps\n // writing to the task (report, settlement) — refuse the soft-delete\n // until it is cancelled or settled. T8: clear claim residue.\n let next: TaskRecord | undefined\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new ToolError(ERR.invalidInput, '任务有正在运行的执行(先在 GUI 取消或等它结束再删除)')\n }\n next = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- checklist\n disposers.push(register(defineTool({\n name: 'taskboard_checklist',\n description:\n `Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), `\n + '\"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). '\n + 'Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n action: { type: 'string', required: true, description: 'add | check | uncheck.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n items: {\n type: 'array',\n description: 'Item texts to append (action=add only; 1..10 per call, 200 chars each).',\n items: { type: 'string' },\n },\n itemId: { type: 'string', description: 'The checklist item id (action=check/uncheck).' },\n note: { type: 'string', description: 'Evidence note recorded with the check (≤400 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; version?: number }; checklist?: Array<Record<string, unknown>>; done?: number; total?: number }\n if (v.task === undefined || v.checklist === undefined) return [{ type: 'text', text: '清单操作失败。' }]\n const lines = v.checklist.map((i, index) => `${i.checked === true ? '☑' : '☐'} [${index + 1}] ${String(i.text)}${i.note !== undefined ? `(证据: ${String(i.note)})` : ''} id=${String(i.id)}`)\n return [{\n type: 'text',\n text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\\n${lines.join('\\n')}`,\n }]\n },\n },\n async execute(args: {\n id: string\n action: string\n ifVersion: number\n items?: string[]\n itemId?: string\n note?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: guards + the checklist edit itself run inside the mutation.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n const checklist: ChecklistItem[] = next.checklist === undefined ? [] : [...next.checklist]\n\n if (args.action === 'add') {\n const texts = args.items ?? []\n if (texts.length === 0 || texts.length > 10) {\n throw new ToolError(ERR.invalidInput, 'items must carry 1..10 texts per add call')\n }\n if (checklist.length + texts.length > MAX_CHECKLIST_ITEMS) {\n throw new ToolError(ERR.invalidInput, `checklist may hold at most ${MAX_CHECKLIST_ITEMS} items (currently ${checklist.length})`)\n }\n checklist.push(...checklistFromTexts(texts))\n } else if (args.action === 'check') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for check')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n const note = args.note !== undefined && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : undefined\n item.checked = true\n item.checkedBy = actor.sessionId\n item.checkedAt = deps.now()\n if (note !== undefined) item.note = note\n } else if (args.action === 'uncheck') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for uncheck')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n item.checked = false\n delete item.checkedBy\n delete item.checkedAt\n delete item.note\n } else {\n throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got \"${args.action}\")`)\n }\n\n if (checklist.length > 0) next.checklist = checklist\n else delete next.checklist\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n const progress = next!.checklist !== undefined\n ? { done: next!.checklist.filter(i => i.checked).length, total: next!.checklist.length }\n : { done: 0, total: 0 }\n return json({ task: { id: next!.id, version: next!.version }, checklist: next!.checklist ?? [], ...progress })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------- execution report\n disposers.push(register(defineTool({\n name: 'taskboard_execution_report',\n description:\n 'Submit the structured execution report for the task you are currently executing (summary / changed '\n + 'files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; '\n + 'a later submission overwrites the previous report. If your run already settled, you may back-submit '\n + 'onto your latest succeeded execution while you still hold the task. Commits and diffs are host-collected.',\n parameters: {\n summary: { type: 'string', required: true, description: 'What was done (1..2000 chars).' },\n changedFiles: {\n type: 'array',\n description: 'Files you changed (paths, ≤50 × 300 chars).',\n items: { type: 'string' },\n },\n checks: {\n type: 'array',\n description: 'How the work was verified (e.g. test commands + outcomes, ≤50 entries).',\n items: { type: 'string' },\n },\n artifacts: {\n type: 'array',\n description: 'Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).',\n items: { type: 'string' },\n },\n risk: { type: 'string', description: 'Known remaining risks or follow-ups (≤2000 chars, optional).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { taskId?: string; executionId?: string; report?: { summary?: string } }\n if (v.taskId === undefined || v.report === undefined) return [{ type: 'text', text: '报告提交失败。' }]\n return [{\n type: 'text',\n text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ''}\\n`\n + '接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。',\n }]\n },\n },\n async execute(args: {\n summary: string\n changedFiles?: string[]\n checks?: string[]\n artifacts?: string[]\n risk?: string\n }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const report = normalizeExecutionReport(args)\n // Path 1 (unchanged): attach to the RUNNING execution this session\n // owns — reports ride the live run, so the agent never needs ids.\n let taskId: string | undefined\n let executionId: string | undefined\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n const execution = task.executions.find(e => e.sessionId === sessionId && e.outcome === 'running')\n if (execution !== undefined) {\n execution.report = report\n taskId = task.id\n executionId = execution.id\n return [task]\n }\n }\n return undefined\n })\n // Path 2 (review follow-up, P2): back-submit onto a session-owned\n // SETTLED execution — the main conversation claims tasks directly and\n // has no live run. Allowed when the session holds the task or owns\n // its latest successful execution; never touches anyone else's runs.\n if (taskId === undefined || executionId === undefined) {\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n if (task.trashedAt !== undefined || task.status === 'archived') continue\n const last = task.executions[task.executions.length - 1]\n const owned = last !== undefined && last.sessionId === sessionId && last.outcome === 'succeeded'\n const holds = task.claimedBy === sessionId && last !== undefined && last.sessionId === sessionId\n if (!owned && !holds) continue\n last!.report = report\n taskId = task.id\n executionId = last!.id\n return [task]\n }\n return undefined\n })\n }\n if (taskId === undefined || executionId === undefined) {\n throw new ToolError(ERR.forbidden, 'no running execution and no settled execution of yours to report on — reports attach to your running execution, or back-submit onto your latest succeeded one while you hold the task')\n }\n return json({ taskId, executionId, report })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}\n"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tools.js","names":["v"],"sources":["../../src/host/tools.ts"],"sourcesContent":["import type { SessionArchiveResult } from '../shared/api.ts'\nimport { archiveTaskSessions } from './archive-sessions.ts'\n/**\n * The ten `taskboard_*` agent tools. All writes require a calling agent\n * session (ownership audit), carry optimistic-version checks, and enforce\n * the protocol gates in CODE, not in prompt text:\n *\n * - `move → done` is rejected for agent callers (user confirmation only)\n * - `move todo → in_progress` requires the calling session's workspace to\n * match the task's project (claim boundary)\n * - taking over a task held by another session is rejected\n * - `delete` for agent callers only sets the soft-delete marker\n * - checklist items may be added/checked by agents, but checking never\n * completes the task (done stays user-only)\n *\n * OUTPUT CONTRACT (lesson: registry `createSuccessResult` renders\n * `output.render(args, value)` into `result.content`, and the loop feeds\n * exactly that content to the model — the raw JSON `value` never reaches\n * the model): render() IS the model-facing tool result. Every render must\n * carry the complete facts an agent needs to act (ids, versions, statuses);\n * a \"terse UI summary\" here starves the agent.\n *\n * @module dsh-taskboard/host/tools\n */\nimport type { WorkspaceRegistry } from '@deepseek-ai/dsh-workspace'\nimport { defineTool } from './sdk.ts'\nimport {\n MAX_CHECKLIST_ITEMS,\n asIsolation,\n asPermission,\n asStatus,\n asUrgency,\n canTransition,\n checklistFromTexts,\n defaultIsolationOf,\n defaultPermissionOf,\n effectivePrompt,\n isClaim,\n isClaimedBy,\n newChecklistItemId,\n newCommentId,\n newTaskId,\n normalizeBody,\n normalizeExecution,\n normalizeExecutionReport,\n normalizeModel,\n normalizePrompt,\n normalizeTitle,\n summarize,\n syncClaim,\n type Actor,\n type ChecklistItem,\n type TaskLedger,\n type TaskModel,\n type TaskRecord,\n} from '../shared/protocol.ts'\nimport type { TaskStore } from './store.ts'\n\n/** Render side: one compact task line (id/status/version are load-bearing). */\nfunction taskLine(t: {\n id: string\n title: string\n status: string\n urgency: string\n version: number\n workspaceId: string\n blocked: boolean\n executionMode: string\n commentCount?: number\n lastExecutionOutcome?: string\n checklist?: { done: number; total: number }\n trashed?: boolean\n}): string {\n const parts = [\n `- ${t.id} [${t.status}] v${t.version} · ${t.urgency} · 项目 ${t.workspaceId}`,\n `「${t.title}」`,\n ]\n if (t.blocked) parts.push('·受阻')\n if (t.executionMode === 'scheduled') parts.push('·定时')\n if (t.commentCount !== undefined && t.commentCount > 0) parts.push(`·评论${t.commentCount}`)\n if (t.checklist !== undefined && t.checklist.total > 0) parts.push(`·清单${t.checklist.done}/${t.checklist.total}`)\n if (t.lastExecutionOutcome !== undefined) parts.push(`·上次执行${t.lastExecutionOutcome}`)\n if (t.trashed === true) parts.push('·已删')\n return parts.join(' ')\n}\n\n/** Render side: the full task detail block (everything an executor needs). */\nfunction taskDetail(t: TaskRecord & { effectivePrompt?: string }): string {\n const lines: string[] = [\n `任务 ${t.id} 「${t.title}」`,\n `状态: ${t.status} (v${t.version}) · 紧急度: ${t.urgency} · 项目: ${t.workspaceId}${t.blocked ? ' · 受阻' : ''}`,\n `执行方式: ${t.execution.mode}${t.execution.cron !== undefined ? ` 定期 cron=${t.execution.cron}` : ''}${t.execution.runAt !== undefined ? ` 定时(一次) runAt=${new Date(t.execution.runAt).toISOString()}` : ''}`,\n `隔离: ${t.isolation === 'none' ? '关闭(原目录执行)' : 'Git Worktree'}${t.branch !== undefined ? `(分支 ${t.branch})` : ''}${t.branches !== undefined ? `(多仓库镜像 ${Object.keys(t.branches).length + (t.branch !== undefined ? 1 : 0)} 个仓库)` : ''}`,\n ]\n const holder = isClaimedBy(t)\n if (holder !== undefined) lines.push(`认领: agent ${String(holder).slice(0, 24)}(持有期间其他会话不可移动)`)\n if (t.execution.nextRunAt !== undefined) lines.push(`下次触发: ${new Date(t.execution.nextRunAt).toISOString()}`)\n if (t.model !== undefined) lines.push(`固定模型: ${t.model.provider}/${t.model.model}${t.model.reasoningEffort !== undefined ? ` (思考强度: ${t.model.reasoningEffort})` : ''}`)\n if (t.presetId !== undefined) lines.push(`执行模式: ${t.presetId}(未指定时为部署默认 preset)`)\n lines.push(`描述: ${t.description.length > 0 ? t.description : '(无)'}`)\n lines.push(`执行 Prompt: ${t.effectivePrompt ?? effectivePrompt(t)}`)\n if (t.checklist !== undefined && t.checklist.length > 0) {\n const done = t.checklist.filter(i => i.checked).length\n lines.push(`验收清单 (${done}/${t.checklist.length}):`)\n for (const [index, item] of t.checklist.entries()) {\n const mark = item.checked ? '☑' : '☐'\n const who = item.checkedBy === undefined ? '' : item.checkedBy === 'user' ? ' ·用户勾选' : ` ·agent ${String(item.checkedBy).slice(0, 24)}勾选`\n const note = item.note !== undefined ? ` ·证据: ${item.note}` : ''\n // Carry the checklist item id so `taskboard_checklist check/uncheck` can\n // address it without guessing — a terse render starves the agent (render\n // is fed to the model as result.content). Mirrors the index+id carried\n // by the taskboard_checklist tool output.\n lines.push(` ${mark} [${index + 1}] ${item.text}${who}${note} id=${item.id}`)\n }\n }\n if (t.comments.length > 0) {\n lines.push(`评论 (${t.comments.length}):`)\n for (const c of t.comments) {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n lines.push(` - [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`)\n }\n } else {\n lines.push('评论: 无')\n }\n if (t.executions.length > 0) {\n lines.push(`执行记录 (${t.executions.length}):`)\n for (const e of t.executions) {\n const at = e.startedAt !== undefined ? new Date(e.startedAt).toISOString() : '?'\n const err = e.error !== undefined ? ` 错误: ${e.error}` : ''\n const report = e.report !== undefined ? ' [已交报告]' : ''\n lines.push(` - [${e.trigger} ${at}] ${e.outcome}${report}${err}`)\n }\n } else {\n lines.push('执行记录: 无')\n }\n const updatedBy = t.updatedBy.kind === 'agent' ? `agent ${String(t.updatedBy.sessionId).slice(0, 24)}` : t.updatedBy.kind === 'system' ? 'system' : 'user'\n lines.push(`更新: ${new Date(t.updatedAt).toISOString()} 由 ${updatedBy}`)\n return lines.join('\\n')\n}\n\n/** Stable error codes surfaced at the head of tool error messages. */\nexport const ERR = {\n notReady: 'taskboard_not_ready',\n notFound: 'not_found',\n versionConflict: 'version_conflict',\n workspaceMismatch: 'workspace_mismatch',\n invalidTransition: 'invalid_transition',\n forbidden: 'forbidden',\n requiresAgent: 'unauthorized_actor',\n invalidInput: 'invalid_input',\n} as const\n\n/** Tool failure: an Error whose message starts with a stable code. The code\n * is also carried structurally so the routes layer can map failures without\n * re-parsing messages (review P2). */\nexport class ToolError extends Error {\n constructor(readonly code: string, detail: string) {\n super(`Error: ${code}: ${detail}`)\n }\n}\n\n/** The workspace face the tools need (narrow for tests). */\nexport interface WorkspaceFace {\n /** Resolve the workspace owning a canonical cwd, if any. */\n resolveByPath(path: string): Promise<{ id: string } | undefined>\n /** Get a workspace by id. */\n get(id: string): { id: string; path: string; title: string } | undefined\n /** List all workspaces. */\n list(): Array<{ id: string; path: string; title: string }>\n /** Unarchived sessions owned by this project, in registry order. */\n sessionIds?(workspaceId: string): string[] | undefined\n /** Archive one session durably (when supported by runtime workspaceRegistry). */\n archiveSession?(sessionId: string): Promise<void>\n}\n\n/** Adapt the real registry to the narrow face. */\nexport function workspaceFace(registry: WorkspaceRegistry): WorkspaceFace {\n // Explicit field mapping: Workspace entities expose path/title as prototype\n // getters, which JSON.stringify skips (own enumerable properties only).\n return {\n resolveByPath: async (path) => {\n const ws = await registry.resolveByPath(path as never)\n return ws === undefined ? undefined : { id: ws.id }\n },\n get: id => {\n const ws = registry.get(id as never)\n return ws === undefined ? undefined : { id: ws.id, path: ws.path, title: ws.title }\n },\n list: () => registry.list().map(ws => ({ id: ws.id, path: ws.path, title: ws.title })),\n sessionIds: id => {\n const ws = registry.get(id as never)\n if (ws === undefined) return undefined\n const archived = new Set<string>(registry.archivedSessionIds)\n return ws.sessionIds.filter(sessionId => !archived.has(sessionId))\n },\n ...(typeof registry.archiveSession === 'function'\n ? { archiveSession: (sessionId: string) => registry.archiveSession(sessionId as Parameters<WorkspaceRegistry['archiveSession']>[0]) }\n : {}),\n }\n}\n\n/** Everything the tool set needs. */\nexport interface ToolDeps {\n store: TaskStore\n workspaces: WorkspaceFace\n /** Current epoch ms (injectable for tests). */\n now: () => number\n /** Shared startup barrier; tool definitions stay registered while services initialize. */\n ready?: () => Promise<void>\n /**\n * Registered model provider routes (from the host llm runtime), for\n * advisory validation of pinned models; undefined = runtime unavailable,\n * in which case only the structural check applies.\n */\n modelProviders?: () => string[] | undefined\n}\n\n/** Validate a pinned model: structural check always, provider route when known. */\nfunction checkModel(deps: ToolDeps, raw: unknown): TaskModel {\n const model = normalizeModel(raw)\n const providers = deps.modelProviders?.()\n if (providers !== undefined && !providers.includes(model.provider)) {\n throw new ToolError(ERR.invalidInput, `model provider \"${model.provider}\" has no registered route (available: ${providers.join(', ')})`)\n }\n return model\n}\n\n/** Resolve the calling agent's actor and session id. */\nfunction caller(exec: ToolRunContext): { actor: Actor & { kind: 'agent' }; sessionId: string } {\n if (!exec.agent) throw new ToolError(ERR.requiresAgent, 'taskboard tools require a calling agent session')\n const sessionId = exec.agent.id\n return { actor: { kind: 'agent', sessionId }, sessionId }\n}\n\n/** The calling session's workspace id (undefined when unaffiliated). */\nasync function callerWorkspace(deps: ToolDeps, exec: ToolRunContext): Promise<string | undefined> {\n const cwd = exec.agent?.session.header.cwd\n if (typeof cwd !== 'string' || cwd.length === 0) return undefined\n const ws = await deps.workspaces.resolveByPath(cwd)\n return ws?.id\n}\n\n/** Guard: version match. */\nfunction versionGuard(task: TaskRecord, ifVersion: number | undefined): void {\n if (ifVersion === undefined) {\n throw new ToolError(ERR.versionConflict, 'this write requires ifVersion; read the task first')\n }\n if (ifVersion !== task.version) {\n throw new ToolError(ERR.versionConflict, `stale version ${ifVersion} (current ${task.version}); re-read the task and retry once`)\n }\n}\n\n/**\n * Find a live (non-trashed) task INSIDE a mutator (R1: every guard must run\n * on the fresh draft the serial queue hands us, never on a pre-read clone —\n * a pre-read can pass its version check and then blind-overwrite a task that\n * changed while the caller awaited). Throws not_found for missing/trashed.\n */\nfunction liveTaskAt(ledger: TaskLedger, id: string): { index: number; task: TaskRecord } {\n const index = ledger.tasks.findIndex(t => t.id === id)\n if (index < 0) throw new ToolError(ERR.notFound, `no task ${id}`)\n const task = ledger.tasks[index]!\n if (task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return { index, task }\n}\n\n/** Re-throw with a stable code; non-ToolErrors become invalid_input. */\nfunction fail(error: unknown): never {\n if (error instanceof ToolError) throw error\n const message = error instanceof Error ? error.message : String(error)\n throw new ToolError(ERR.invalidInput, message)\n}\n\n/** Loose json output schema shared by every taskboard tool. */\nconst JSON_OUT = { type: 'json' } as const\n\n/** Deep-JSON a value for a json-rooted tool output (spread results lose implicit index signatures). */\nfunction json<T>(value: T): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n}\n\n/** The exec context face the tools read (agent identity + session cwd). */\nexport interface ToolRunContext {\n agent?: { id: string; session: { header: { cwd?: string } } }\n}\n\n/** Registry-like context face (tests stub this). */\nexport interface ToolContextFace {\n tools: { register(tool: { name: string }): unknown }\n}\n\n/**\n * Register all ten tools.\n * @param ctx - a context exposing `tools.register`.\n * @param deps - store + workspaces + clock.\n * @returns dispose functions, one per tool.\n */\nexport function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Array<() => void> {\n const disposers: Array<() => void> = []\n const { store, workspaces } = deps\n\n // Env-gated tool-call tracing (ATB_TRACE=1) — evidence for protocol E2E.\n const register = (tool: { name: string; execute?: unknown }) => {\n if (typeof tool.execute === 'function') {\n const orig = tool.execute as (args: unknown, exec: unknown) => Promise<unknown>\n tool.execute = async (args: unknown, exec: unknown) => {\n await deps.ready?.()\n if (process.env.ATB_TRACE === '1') console.error(`[atb ▶] ${tool.name}`, JSON.stringify(args).slice(0, 300))\n try {\n const result = await orig(args, exec)\n if (process.env.ATB_TRACE === '1') console.error(`[atb ✓] ${tool.name}`, JSON.stringify(result).slice(0, 300))\n return result\n } catch (error) {\n if (process.env.ATB_TRACE === '1') console.error(`[atb ✗] ${tool.name}`, String(error).slice(0, 400))\n throw error\n }\n }\n }\n return ctx.tools.register(tool as { name: string })\n }\n\n // ------------------------------------------------------------------ list\n disposers.push(register(defineTool({\n name: 'taskboard_list',\n description:\n 'List task-board tasks. Filter by project (workspaceId), status, or urgency. '\n + 'Returns compact summaries (id, title, status, urgency, version, claim owner). '\n + 'Check this before starting work to find claimable todo tasks in your project.',\n parameters: {\n workspaceId: { type: 'string', description: 'Filter by project (DSH workspace id).' },\n status: { type: 'string', description: 'Filter by exact status (backlog/todo/in_progress/in_review/done/canceled/archived).' },\n urgency: { type: 'string', description: 'Filter by urgency (urgent/normal/relaxed).' },\n includeTrashed: { type: 'boolean', description: 'Include soft-deleted tasks (default false).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { revision?: number; tasks?: Array<Record<string, unknown>> }\n const tasks = v.tasks ?? []\n const head = `任务 ${tasks.length} 条(台账 rev ${v.revision ?? '?'})`\n if (tasks.length === 0) return [{ type: 'text', text: `${head}:无匹配任务。` }]\n return [{ type: 'text', text: [head, ...tasks.map(t => taskLine(t as never))].join('\\n') }]\n },\n },\n async execute(args) {\n try {\n const a = args as { workspaceId?: string; status?: string; urgency?: string; includeTrashed?: boolean }\n const tasks = store.snapshot().tasks.filter(t =>\n (a.workspaceId === undefined || t.workspaceId === a.workspaceId)\n && (a.status === undefined || t.status === a.status)\n && (a.urgency === undefined || t.urgency === a.urgency)\n && (a.includeTrashed === true || t.trashedAt === undefined))\n return json({ revision: store.snapshot().revision, tasks: tasks.map(summarize) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------- get\n disposers.push(register(defineTool({\n name: 'taskboard_get',\n description:\n 'Read one task in full: description, prompt, project, urgency, status, comments, executions, version. '\n + 'Read this (and the comments) BEFORE claiming or starting work on a task.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id from the board.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: TaskRecord & { effectivePrompt?: string } }\n return [{ type: 'text', text: v.task === undefined ? '任务不存在。' : taskDetail(v.task) }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const { id } = args\n const task = store.get(id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${id}`)\n return json({ task: { ...task, effectivePrompt: effectivePrompt(task) } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- create\n disposers.push(register(defineTool({\n name: 'taskboard_create',\n description:\n 'Create a task on the board. Required: title, workspaceId (project), urgency (urgent/normal/relaxed). '\n + 'Optional: description, prompt (sent to a fresh session on execution), status (default todo), '\n + 'execution mode (claim | scheduled+cron 定期重复 | scheduled+runAt 定时一次), model {provider, model} to pin executions to a model. '\n + 'Do not track trivial requests as tasks.',\n parameters: {\n title: { type: 'string', required: true, description: 'Short imperative line (1..200 chars).' },\n workspaceId: { type: 'string', required: true, description: 'Project (DSH workspace id) this task belongs to.' },\n urgency: { type: 'string', required: true, description: 'urgent (red) | normal (purple) | relaxed (blue).' },\n description: { type: 'string', description: 'What the task involves (plain text).' },\n prompt: { type: 'string', description: 'Extra execution instructions; the session receives title+description+this prompt.' },\n status: { type: 'string', description: 'Initial status; default todo. backlog = not approved for execution.' },\n execution: {\n type: 'object',\n additionalProperties: false,\n description: 'Execution config: { mode: \"claim\" } (default) | { mode: \"scheduled\", cron } periodic (定期, repeats) | { mode: \"scheduled\", runAt } one-shot (定时, fires once).',\n properties: {\n mode: { type: 'string', description: 'claim | scheduled.' },\n cron: { type: 'string', description: 'Five-field cron expression (scheduled periodic only).' },\n runAt: { type: 'string', description: 'One-shot trigger time: epoch ms or ISO string (scheduled one-shot only; must be in the future).' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omitted → board default model and effort, then deployment default.',\n properties: {\n provider: { type: 'string', description: 'Provider route id.' },\n model: { type: 'string', description: 'Provider-owned model id.' },\n reasoningEffort: { type: 'string', description: 'Optional thinking intensity / reasoning effort (e.g. low, medium, high).' },\n },\n },\n isolation: {\n type: 'string',\n description: 'Code isolation for executions: \"worktree\" (each run gets a fresh git worktree on branch task/<标题>+<taskId>) or \"none\" (run in the project directory, zero git interaction). Omitted → the board default (看板设置 → 默认执行隔离; factory default \"none\").',\n },\n permission: {\n type: 'string',\n description: 'Execution permission: \"read-only\", \"workspace-write\", or \"danger-full-access\". Omitted → the board default (看板设置 → 默认权限; factory default \"workspace-write\").',\n },\n presetId: {\n type: 'string',\n description: 'Agent preset for execution sessions (tool set / persona). Omitted → board default, then deployment default; empty string follows deployment default.',\n },\n checklist: {\n type: 'array',\n description: `Acceptance checklist (DoD) item texts (≤${MAX_CHECKLIST_ITEMS} × 200 chars); agents check them off at handoff, the user reviews.`,\n items: { type: 'string' },\n },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '创建失败。' : `已创建任务 ${t.id} [${t.status}] v${t.version}。写入前先 taskboard_get 读取。` }]\n },\n },\n async execute(args: {\n title: string\n workspaceId: string\n urgency: string\n status?: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number }\n model?: { provider?: string; model?: string; reasoningEffort?: string }\n isolation?: string\n permission?: string\n presetId?: string\n checklist?: string[]\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const title = normalizeTitle(args.title)\n if (workspaces.get(args.workspaceId) === undefined) {\n throw new ToolError(ERR.notFound, `unknown workspaceId ${args.workspaceId}`)\n }\n const urgency = asUrgency(args.urgency)\n const status = args.status === undefined ? 'todo' as const : asStatus(args.status)\n if (status !== 'backlog' && status !== 'todo') {\n throw new ToolError(ERR.invalidTransition, 'a new task must start as backlog or todo (in_progress requires claiming the task)')\n }\n const execution = normalizeExecution(args.execution ?? {}, deps.now())\n const modelRaw = args.model ?? store.snapshot().settings?.defaultModel\n const model = modelRaw !== undefined ? checkModel(deps, modelRaw) : undefined\n // 0.5.0: an omitted isolation is MATERIALIZED from the board setting\n // (看板设置) at creation, so later setting changes never rewrite\n // existing tasks.\n const isolation = args.isolation === undefined ? defaultIsolationOf(store.snapshot().settings) : asIsolation(args.isolation)\n // Match the GUI create route: freeze the current board default onto\n // the task so later settings changes do not silently alter a schedule.\n const permission = args.permission === undefined ? defaultPermissionOf(store.snapshot().settings) : asPermission(args.permission)\n const presetId = args.presetId === undefined ? store.snapshot().settings?.defaultPresetId : args.presetId.trim() || undefined\n // T9: match the GUI create route — trim and drop blank lines instead\n // of failing the whole call over one empty string.\n const checklistTexts = args.checklist?.map(c => c.trim()).filter(c => c.length > 0)\n const checklist = checklistTexts !== undefined && checklistTexts.length > 0 ? checklistFromTexts(checklistTexts) : undefined\n const now = deps.now()\n const task: TaskRecord = {\n id: newTaskId(),\n title,\n description: (args.description ?? '').trim(),\n prompt: normalizePrompt(args.prompt),\n workspaceId: args.workspaceId,\n urgency,\n status,\n blocked: false,\n execution,\n model,\n isolation,\n permission,\n ...(presetId !== undefined ? { presetId } : {}),\n ...(checklist !== undefined ? { checklist } : {}),\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: actor,\n updatedBy: actor,\n comments: [],\n executions: [],\n }\n await store.mutate('task-created', ledger => {\n ledger.tasks.push(task)\n return [task]\n })\n return json({ task: summarize(task) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- update\n disposers.push(register(defineTool({\n name: 'taskboard_update',\n description:\n 'Update a task\\'s title/description/prompt/urgency/blocked/permission. Requires ifVersion (read first). '\n + 'The model and execution config are read-only through this tool (they belong to the task owner/user).',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'The task version you read; the write fails on mismatch.' },\n title: { type: 'string', description: 'New title.' },\n description: { type: 'string', description: 'New description.' },\n prompt: { type: 'string', description: 'New execution prompt.' },\n urgency: { type: 'string', description: 'urgent | normal | relaxed.' },\n blocked: { type: 'boolean', description: 'Blocked marker (work cannot continue right now).' },\n permission: { type: 'string', description: 'Execution permission: read-only | workspace-write | danger-full-access.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '更新失败。' : `已更新任务 ${t.id},当前 v${t.version} [${t.status}]。` }]\n },\n },\n async execute(args: {\n id: string\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n permission?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: lookup + version guard + write run inside the serial-queue\n // mutation, on the fresh draft — a pre-read clone could pass its\n // version check and then blind-overwrite a concurrent writer.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n if (args.title !== undefined) next.title = normalizeTitle(args.title)\n if (args.description !== undefined) next.description = args.description.trim()\n if (args.prompt !== undefined) next.prompt = normalizePrompt(args.prompt)\n if (args.urgency !== undefined) next.urgency = asUrgency(args.urgency)\n if (args.blocked !== undefined) next.blocked = args.blocked\n if (args.permission !== undefined) next.permission = asPermission(args.permission)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n return json({ task: summarize(next!) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------------------ move\n disposers.push(register(defineTool({\n name: 'taskboard_move',\n description:\n 'Move a task between statuses (requires ifVersion). Claim = todo→in_progress (only a session '\n + 'inside the task\\'s project may claim; never take over a task held by another session). '\n + 'After implementing and self-verifying: comment, then in_progress→in_review. '\n + 'You can NEVER move a task to done — that requires explicit user confirmation.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n status: { type: 'string', required: true, description: 'Target status.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n archiveSessions: { type: 'boolean', description: 'When moving to archived: whether to archive associated execution sessions as well. Defaults to false.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; status?: string; version?: number }; sessionArchive?: SessionArchiveResult }\n const t = v.task\n return [{ type: 'text', text: t === undefined ? '移动失败。' : `任务 ${t.id} 已移到 ${t.status},当前 v${t.version}。${v.sessionArchive === undefined ? '' : ` 会话归档结果:${JSON.stringify(v.sessionArchive)}`}` }]\n },\n },\n async execute(args: { id: string; status: string; ifVersion: number; archiveSessions?: boolean }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n const to = asStatus(args.status)\n // Claim boundary (policy gate): resolving the caller's workspace is\n // async, so it happens BEFORE the mutation — the comparison runs INSIDE\n // against the FRESH task (stronger than the old stale-clone compare).\n const callerWsId = to === 'in_progress'\n ? await callerWorkspace(deps, exec as ToolRunContext)\n : undefined\n // R1: every state guard + the write itself run inside the mutation.\n let next: TaskRecord | undefined\n let beforeTask: TaskRecord | undefined\n await store.mutate('task-moved', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n beforeTask = task\n\n // Code-level gate: agents never complete a task.\n if (to === 'done') {\n throw new ToolError(ERR.forbidden, 'moving a task to done requires explicit user confirmation (GUI); agents cannot do it')\n }\n if (!canTransition(task.status, to)) {\n throw new ToolError(ERR.invalidTransition, `illegal transition ${task.status} → ${to}`)\n }\n // Exclusive hold: while a task is in_progress under a session\n // (explicit claimedBy — an agent claim or a live execution), no other\n // session may move it (that would be a takeover).\n if (task.status === 'in_progress' && task.claimedBy !== undefined && task.claimedBy !== actor.sessionId) {\n throw new ToolError(ERR.forbidden, `task is held by session ${task.claimedBy}; never take over another session's claim`)\n }\n // Claim boundary: the calling session must belong to the task's project.\n if (isClaim(task.status, to) && callerWsId !== task.workspaceId) {\n throw new ToolError(ERR.workspaceMismatch, 'only a session inside this task\\'s project may claim it')\n }\n next = structuredClone(task)\n next.status = to\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n if (isClaim(task.status, to)) next.blocked = false\n // Record the holder on a claim; every move out of in_progress releases it.\n syncClaim(next, to, deps.now(), isClaim(task.status, to) ? actor.sessionId : undefined)\n ledger.tasks[index] = next\n return [next]\n })\n const sessionArchive = to === 'archived' && args.archiveSessions === true\n ? await archiveTaskSessions(beforeTask ?? next!, deps.workspaces.archiveSession)\n : undefined\n return json({ task: summarize(next!), ...(sessionArchive !== undefined ? { sessionArchive } : {}) })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ----------------------------------------------------------- comment_add\n disposers.push(register(defineTool({\n name: 'taskboard_comment_add',\n description:\n 'Append a progress/report comment to a task. When handing off to review, the comment should cover: '\n + 'what changed, how it was verified, outcome, and remaining risks.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n body: { type: 'string', required: true, description: 'Comment text (1..4000 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comment?: { id?: string }; task?: { id?: string; version?: number; status?: string } }\n const c = v.comment\n const t = v.task\n if (c === undefined || t === undefined) return [{ type: 'text', text: '评论失败。' }]\n // The comment bumped the version — echo it so the agent can chain the\n // next write (e.g. move → in_review) WITHOUT re-reading.\n return [{\n type: 'text',\n text: `评论 ${c.id} 已添加;任务 ${t.id} 当前 v${t.version} [${t.status}](后续写操作用此版本号).`,\n }]\n },\n },\n async execute(args: { id: string; body: string }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const comment = {\n id: newCommentId(),\n body: normalizeBody(args.body),\n version: 1,\n createdAt: deps.now(),\n threadId: sessionId,\n }\n // R1: find + append inside the mutation (no ifVersion by design —\n // comments are append-only — but the write must not clobber a task\n // that changed while we were queued).\n let next: TaskRecord | undefined\n await store.mutate('comment-added', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n next.comments.push(comment)\n next.version = task.version + 1\n next.updatedAt = deps.now()\n ledger.tasks[index] = next\n return [next]\n })\n return json({ comment, task: { id: next!.id, version: next!.version, status: next!.status } })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- comments\n disposers.push(register(defineTool({\n name: 'taskboard_comments',\n description: 'List a task\\'s comments, oldest first. Read them before deciding to start work.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { comments?: unknown[] }\n const list = v.comments as Array<{ body: string; createdAt: number; threadId?: string }> | undefined\n if (list === undefined || list.length === 0) return [{ type: 'text', text: '无评论。' }]\n const lines = list.map(c => {\n const who = c.threadId !== undefined ? `agent ${String(c.threadId).slice(0, 24)}` : 'user'\n return `- [${who} ${new Date(c.createdAt).toISOString()}] ${c.body}`\n })\n return [{ type: 'text', text: `评论 ${list.length} 条:\\n${lines.join('\\n')}` }]\n },\n },\n async execute(args: { id: string }) {\n try {\n const task = store.get(args.id)\n if (task === undefined || task.trashedAt !== undefined) throw new ToolError(ERR.notFound, `no task ${args.id}`)\n return json({ comments: task.comments })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ---------------------------------------------------------------- delete\n disposers.push(register(defineTool({\n name: 'taskboard_delete',\n description:\n 'Soft-delete a task (marks it trashed; the user confirms the purge in the GUI). '\n + 'Requires ifVersion. Prefer canceled/archived over delete unless the task was a mistake.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read.' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { trashed?: boolean }\n return [{ type: 'text', text: v.trashed === true ? '任务已标记删除(等待用户在 GUI 清除)。' : '删除失败。' }]\n },\n },\n async execute(args: { id: string; ifVersion: number }, exec: unknown) {\n try {\n caller(exec as ToolRunContext)\n // R1: guards inside the mutation. S5: a running execution keeps\n // writing to the task (report, settlement) — refuse the soft-delete\n // until it is cancelled or settled. T8: clear claim residue.\n let next: TaskRecord | undefined\n await store.mutate('task-deleted', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.executions.some(e => e.outcome === 'running')) {\n throw new ToolError(ERR.invalidInput, '任务有正在运行的执行(先在 GUI 取消或等它结束再删除)')\n }\n next = structuredClone(task)\n next.trashedAt = deps.now()\n next.version = task.version + 1\n delete next.claimedBy\n delete next.claimedAt\n next.blocked = false\n ledger.tasks[index] = next\n return [next]\n })\n return { trashed: true }\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // -------------------------------------------------------------- checklist\n disposers.push(register(defineTool({\n name: 'taskboard_checklist',\n description:\n `Manage the task's acceptance checklist (DoD). Actions: \"add\" (append item texts, ≤10 per call), `\n + '\"check\" (mark an item done, with an optional evidence note), \"uncheck\" (reopen an item). '\n + 'Checking items NEVER completes the task — done stays a user-only action. Requires ifVersion.',\n parameters: {\n id: { type: 'string', required: true, description: 'Task id.' },\n action: { type: 'string', required: true, description: 'add | check | uncheck.' },\n ifVersion: { type: 'number', required: true, description: 'Task version you read; fails on mismatch.' },\n items: {\n type: 'array',\n description: 'Item texts to append (action=add only; 1..10 per call, 200 chars each).',\n items: { type: 'string' },\n },\n itemId: { type: 'string', description: 'The checklist item id (action=check/uncheck).' },\n note: { type: 'string', description: 'Evidence note recorded with the check (≤400 chars).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { task?: { id?: string; version?: number }; checklist?: Array<Record<string, unknown>>; done?: number; total?: number }\n if (v.task === undefined || v.checklist === undefined) return [{ type: 'text', text: '清单操作失败。' }]\n const lines = v.checklist.map((i, index) => `${i.checked === true ? '☑' : '☐'} [${index + 1}] ${String(i.text)}${i.note !== undefined ? `(证据: ${String(i.note)})` : ''} id=${String(i.id)}`)\n return [{\n type: 'text',\n text: `任务 ${v.task.id} 验收清单 ${v.done ?? 0}/${v.total ?? 0} 已完成,当前 v${v.task.version}:\\n${lines.join('\\n')}`,\n }]\n },\n },\n async execute(args: {\n id: string\n action: string\n ifVersion: number\n items?: string[]\n itemId?: string\n note?: string\n }, exec: unknown) {\n try {\n const { actor } = caller(exec as ToolRunContext)\n // R1: guards + the checklist edit itself run inside the mutation.\n let next: TaskRecord | undefined\n await store.mutate('task-updated', ledger => {\n const { index, task } = liveTaskAt(ledger, args.id)\n versionGuard(task, args.ifVersion)\n if (task.status === 'archived') throw new ToolError(ERR.invalidTransition, 'archived tasks are immutable')\n next = structuredClone(task)\n const checklist: ChecklistItem[] = next.checklist === undefined ? [] : [...next.checklist]\n\n if (args.action === 'add') {\n const texts = args.items ?? []\n if (texts.length === 0 || texts.length > 10) {\n throw new ToolError(ERR.invalidInput, 'items must carry 1..10 texts per add call')\n }\n if (checklist.length + texts.length > MAX_CHECKLIST_ITEMS) {\n throw new ToolError(ERR.invalidInput, `checklist may hold at most ${MAX_CHECKLIST_ITEMS} items (currently ${checklist.length})`)\n }\n checklist.push(...checklistFromTexts(texts))\n } else if (args.action === 'check') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for check')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n const note = args.note !== undefined && args.note.trim().length > 0 ? args.note.trim().slice(0, 400) : undefined\n item.checked = true\n item.checkedBy = actor.sessionId\n item.checkedAt = deps.now()\n if (note !== undefined) item.note = note\n } else if (args.action === 'uncheck') {\n if (args.itemId === undefined) throw new ToolError(ERR.invalidInput, 'itemId is required for uncheck')\n const item = checklist.find(i => i.id === args.itemId)\n if (item === undefined) throw new ToolError(ERR.notFound, `no checklist item ${args.itemId} (task ${args.id})`)\n item.checked = false\n delete item.checkedBy\n delete item.checkedAt\n delete item.note\n } else {\n throw new ToolError(ERR.invalidInput, `action must be add | check | uncheck (got \"${args.action}\")`)\n }\n\n if (checklist.length > 0) next.checklist = checklist\n else delete next.checklist\n next.version = task.version + 1\n next.updatedAt = deps.now()\n next.updatedBy = actor\n ledger.tasks[index] = next\n return [next]\n })\n const progress = next!.checklist !== undefined\n ? { done: next!.checklist.filter(i => i.checked).length, total: next!.checklist.length }\n : { done: 0, total: 0 }\n return json({ task: { id: next!.id, version: next!.version }, checklist: next!.checklist ?? [], ...progress })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n // ------------------------------------------------------- execution report\n disposers.push(register(defineTool({\n name: 'taskboard_execution_report',\n description:\n 'Submit the structured execution report for the task you are currently executing (summary / changed '\n + 'files / how you verified / artifacts / remaining risk). Submit BEFORE moving the task to in_review; '\n + 'a later submission overwrites the previous report. If your run already settled, you may back-submit '\n + 'onto your latest succeeded execution while you still hold the task. Commits and diffs are host-collected.',\n parameters: {\n summary: { type: 'string', required: true, description: 'What was done (1..2000 chars).' },\n changedFiles: {\n type: 'array',\n description: 'Files you changed (paths, ≤50 × 300 chars).',\n items: { type: 'string' },\n },\n checks: {\n type: 'array',\n description: 'How the work was verified (e.g. test commands + outcomes, ≤50 entries).',\n items: { type: 'string' },\n },\n artifacts: {\n type: 'array',\n description: 'Artifacts worth reviewing (build outputs, screenshots, docs, ≤30 entries).',\n items: { type: 'string' },\n },\n risk: { type: 'string', description: 'Known remaining risks or follow-ups (≤2000 chars, optional).' },\n },\n output: {\n schema: JSON_OUT,\n render: (_args, value) => {\n const v = value as { taskId?: string; executionId?: string; report?: { summary?: string } }\n if (v.taskId === undefined || v.report === undefined) return [{ type: 'text', text: '报告提交失败。' }]\n return [{\n type: 'text',\n text: `执行报告已记录到任务 ${v.taskId}(执行 ${v.executionId}):${v.report.summary?.slice(0, 120) ?? ''}\\n`\n + '接下来:taskboard_comment_add 留交接评论,然后 taskboard_move 移至待验收 in_review。',\n }]\n },\n },\n async execute(args: {\n summary: string\n changedFiles?: string[]\n checks?: string[]\n artifacts?: string[]\n risk?: string\n }, exec: unknown) {\n try {\n const { sessionId } = caller(exec as ToolRunContext)\n const report = normalizeExecutionReport(args)\n // Path 1 (unchanged): attach to the RUNNING execution this session\n // owns — reports ride the live run, so the agent never needs ids.\n let taskId: string | undefined\n let executionId: string | undefined\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n const execution = task.executions.find(e => e.sessionId === sessionId && e.outcome === 'running')\n if (execution !== undefined) {\n execution.report = report\n taskId = task.id\n executionId = execution.id\n return [task]\n }\n }\n return undefined\n })\n // Path 2 (review follow-up, P2): back-submit onto a session-owned\n // SETTLED execution — the main conversation claims tasks directly and\n // has no live run. Allowed when the session holds the task or owns\n // its latest successful execution; never touches anyone else's runs.\n if (taskId === undefined || executionId === undefined) {\n await store.mutate('execution-recorded', ledger => {\n for (const task of ledger.tasks) {\n if (task.trashedAt !== undefined || task.status === 'archived') continue\n const last = task.executions[task.executions.length - 1]\n const owned = last !== undefined && last.sessionId === sessionId && last.outcome === 'succeeded'\n const holds = task.claimedBy === sessionId && last !== undefined && last.sessionId === sessionId\n if (!owned && !holds) continue\n last!.report = report\n taskId = task.id\n executionId = last!.id\n return [task]\n }\n return undefined\n })\n }\n if (taskId === undefined || executionId === undefined) {\n throw new ToolError(ERR.forbidden, 'no running execution and no settled execution of yours to report on — reports attach to your running execution, or back-submit onto your latest succeeded one while you hold the task')\n }\n return json({ taskId, executionId, report })\n } catch (error) { fail(error) }\n },\n })) as () => void)\n\n return disposers\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/lib/shared/api.js.map b/lib/shared/api.js.map index b996831..a04a557 100644 --- a/lib/shared/api.js.map +++ b/lib/shared/api.js.map @@ -1 +1 @@ -{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type QueueSummary = {\n depth: number\n dispatching: number\n oldestQueuedAt?: number\n oldestWaitMinutes?: number\n maxConcurrent: number\n}\n\nexport type StateResponse = TaskLedger & { capabilities?: { archiveSessions: boolean }; queue?: QueueSummary }\n\n/** Result of dropping every durable queue entry (board queue panel). */\nexport type QueueClearResponse = { cleared: number }\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n/** Sessions currently owned by one project and eligible for explicit reuse. */\nexport type ProjectSessionView = { id: string; title?: string }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n model?: TaskModel\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Agent preset for execution sessions; omitted = deployment default. */\n presetId?: string\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string; archiveSessions?: boolean }\n\nexport type SessionArchiveResult = { archived: string[]; failed: Array<{ sessionId: string; error: string }>; unsupported: string[] }\nexport type MoveTaskResponse = TaskSummary & { sessionArchive?: SessionArchiveResult }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** One content-addressed image uploaded outside the ledger. */\nexport type AttachmentUpload = {\n id: string\n name: string\n size: number\n url: string\n extension: 'png' | 'jpg' | 'gif' | 'webp'\n mime: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp'\n}\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n queue: QueueSummary\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Current host-side location of all durable taskboard data. */\nexport type StorageStatus = {\n currentDirectory: string\n defaultDirectory: string\n isDefault: boolean\n configured: boolean\n writable: boolean\n assetCount: number\n assetBytes: number\n checkedDirectory?: string\n error?: string\n}\n\n/** Completed storage relocation, including non-fatal old-file cleanup failures. */\nexport type StorageMigrationResult = StorageStatus & { migrated: boolean; warnings: string[] }\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n /** Global simultaneous execution cap (1–100). */\n maxConcurrent?: number\n /** Offline missed-window threshold in whole minutes (1–1440). */\n scheduleMissedAfterMinutes?: number\n /** Drop queued work after this many minutes; 0 retains it indefinitely. */\n queueMaxAgeMinutes?: number\n /** Minimum milliseconds between scheduled session starts; defaults to 1000 and accepts 0 to disable throttling. */\n dispatchIntervalMs?: number\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}\n"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"} \ No newline at end of file +{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type QueueSummary = {\n depth: number\n dispatching: number\n oldestQueuedAt?: number\n oldestWaitMinutes?: number\n maxConcurrent: number\n}\n\nexport type StateResponse = TaskLedger & { capabilities?: { archiveSessions: boolean }; queue?: QueueSummary }\n\n/** Result of dropping every durable queue entry (board queue panel). */\nexport type QueueClearResponse = { cleared: number }\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n/** Sessions currently owned by one project and eligible for explicit reuse. */\nexport type ProjectSessionView = { id: string; title?: string }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n /** Omitted = board default; null = follow the deployment default explicitly. */\n model?: TaskModel | null\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Omitted = board default; null = follow the deployment default explicitly. */\n presetId?: string | null\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string; archiveSessions?: boolean }\n\nexport type SessionArchiveResult = { archived: string[]; failed: Array<{ sessionId: string; error: string }>; unsupported: string[] }\nexport type MoveTaskResponse = TaskSummary & { sessionArchive?: SessionArchiveResult }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** One content-addressed image uploaded outside the ledger. */\nexport type AttachmentUpload = {\n id: string\n name: string\n size: number\n url: string\n extension: 'png' | 'jpg' | 'gif' | 'webp'\n mime: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp'\n}\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n queue: QueueSummary\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Current host-side location of all durable taskboard data. */\nexport type StorageStatus = {\n currentDirectory: string\n defaultDirectory: string\n isDefault: boolean\n configured: boolean\n writable: boolean\n assetCount: number\n assetBytes: number\n checkedDirectory?: string\n error?: string\n}\n\n/** Completed storage relocation, including non-fatal old-file cleanup failures. */\nexport type StorageMigrationResult = StorageStatus & { migrated: boolean; warnings: string[] }\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n /** Default model and optional reasoning effort for NEW tasks; omitted = deployment default. */\n defaultModel?: TaskModel\n /** Default agent preset for NEW tasks; omitted = deployment default. */\n defaultPresetId?: string\n /** Global simultaneous execution cap (1–100). */\n maxConcurrent?: number\n /** Offline missed-window threshold in whole minutes (1–1440). */\n scheduleMissedAfterMinutes?: number\n /** Drop queued work after this many minutes; 0 retains it indefinitely. */\n queueMaxAgeMinutes?: number\n /** Minimum milliseconds between scheduled session starts; defaults to 1000 and accepts 0 to disable throttling. */\n dispatchIntervalMs?: number\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}\n"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"} \ No newline at end of file diff --git a/lib/shared/protocol.js b/lib/shared/protocol.js index a80b210..04c894a 100644 --- a/lib/shared/protocol.js +++ b/lib/shared/protocol.js @@ -105,6 +105,12 @@ function asBoardSettings(raw) { out.syncExternalSessions = e.syncExternalSessions; } if (e.defaultPermission !== void 0) out.defaultPermission = asPermission(e.defaultPermission); + if (e.defaultModel !== void 0) out.defaultModel = normalizeModel(e.defaultModel); + if (e.defaultPresetId !== void 0) { + if (typeof e.defaultPresetId !== "string") throw new Error("defaultPresetId must be a string"); + const id = e.defaultPresetId.trim(); + if (id.length > 0) out.defaultPresetId = id; + } if (e.maxConcurrent !== void 0) { if (typeof e.maxConcurrent !== "number" || !Number.isSafeInteger(e.maxConcurrent) || e.maxConcurrent < 1 || e.maxConcurrent > 100) throw new Error(`maxConcurrent must be an integer from 1 to 100`); out.maxConcurrent = e.maxConcurrent; diff --git a/lib/shared/protocol.js.map b/lib/shared/protocol.js.map index b1cb846..7cec9b2 100644 --- a/lib/shared/protocol.js.map +++ b/lib/shared/protocol.js.map @@ -1 +1 @@ -{"version":3,"file":"protocol.js","names":[],"sources":["../../src/shared/protocol.ts"],"sourcesContent":["/**\n * Task domain model, state machine, urgency classes, and cron math — the\n * framework-free core shared verbatim by the host half (tools, store, routes,\n * scheduler) and, from P2 on, the browser half (board view).\n *\n * Everything here is a pure function over plain data: no imports beyond the\n * standard library, no I/O, no globals. Tests drive it directly.\n *\n * @module dsh-taskboard/shared/protocol\n */\n\n// ---------------------------------------------------------------------------\n// Status vocabulary\n// ---------------------------------------------------------------------------\n\n/**\n * Task lifecycle states. Main board columns render `backlog → todo →\n * in_progress → in_review → done`; `canceled` and `archived` are secondary\n * states collected under an \"other tasks\" tab. `blocked` is NOT a status —\n * it is a horizontal marker any non-terminal state may carry.\n */\nexport type TaskStatus =\n | 'backlog'\n | 'todo'\n | 'in_progress'\n | 'in_review'\n | 'done'\n | 'canceled'\n | 'archived'\n\n/** Statuses shown as the five main board columns, in order. */\nexport const MAIN_STATUSES: readonly TaskStatus[] = [\n 'backlog',\n 'todo',\n 'in_progress',\n 'in_review',\n 'done',\n]\n\n/** Statuses collected under the secondary tab. */\nexport const SECONDARY_STATUSES: readonly TaskStatus[] = ['canceled', 'archived']\n\n/** Every valid status, main first. */\nexport const ALL_STATUSES: readonly TaskStatus[] = [...MAIN_STATUSES, ...SECONDARY_STATUSES]\n\n/**\n * Legal forward/sideways transitions. Anything not listed is rejected with\n * `invalid_transition`. `archived` is terminal.\n */\nconst TRANSITIONS: Readonly<Record<TaskStatus, readonly TaskStatus[]>> = {\n backlog: ['todo', 'canceled'],\n todo: ['in_progress', 'backlog', 'canceled'],\n in_progress: ['in_review', 'todo', 'canceled'],\n in_review: ['in_progress', 'todo', 'done', 'canceled'],\n done: ['archived', 'todo'],\n canceled: ['archived', 'todo'],\n archived: [],\n}\n\n/**\n * Whether a status move is legal per the state machine.\n * @param from - current status.\n * @param to - requested status.\n * @returns true when the transition is allowed.\n */\nexport function canTransition(from: TaskStatus, to: TaskStatus): boolean {\n return TRANSITIONS[from].includes(to)\n}\n\n/**\n * The claim move: the one transition that transfers ownership of a task to\n * the calling session. Guarded by the project (workspace) boundary in the\n * tool layer.\n */\nexport function isClaim(from: TaskStatus, to: TaskStatus): boolean {\n return from === 'todo' && to === 'in_progress'\n}\n\n/** Statuses a `done` move may depart from (user confirmation only). */\nexport function canCompleteFrom(from: TaskStatus): boolean {\n return from === 'in_review'\n}\n\n// ---------------------------------------------------------------------------\n// Urgency\n// ---------------------------------------------------------------------------\n\n/** Urgency classes with fixed UI colors. */\nexport type Urgency = 'urgent' | 'normal' | 'relaxed'\n\n/** All valid urgency values. */\nexport const URGENCIES: readonly Urgency[] = ['urgent', 'normal', 'relaxed']\n\n/** CSS color token per urgency: red / purple / blue. */\nexport const URGENCY_COLOR: Readonly<Record<Urgency, string>> = {\n urgent: '#e5484d',\n normal: '#8e4ec6',\n relaxed: '#3e63dd',\n}\n\n// ---------------------------------------------------------------------------\n// Execution\n// ---------------------------------------------------------------------------\n\n/**\n * Per-task code isolation mode (0.3.0).\n * - `worktree`: each execution runs in a fresh `git worktree` on a dedicated\n * task branch (`task/<标题>+<taskId>`) under `<workspace>/.dsh-worktrees/`.\n * - `none`: run in the workspace directory as before, zero git interaction.\n * Omitted = {@link DEFAULT_ISOLATION}; since 0.5.0 creation materializes the\n * board default onto the record (看板设置 → 执行隔离), and non-git projects\n * still auto-degrade at run time (the execution record carries an\n * `isolationNote` explaining why).\n */\nexport type IsolationMode = 'worktree' | 'none'\n\n/**\n * Factory-default isolation (0.5.0): 原目录执行. Applies when neither the\n * task record nor the board setting (`BoardSettings.defaultIsolation`)\n * says otherwise. Before 0.5.0 the implicit default was 'worktree'.\n */\nexport const DEFAULT_ISOLATION: IsolationMode = 'none'\n\n/** Factory default and safe UI/API range for concurrent task executions. */\nexport const DEFAULT_MAX_CONCURRENT = 3\nexport const MIN_MAX_CONCURRENT = 1\nexport const MAX_MAX_CONCURRENT = 100\n/** Factory default and safe UI/API range for offline missed-window handling. */\nexport const DEFAULT_SCHEDULE_MISSED_AFTER_MINUTES = 5\nexport const MIN_SCHEDULE_MISSED_AFTER_MINUTES = 1\nexport const MAX_SCHEDULE_MISSED_AFTER_MINUTES = 1_440\n/** A queued entry's optional shelf life. Zero deliberately preserves durable replay. */\nexport const DEFAULT_QUEUE_MAX_AGE_MINUTES = 0\nexport const MAX_QUEUE_MAX_AGE_MINUTES = 10_080\n/** Smallest user-selectable delay; zero explicitly disables dispatch throttling. */\nexport const MIN_DISPATCH_INTERVAL_MS = 0\n/** Default spacing between scheduler-created sessions. */\nexport const DEFAULT_DISPATCH_INTERVAL_MS = 1_000\nexport const MAX_DISPATCH_INTERVAL_MS = 60_000\n\n/** Validate an isolation value. */\nexport function asIsolation(raw: string): IsolationMode {\n if (raw !== 'worktree' && raw !== 'none') {\n throw new Error(\"isolation must be 'worktree' or 'none'\")\n }\n return raw\n}\n\n/** Resolve a task's effective isolation (omitted → the factory default). */\nexport function effectiveIsolation(task: Pick<TaskRecord, 'isolation'>): IsolationMode {\n return task.isolation === undefined ? DEFAULT_ISOLATION : task.isolation\n}\n\n/**\n * Execution permission preset (0.5.5). Matches DSH permission presets:\n * - 'workspace-write': 可写入工作区 (factory default)\n * - 'read-only': 仅可查看\n * - 'danger-full-access': 完全权限\n */\nexport type PermissionMode = 'workspace-write' | 'read-only' | 'danger-full-access'\n\n/** Factory default permission preset (0.5.5). */\nexport const DEFAULT_PERMISSION: PermissionMode = 'workspace-write'\n\n/** Canonical list of supported permission modes. */\nexport const ALL_PERMISSIONS: readonly PermissionMode[] = ['workspace-write', 'read-only', 'danger-full-access']\n\n/** Validate and normalize a permission string into a valid {@link PermissionMode}. */\nexport function asPermission(raw: unknown): PermissionMode {\n if (typeof raw !== 'string') return DEFAULT_PERMISSION\n const normalized = raw.trim()\n if (normalized === 'workspace-write' || normalized === 'workspaceWrite') return 'workspace-write'\n if (normalized === 'read-only' || normalized === 'readOnly') return 'read-only'\n if (normalized === 'danger-full-access' || normalized === 'fullAccess') return 'danger-full-access'\n throw new Error(\"permission must be 'workspace-write', 'read-only', or 'danger-full-access'\")\n}\n\n/**\n * Board-level settings persisted with the ledger (0.5.0). Only fields the\n * user explicitly set are present; absent fields follow factory defaults.\n */\nexport type BoardSettings = {\n /** Default code isolation applied when a NEW task is created without an explicit choice. */\n defaultIsolation?: IsolationMode\n /** Automatically capture external workspace sessions into the taskboard (default: false). */\n syncExternalSessions?: boolean\n /** Default permission preset applied when a NEW task is created without an explicit choice (0.5.5, default: 'workspace-write'). */\n defaultPermission?: PermissionMode\n /** Global cap for simultaneously running taskboard executions. */\n maxConcurrent?: number\n /** Only never-queued windows older than this are classified as offline misses. */\n scheduleMissedAfterMinutes?: number\n /** Drop an already queued window after this many minutes; zero keeps it indefinitely. */\n queueMaxAgeMinutes?: number\n /** Minimum delay between scheduled session starts; zero explicitly disables throttling. */\n dispatchIntervalMs?: number\n}\n\n/** Validate raw input into sanitized {@link BoardSettings} (unknown fields dropped). */\nexport function asBoardSettings(raw: unknown): BoardSettings {\n if (typeof raw !== 'object' || raw === null) {\n throw new Error('board settings must be an object')\n }\n const e = raw as Record<string, unknown>\n const out: BoardSettings = {}\n if (e.defaultIsolation !== undefined) {\n if (typeof e.defaultIsolation !== 'string') {\n throw new Error(\"defaultIsolation must be 'worktree' or 'none'\")\n }\n out.defaultIsolation = asIsolation(e.defaultIsolation)\n }\n if (e.syncExternalSessions !== undefined) {\n if (typeof e.syncExternalSessions !== 'boolean') {\n throw new Error('syncExternalSessions must be a boolean')\n }\n out.syncExternalSessions = e.syncExternalSessions\n }\n if (e.defaultPermission !== undefined) {\n out.defaultPermission = asPermission(e.defaultPermission)\n }\n if (e.maxConcurrent !== undefined) {\n if (typeof e.maxConcurrent !== 'number' || !Number.isSafeInteger(e.maxConcurrent)\n || e.maxConcurrent < MIN_MAX_CONCURRENT || e.maxConcurrent > MAX_MAX_CONCURRENT) {\n throw new Error(`maxConcurrent must be an integer from ${MIN_MAX_CONCURRENT} to ${MAX_MAX_CONCURRENT}`)\n }\n out.maxConcurrent = e.maxConcurrent\n }\n if (e.scheduleMissedAfterMinutes !== undefined) {\n if (typeof e.scheduleMissedAfterMinutes !== 'number' || !Number.isSafeInteger(e.scheduleMissedAfterMinutes)\n || e.scheduleMissedAfterMinutes < MIN_SCHEDULE_MISSED_AFTER_MINUTES || e.scheduleMissedAfterMinutes > MAX_SCHEDULE_MISSED_AFTER_MINUTES) {\n throw new Error(`scheduleMissedAfterMinutes must be an integer from ${MIN_SCHEDULE_MISSED_AFTER_MINUTES} to ${MAX_SCHEDULE_MISSED_AFTER_MINUTES}`)\n }\n out.scheduleMissedAfterMinutes = e.scheduleMissedAfterMinutes\n }\n if (e.queueMaxAgeMinutes !== undefined) {\n if (typeof e.queueMaxAgeMinutes !== 'number' || !Number.isSafeInteger(e.queueMaxAgeMinutes)\n || e.queueMaxAgeMinutes < DEFAULT_QUEUE_MAX_AGE_MINUTES || e.queueMaxAgeMinutes > MAX_QUEUE_MAX_AGE_MINUTES) {\n throw new Error(`queueMaxAgeMinutes must be an integer from ${DEFAULT_QUEUE_MAX_AGE_MINUTES} to ${MAX_QUEUE_MAX_AGE_MINUTES}`)\n }\n out.queueMaxAgeMinutes = e.queueMaxAgeMinutes\n }\n if (e.dispatchIntervalMs !== undefined) {\n if (typeof e.dispatchIntervalMs !== 'number' || !Number.isSafeInteger(e.dispatchIntervalMs)\n || e.dispatchIntervalMs < MIN_DISPATCH_INTERVAL_MS || e.dispatchIntervalMs > MAX_DISPATCH_INTERVAL_MS) {\n throw new Error(`dispatchIntervalMs must be an integer from ${MIN_DISPATCH_INTERVAL_MS} to ${MAX_DISPATCH_INTERVAL_MS}`)\n }\n out.dispatchIntervalMs = e.dispatchIntervalMs\n }\n return out\n}\n\n/** The effective default isolation for NEW tasks (board setting → factory default). */\nexport function defaultIsolationOf(settings?: BoardSettings): IsolationMode {\n return settings?.defaultIsolation ?? DEFAULT_ISOLATION\n}\n\n/** The effective external session sync switch (board setting → factory default false). */\nexport function defaultSyncExternalSessionsOf(settings?: BoardSettings): boolean {\n return settings?.syncExternalSessions ?? false\n}\n\n/** The effective default permission preset for NEW tasks (board setting → factory default 'workspace-write'). */\nexport function defaultPermissionOf(settings?: BoardSettings): PermissionMode {\n return settings?.defaultPermission ?? DEFAULT_PERMISSION\n}\n\n/** Effective global execution cap (board setting → supplied deployment default). */\nexport function maxConcurrentOf(settings: BoardSettings | undefined, fallback = DEFAULT_MAX_CONCURRENT): number {\n return settings?.maxConcurrent ?? fallback\n}\n\n/** Effective offline missed-window threshold in minutes (board setting → factory default). */\nexport function scheduleMissedAfterMinutesOf(settings?: BoardSettings): number {\n return settings?.scheduleMissedAfterMinutes ?? DEFAULT_SCHEDULE_MISSED_AFTER_MINUTES\n}\n\n/** Effective queued-work shelf life in minutes; zero means durable replay. */\nexport function queueMaxAgeMinutesOf(settings?: BoardSettings): number {\n return settings?.queueMaxAgeMinutes ?? DEFAULT_QUEUE_MAX_AGE_MINUTES\n}\n\n/** Effective minimum spacing between scheduled session starts. */\nexport function dispatchIntervalMsOf(settings?: BoardSettings): number {\n return settings?.dispatchIntervalMs ?? DEFAULT_DISPATCH_INTERVAL_MS\n}\n\n/** How a task may run. */\nexport type ExecutionMode = 'claim' | 'scheduled'\n\n/** How a periodic cron task continues after a successful scheduled run. */\nexport type PeriodicCompletion = 'rearm' | 'spawn'\n/** Default periodic behavior: keep this round for review and create the next todo card. */\nexport const DEFAULT_PERIODIC_COMPLETION: PeriodicCompletion = 'spawn'\n\n/** How a scheduled task obtains its conversation when no existing session is selected. */\nexport type ScheduledSessionReuseMode = 'fresh' | 'reuse'\n\n/**\n * Per-task execution configuration. `claim` tasks wait for an in-project\n * session to claim them; `scheduled` tasks run on the host cron scheduler.\n */\nexport interface ExecutionConfig {\n mode: ExecutionMode\n /** Explicit existing project session to continue on scheduled runs. This is a mandatory selection. */\n reuseSessionId?: string\n /**\n * Periodic-cron-only policy: `fresh` creates a conversation for every\n * trigger; `reuse` creates one on the first trigger then continues it.\n * Omitted is the legacy `reuse` behavior, retained for already-saved tasks.\n */\n sessionReuseMode?: ScheduledSessionReuseMode\n /** Host-owned continuation pointer for `sessionReuseMode: 'reuse'`. */\n autoReuseSessionId?: string\n /** Effective configuration paired with {@link autoReuseSessionId}. */\n autoReuseSessionKey?: string\n /**\n * Five-field cron expression (minute hour day month weekday). Present on\n * PERIODIC scheduled tasks (定期执行): the scheduler refires the task each\n * time it comes due while the card sits in todo.\n */\n cron?: string\n /**\n * Optional host-owned completion policy for periodic cron tasks. `rearm`\n * returns this card to todo; `spawn` preserves it for review and creates a\n * fresh todo successor. Omitted records use the {@link DEFAULT_PERIODIC_COMPLETION}.\n */\n periodicCompletion?: PeriodicCompletion\n /**\n * One-shot trigger time (epoch ms). Present on ONE-SHOT scheduled tasks\n * (定时执行): the scheduler fires the task once when due and consumes the\n * field. Mutually exclusive with {@link cron}.\n */\n runAt?: number\n /** Next due time (epoch ms); maintained by the host scheduler. */\n nextRunAt?: number\n /**\n * Original due window of a scheduled dispatch waiting for global capacity.\n * Its presence is durable: a host restart must not reclassify an already\n * online-and-queued run as a window missed while the host was down.\n */\n queuedRunAt?: number\n /** When the scheduler put {@link queuedRunAt} into its durable FIFO queue. */\n queuedAt?: number\n /** A queue entry reserved for an execution gate but not yet running. */\n dispatchingRunAt?: number\n /** Last time the scheduler triggered this task (epoch ms). */\n lastTriggeredAt?: number\n}\n\n/**\n * Parse a five-field cron expression. Supported field syntax: star, star/step\n * (`* / n` without spaces), a single number, an `a-b` range, and comma lists\n * of those. Day-of-week accepts both 0 and 7 as Sunday (normalized to 0).\n *\n * @param expr - the expression to parse.\n * @returns the match sets per field, or null when invalid.\n */\nexport function parseCron(expr: string): CronMatch | null {\n const fields = expr.trim().split(/\\s+/)\n if (fields.length !== 5) return null\n const ranges: ReadonlyArray<readonly [number, number]> = [\n [0, 59],\n [0, 23],\n [1, 31],\n [1, 12],\n [0, 7],\n ]\n const sets: Array<Set<number>> = []\n for (let i = 0; i < 5; i++) {\n const [min, max] = ranges[i]!\n const set = new Set<number>()\n if (!parseCronField(fields[i]!, min, max, set)) return null\n sets.push(set)\n }\n const weekdays = new Set<number>()\n for (const day of sets[4]!) weekdays.add(day === 7 ? 0 : day)\n return { minutes: sets[0]!, hours: sets[1]!, days: sets[2]!, months: sets[3]!, weekdays }\n}\n\n/** Parsed cron field match sets. */\nexport type CronMatch = {\n minutes: ReadonlySet<number>\n hours: ReadonlySet<number>\n days: ReadonlySet<number>\n months: ReadonlySet<number>\n weekdays: ReadonlySet<number>\n}\n\n/** Parse one cron field into a match set; false on any syntax error. */\nfunction parseCronField(field: string, min: number, max: number, out: Set<number>): boolean {\n for (const part of field.split(',')) {\n const [range, stepRaw] = part.split('/')\n const step = stepRaw === undefined ? 1 : Number.parseInt(stepRaw, 10)\n if (!Number.isInteger(step) || step < 1) return false\n let lo: number\n let hi: number\n if (range === undefined || range === '') return false\n if (range === '*') {\n lo = min\n hi = max\n } else if (range.includes('-')) {\n const [a, b] = range.split('-')\n lo = Number.parseInt(a ?? '', 10)\n hi = Number.parseInt(b ?? '', 10)\n if (!Number.isInteger(lo) || !Number.isInteger(hi)) return false\n } else {\n lo = Number.parseInt(range, 10)\n if (!Number.isInteger(lo)) return false\n hi = stepRaw === undefined ? lo : max\n }\n if (lo < min || hi > max || lo > hi) return false\n for (let v = lo; v <= hi; v += step) out.add(v)\n }\n return out.size > 0\n}\n\n/**\n * The next time at or after `from` matching the cron sets (local time),\n * or null when no match exists within four years (e.g. Feb 30).\n * @param match - parsed cron sets.\n * @param from - epoch ms start point (inclusive match candidate).\n * @returns the next match's epoch ms, or null.\n */\nexport function nextCronTime(match: CronMatch, from: number): number | null {\n // Walk minute by minute from the next whole minute, capped at ~4 years.\n const start = new Date(from)\n start.setSeconds(0, 0)\n start.setMinutes(start.getMinutes() + 1)\n const cap = from + 4 * 366 * 24 * 60 * 60 * 1000\n let t = start.getTime()\n while (t <= cap) {\n const d = new Date(t)\n if (\n match.months.has(d.getMonth() + 1)\n && match.days.has(d.getDate())\n && match.weekdays.has(d.getDay())\n && match.hours.has(d.getHours())\n && match.minutes.has(d.getMinutes())\n ) {\n return t\n }\n t += 60_000\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Records\n// ---------------------------------------------------------------------------\n\n/** Who performed a write. */\nexport type Actor =\n | { kind: 'user' }\n | { kind: 'agent'; sessionId: string }\n | { kind: 'system' }\n\n/** Structured row of a multi-repo merge system comment (0.6.4). */\nexport type SystemCommentRow = {\n /** Repo path relative to the workspace ('' = the workspace root repo). */\n repo: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim) when outcome = 'failed'. */\n error?: string\n}\n\n/** A progress/report comment on a task. */\nexport type CommentRecord = {\n id: string\n /** Comment body (plain text; UI renders as pre-wrapped). */\n body: string\n /** Optimistic-concurrency version of this comment. */\n version: number\n createdAt: number\n /** The session that wrote this comment; absent for user-written ones. */\n threadId?: string\n /**\n * i18n key of a host-generated system message (0.6.4). The GUI localizes it\n * at render time; `body` stays a zh fallback for agent tools / CSV / raw\n * JSON views.\n */\n systemKey?: string\n /** Flat {name} interpolation params for the system message. */\n systemParams?: Record<string, string>\n /** Structured per-repo rows for the multi-repo merge summary (0.6.4). */\n systemRows?: SystemCommentRow[]\n}\n\n/** One commit produced by an isolated execution (hash + subject). */\nexport type CommitInfo = { hash: string; subject: string }\n\n// ---------------------------------------------------------------------------\n// Multi-repo mirror isolation (0.6.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Max repos ONE task mirror may cover (0.6.3): the workspace root repo plus\n * its parallel nested repos. A workspace discovering more degrades the whole\n * worktree run to the original directory (plan §4.1 — predictable, and never\n * a half-built mirror).\n */\nexport const MAX_MIRROR_REPOS = 8\n\n/**\n * Whether `rel` is a legal repo-path key (0.6.3): `''` = the workspace root\n * repo; otherwise a relative forward-slash path with no traversal/absolute\n * shape and no dot segments. These keys ride into filesystem joins\n * (`<workspace>/<rel>`) and ledger maps — the same R4 paranoia as task ids.\n */\nexport function isValidRelRepoPath(rel: string): boolean {\n if (rel === '') return true\n if (rel.length === 0 || rel.length > 300) return false\n if (/^[A-Za-z]:[\\\\/]/.test(rel) || rel.startsWith('\\\\\\\\') || rel.startsWith('/')) return false\n const parts = rel.split('/')\n return parts.every(p =>\n p.length > 0 && p !== '.' && p !== '..' && !p.startsWith('.') && !/[\\\\:*?\"<>|]/.test(p))\n}\n\n/**\n * Per-repo isolation + evidence facts for one execution (0.6.3 multi-repo\n * mirror). Present only when the mirror covers MORE than the workspace root\n * repo alone (or has skipped repos) — a plain single-repo run keeps the\n * legacy flat fields above, untouched, so old ledgers/clients stay blind to\n * this. The root repo (when mirrored) ALSO dual-writes the flat fields.\n */\nexport type ExecutionRepoEvidence = {\n /** Repo path relative to the workspace ('' = the workspace root repo). */\n repo: string\n /** The task branch checked out in this repo's worktree. */\n branch: string\n /** Absolute path of the repo's worktree inside the task mirror. */\n worktreePath: string\n /** HEAD of the task branch before the execution started. */\n baseCommit?: string\n /** HEAD at settlement. */\n headCommit?: string\n /** Commits between baseCommit and headCommit (capped, newest first). */\n commits?: CommitInfo[]\n commitsTotal?: number\n /** Uncommitted changes at settlement (`status --porcelain` lines, capped). */\n dirtyFiles?: string[]\n dirtyFilesTotal?: number\n diffStat?: string\n changedFiles?: number\n}\n\n/**\n * The structured execution report an agent submits at handoff (0.4.0).\n * Commits/dirty/diff facts are host-collected git evidence — the report\n * covers the BUSINESS side the host cannot see.\n */\nexport type ExecutionReport = {\n /** What was done (1..2000 chars, required). */\n summary: string\n /** Files the agent changed (paths, ≤50 × 300 chars). */\n changedFiles: string[]\n /** How the work was self-verified (≤50 × 300 chars). */\n checks: string[]\n /** Produced artifacts worth reviewing (≤30 × 300 chars). */\n artifacts: string[]\n /** Known remaining risks / follow-ups (≤2000 chars, '' allowed). */\n risk: string\n}\n\n/** One Definition-of-Done checklist item (0.4.0). */\nexport type ChecklistItem = {\n id: string\n /** What must be true for acceptance (1..200 chars). */\n text: string\n checked: boolean\n /** Who checked it: an agent session id, or 'user' for GUI toggles. */\n checkedBy?: string\n /** When it was checked (epoch ms). */\n checkedAt?: number\n /** Evidence note attached when checking (≤400 chars). */\n note?: string\n}\n\n/** One execution attempt of a task. */\nexport type ExecutionRecord = {\n id: string\n /** The session this execution ran in; set once the session is really started. */\n sessionId?: string\n /** Effective configuration of a scheduled session; used only for compatible reuse. */\n sessionReuseKey?: string\n /** Trigger: manual button or the host scheduler. */\n trigger: 'manual' | 'scheduled'\n startedAt?: number\n /** Most recent observed session activity, throttled by the host. */\n lastActivityAt?: number\n endedAt?: number\n outcome: 'running' | 'succeeded' | 'failed' | 'cancelled'\n error?: string\n /** Code isolation actually used (`none` also covers degraded worktree runs). */\n isolation?: IsolationMode\n /** Why worktree isolation degraded to running in the original directory. */\n isolationNote?: string\n /** The task branch this execution worked on (worktree runs only). */\n branch?: string\n /** Absolute path of the dedicated worktree (worktree runs only). */\n worktreePath?: string\n /** HEAD of the task branch before the execution started. */\n baseCommit?: string\n /** HEAD at settlement. */\n headCommit?: string\n /** Commits between baseCommit and headCommit (hash + subject; capped at 50, newest first). */\n commits?: CommitInfo[]\n /** Total commits before the evidence cap (equals commits.length when under it). */\n commitsTotal?: number\n /** Uncommitted changes present at settlement (`git status --porcelain` lines; capped at 100). */\n dirtyFiles?: string[]\n /** Total uncommitted lines before the evidence cap. */\n dirtyFilesTotal?: number\n /** Aggregate diff stat between baseCommit and headCommit. */\n diffStat?: string\n /** How many files differ between baseCommit and headCommit. */\n changedFiles?: number\n /** Per-repo facts of a multi-repo mirror run (0.6.3; absent on single-repo runs). */\n repos?: ExecutionRepoEvidence[]\n /** The agent's structured report, submitted via taskboard_execution_report. */\n report?: ExecutionReport\n}\n\n/** The per-model override a task may carry; absent = session default model. */\nexport type TaskModel = {\n provider: string\n model: string\n /** Thinking intensity / reasoning effort (optional; e.g. 'low', 'medium', 'high', 'none'). */\n reasoningEffort?: string\n}\n\n/** One task on the board. */\nexport type TaskRecord = {\n id: string\n title: string\n description: string\n /** Extra execution prompt; the execution session receives title+description+prompt. */\n prompt: string\n /** Owning project: a DSH workspace id. */\n workspaceId: string\n urgency: Urgency\n status: TaskStatus\n /** Horizontal marker: work cannot continue right now (any non-terminal status). */\n blocked: boolean\n execution: ExecutionConfig\n model?: TaskModel\n /** Code isolation for executions (omitted = the worktree default; see {@link IsolationMode}). */\n isolation?: IsolationMode\n /**\n * The agent preset execution sessions are composed from (omitted = the\n * deployment default preset). Recorded on the session header and mounted\n * via the presets service at creation — this is what hands the session its\n * tool set. Editable any time (each run composes fresh).\n */\n presetId?: string\n /**\n * Execution permission preset (0.5.5; see {@link PermissionMode}).\n */\n permission?: PermissionMode\n /**\n * Definition-of-Done acceptance checklist (0.4.0). Agents may append items\n * and check/uncheck them (with evidence); the GUI may edit the whole list.\n * Unchecked items highlight at review time; done stays user-only.\n */\n checklist?: ChecklistItem[]\n /**\n * The task branch fixed at the FIRST worktree creation (`task/<标题>+<taskId>`).\n * Renaming the task afterwards never changes it (history preservation).\n * Multi-repo mirror runs (0.6.3): this stays the WORKSPACE ROOT repo's\n * branch; every nested repo pins its own under {@link branches}.\n */\n branch?: string\n /**\n * Per-repo task branches of a multi-repo mirror task (0.6.3): repo path\n * relative to the workspace (never `''` — the root uses {@link branch})\n * → branch name. Pinned at the repo's FIRST successful worktree creation,\n * same rename-proof semantics as {@link branch}.\n */\n branches?: Record<string, string>\n /**\n * The session currently holding the in-progress claim (explicit claim or a\n * live execution). Present only while `status === 'in_progress'`: any move\n * out of in_progress releases it. `updatedBy` is audit-only — user edits no\n * longer erase the holder.\n */\n claimedBy?: string\n /** When the current holder claimed the task (epoch ms). */\n claimedAt?: number\n version: number\n createdAt: number\n updatedAt: number\n createdBy: Actor\n updatedBy: Actor\n comments: CommentRecord[]\n executions: ExecutionRecord[]\n /** How many older execution records were pruned by the retention cap. */\n executionsPruned?: number\n /**\n * Id of the task this card continues (0.7.x periodic execution): when a\n * PERIODIC scheduled task succeeds, the finished card moves to in_review\n * and a fresh todo card carrying the cron is minted to keep the cycle\n * going. Absent on originally created tasks.\n */\n spawnedFrom?: string\n /** Soft-delete marker set by agent `taskboard_delete`; user confirms the purge. */\n trashedAt?: number\n}\n\n/** Retention cap: how many execution records each task keeps (oldest pruned). */\nexport const MAX_EXECUTIONS = 20\n\n/**\n * Enforce the execution-record retention cap on one task (in place): keep the\n * newest {@link MAX_EXECUTIONS} records, count the dropped ones in\n * `executionsPruned`. Running records are always the newest, never dropped.\n * @param task - the task to prune.\n */\nexport function pruneExecutions(task: TaskRecord): void {\n if (task.executions.length <= MAX_EXECUTIONS) return\n const dropped = task.executions.length - MAX_EXECUTIONS\n task.executions = task.executions.slice(-MAX_EXECUTIONS)\n task.executionsPruned = (task.executionsPruned ?? 0) + dropped\n}\n\n/**\n * Mint the successor card of a PERIODIC scheduled task that just succeeded\n * (0.7.x): the finished card goes to in_review for acceptance while this\n * fresh todo card carries the cron onward, keeping the cycle alive. The\n * next run is recomputed from `now` (no compensating catch-up burst).\n * Pure: the caller pushes the returned record into the ledger.\n * @param source - the finished periodic task (still carrying its cron).\n * @param prevExecutionId - id of the execution that just succeeded.\n * @param now - current epoch ms.\n * @returns the successor task record.\n */\nexport function spawnNextCycle(source: TaskRecord, prevExecutionId: string | undefined, now: number): TaskRecord {\n const cron = source.execution.cron\n if (cron === undefined) throw new Error('spawnNextCycle: source task has no cron')\n const match = parseCron(cron)\n const next = match === null ? undefined : nextCronTime(match, now) ?? undefined\n if (next === undefined) throw new Error('spawnNextCycle: cron has no upcoming match within 4 years')\n return {\n id: newTaskId(),\n title: source.title,\n description: source.description,\n prompt: source.prompt,\n workspaceId: source.workspaceId,\n urgency: source.urgency,\n status: 'todo',\n blocked: false,\n execution: { mode: 'scheduled', cron, nextRunAt: next, periodicCompletion: source.execution.periodicCompletion ?? DEFAULT_PERIODIC_COMPLETION,\n ...(source.execution.reuseSessionId !== undefined ? { reuseSessionId: source.execution.reuseSessionId } : {}),\n ...(source.execution.sessionReuseMode !== undefined ? { sessionReuseMode: source.execution.sessionReuseMode } : {}),\n ...(source.execution.autoReuseSessionId !== undefined ? { autoReuseSessionId: source.execution.autoReuseSessionId } : {}),\n ...(source.execution.autoReuseSessionKey !== undefined ? { autoReuseSessionKey: source.execution.autoReuseSessionKey } : {}) },\n ...(source.model !== undefined ? { model: structuredClone(source.model) } : {}),\n ...(source.isolation !== undefined ? { isolation: source.isolation } : {}),\n ...(source.presetId !== undefined ? { presetId: source.presetId } : {}),\n ...(source.permission !== undefined ? { permission: source.permission } : {}),\n ...(source.checklist !== undefined\n ? { checklist: source.checklist.map(item => ({ ...item, checked: false, checkedBy: undefined, checkedAt: undefined, note: undefined })) }\n : {}),\n ...(source.branch !== undefined ? { branch: source.branch } : {}),\n ...(source.branches !== undefined ? { branches: { ...source.branches } } : {}),\n spawnedFrom: source.id,\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: { kind: 'system' },\n updatedBy: { kind: 'system' },\n comments: [{\n id: newCommentId(),\n body: normalizeBody(`[系统] 定期任务上一轮执行完毕(执行 ${prevExecutionId ?? '未知'}),本卡承接定时继续下一轮;上一轮成果见 ${source.id} 的待验收。`),\n systemKey: 'sys.spawnedFrom',\n systemParams: { sourceId: source.id, executionId: prevExecutionId ?? '' },\n version: 1,\n createdAt: now,\n }],\n executions: [],\n }\n}\n\n/** The whole durable ledger. */\nexport type TaskLedger = {\n schemaVersion: number\n /** Global monotonic revision; every mutation bumps it. */\n revision: number\n tasks: TaskRecord[]\n /** Board-level settings (0.5.0); absent on ledgers never touched by 设置. */\n settings?: BoardSettings\n}\n\n/** Current ledger format version. */\nexport const LEDGER_SCHEMA_VERSION = 1\n\n/** An empty ledger. */\nexport function emptyLedger(): TaskLedger {\n return { schemaVersion: LEDGER_SCHEMA_VERSION, revision: 0, tasks: [] }\n}\n\n// ---------------------------------------------------------------------------\n// ids\n// ---------------------------------------------------------------------------\n\n/** Random base36 suffix. */\nfunction suffix(): string {\n return Math.random().toString(36).slice(2, 8)\n}\n\n/**\n * Legal task id charset (R4): `t-<base36>-<base36>` from {@link newTaskId},\n * and the ONLY shape accepted from the outside (import) or used to build\n * filesystem paths (worktree dirs). Ids ride into `join(ws, '.dsh-worktrees',\n * id)` — a lax charset here is an arbitrary-directory delete primitive.\n */\nexport function isValidTaskId(id: string): boolean {\n return /^[A-Za-z0-9][A-Za-z0-9_-]{0,99}$/.test(id)\n}\n\n/** Mint a task id. */\nexport function newTaskId(): string {\n return `t-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint a comment id. */\nexport function newCommentId(): string {\n return `c-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint a checklist item id. */\nexport function newChecklistItemId(): string {\n return `k-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint an execution id. */\nexport function newExecutionId(): string {\n return `e-${Date.now().toString(36)}-${suffix()}`\n}\n\n// ---------------------------------------------------------------------------\n// validation helpers (input shaping for tools and routes)\n// ---------------------------------------------------------------------------\n\n/**\n * Validate and normalize a title: trimmed, 1..200 chars.\n * @param raw - the raw input.\n * @returns the normalized title.\n * @throws when empty or too long.\n */\nexport function normalizeTitle(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > 200) {\n throw new Error('title must be 1..200 characters')\n }\n return t\n}\n\n/**\n * Validate a task prompt: trimmed, at most 8000 chars; empty becomes ''.\n * @param raw - the raw input.\n */\nexport function normalizePrompt(raw: string | undefined): string {\n const t = (raw ?? '').trim()\n if (t.length > 8000) throw new Error('prompt must be at most 8000 characters')\n return t\n}\n\n/**\n * Validate and normalize a comment body: trimmed, 1..4000 chars.\n * @param raw - the raw input.\n */\nexport function normalizeBody(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > 4000) {\n throw new Error('comment body must be 1..4000 characters')\n }\n return t\n}\n\n/**\n * Validate an urgency value.\n * @param raw - the raw input.\n */\nexport function asUrgency(raw: string): Urgency {\n if (!URGENCIES.includes(raw as Urgency)) {\n throw new Error(`urgency must be one of: ${URGENCIES.join(', ')}`)\n }\n return raw as Urgency\n}\n\n/**\n * Validate a status value.\n * @param raw - the raw input.\n */\nexport function asStatus(raw: string): TaskStatus {\n if (!ALL_STATUSES.includes(raw as TaskStatus)) {\n throw new Error(`status must be one of: ${ALL_STATUSES.join(', ')}`)\n }\n return raw as TaskStatus\n}\n\n/**\n * Parse a raw runAt input: epoch ms number or ISO date string → epoch ms.\n * @param raw - untyped runAt value.\n * @returns the epoch ms, or undefined when absent.\n */\nfunction normalizeRunAt(raw: unknown): number | undefined {\n if (raw === undefined || raw === null) return undefined\n if (typeof raw === 'number' && Number.isFinite(raw)) return Math.trunc(raw)\n if (typeof raw === 'string') {\n const t = Date.parse(raw)\n if (Number.isNaN(t)) throw new Error('execution.runAt is not a valid time (epoch ms or ISO string)')\n return t\n }\n throw new Error('execution.runAt must be an epoch ms number or an ISO date string')\n}\n\n/**\n * Validate an execution config request from raw tool/route input.\n * `scheduled` requires either a valid cron (periodic, 定期执行) or a runAt\n * instant (one-shot, 定时执行); the two are mutually exclusive. A cron's\n * first `nextRunAt` is computed from `now`.\n * @param raw - raw execution input ({@link ExecutionConfig} fields, untyped).\n * @param now - current epoch ms.\n * @param opts - `allowPastRunAt` lets the import path keep a historical\n * one-shot instant instead of rejecting it.\n * @returns the normalized config.\n */\nexport function normalizeExecution(\n raw: { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown },\n now: number,\n opts?: { allowPastRunAt?: boolean },\n): ExecutionConfig {\n const mode = raw.mode ?? 'claim'\n if (mode !== 'claim' && mode !== 'scheduled') {\n throw new Error(\"execution.mode must be 'claim' or 'scheduled'\")\n }\n if (mode === 'claim') {\n if (raw.periodicCompletion !== undefined) throw new Error('execution.periodicCompletion requires a cron schedule')\n if (raw.reuseSessionId !== undefined) throw new Error('execution.reuseSessionId requires a scheduled task')\n if (raw.sessionReuseMode !== undefined) throw new Error('execution.sessionReuseMode requires a scheduled task')\n return { mode }\n }\n if (raw.reuseSessionId !== undefined && (typeof raw.reuseSessionId !== 'string' || raw.reuseSessionId.length === 0\n || raw.reuseSessionId.length > 256 || raw.reuseSessionId.trim() !== raw.reuseSessionId || /[\\x00-\\x1f\\x7f]/.test(raw.reuseSessionId))) {\n throw new Error('execution.reuseSessionId must be a valid session id')\n }\n const reuseSession = raw.reuseSessionId === undefined ? {} : { reuseSessionId: raw.reuseSessionId as string }\n if (raw.sessionReuseMode !== undefined && raw.sessionReuseMode !== 'fresh' && raw.sessionReuseMode !== 'reuse') {\n throw new Error(\"execution.sessionReuseMode must be 'fresh' or 'reuse'\")\n }\n const sessionReuseMode = raw.sessionReuseMode === undefined ? {} : { sessionReuseMode: raw.sessionReuseMode as ScheduledSessionReuseMode }\n const runAt = normalizeRunAt(raw.runAt)\n const cron = (raw.cron ?? '').trim()\n if (cron.length > 0 && runAt !== undefined) {\n throw new Error('execution: cron and runAt are mutually exclusive (periodic vs one-shot)')\n }\n if (runAt !== undefined) {\n if (raw.periodicCompletion !== undefined) throw new Error('execution.periodicCompletion requires a cron schedule')\n if (raw.sessionReuseMode !== undefined) throw new Error('execution.sessionReuseMode requires a cron schedule')\n if (!opts?.allowPastRunAt && runAt <= now) throw new Error('execution.runAt must be in the future')\n return { mode, runAt, nextRunAt: runAt, ...reuseSession }\n }\n const match = parseCron(cron)\n if (match === null) throw new Error('execution.cron is not a valid 5-field cron expression')\n const next = nextCronTime(match, now)\n if (next === null) throw new Error('execution.cron never matches within 4 years')\n const periodicCompletion = raw.periodicCompletion ?? DEFAULT_PERIODIC_COMPLETION\n if (periodicCompletion !== 'rearm' && periodicCompletion !== 'spawn') {\n throw new Error(\"execution.periodicCompletion must be 'rearm' or 'spawn'\")\n }\n return { mode, cron, nextRunAt: next, periodicCompletion, ...reuseSession, ...sessionReuseMode }\n}\n\n/**\n * The effective prompt of a task: title+description, with the explicit\n * prompt appended when set — title+description+prompt.\n * @param task - the task.\n */\nexport function effectivePrompt(task: TaskRecord): string {\n const head = task.title\n const body = task.description.length > 0 ? `${head}\\n\\n${task.description}` : head\n return task.prompt.length > 0 ? `${body}\\n\\n${task.prompt}` : body\n}\n\n/**\n * Whether the task is currently claimed by a session (running state).\n * @param task - the task.\n */\nexport function isClaimedBy(task: TaskRecord): string | undefined {\n return task.status === 'in_progress' && task.claimedBy !== undefined ? task.claimedBy : undefined\n}\n\n/**\n * Maintain the explicit claim fields around a status change: entering\n * in_progress under a session records the holder (an execution-start or an\n * agent claim); every move out of in_progress releases the claim (handoff,\n * give-back, cancel). A user-driven move into in_progress records no holder —\n * no session works on it yet.\n * @param task - the task being written (mutated in place).\n * @param to - the target status.\n * @param now - current epoch ms.\n * @param holder - the session id claiming the task, when applicable.\n */\nexport function syncClaim(task: TaskRecord, to: TaskStatus, now: number, holder?: string): void {\n if (to !== 'in_progress') {\n delete task.claimedBy\n delete task.claimedAt\n } else if (holder !== undefined) {\n task.claimedBy = holder\n task.claimedAt = now\n }\n}\n\n/**\n * Collect unique execution session IDs associated with a task:\n * - executions with a non-empty `sessionId`\n * Creator and claim sessions may serve other tasks and are never included.\n * @param task - the task record to inspect.\n * @returns an array of distinct session IDs in stable discovery order.\n */\nexport function taskAssociatedSessionIds(task: TaskRecord): string[] {\n const seen = new Set<string>()\n const result: string[] = []\n const push = (raw: unknown) => {\n if (typeof raw === 'string') {\n const trimmed = raw.trim()\n if (trimmed.length > 0 && !seen.has(trimmed)) {\n seen.add(trimmed)\n result.push(trimmed)\n }\n }\n }\n\n if (Array.isArray(task.executions)) {\n for (const ex of task.executions) {\n if (ex !== null && typeof ex === 'object') {\n push((ex as { sessionId?: unknown }).sessionId)\n }\n }\n }\n return result\n}\n\n/**\n * Validate and normalize a pinned model: `{ provider, model, reasoningEffort? }`,\n * provider and model must be non-empty trimmed strings.\n * @param raw - the raw input.\n * @returns the normalized model.\n * @throws when the shape or the fields are invalid.\n */\nexport function normalizeModel(raw: unknown): TaskModel {\n if (typeof raw !== 'object' || raw === null) {\n throw new Error('model must be { provider: string, model: string }')\n }\n const { provider, model, reasoningEffort } = raw as { provider?: unknown; model?: unknown; reasoningEffort?: unknown }\n if (typeof provider !== 'string' || typeof model !== 'string') {\n throw new Error('model must be { provider: string, model: string }')\n }\n const p = provider.trim()\n const m = model.trim()\n if (p.length === 0 || m.length === 0) {\n throw new Error('model.provider and model.model must be non-empty strings')\n }\n const eff = typeof reasoningEffort === 'string' && reasoningEffort.trim().length > 0 ? reasoningEffort.trim() : undefined\n return { provider: p, model: m, ...(eff !== undefined ? { reasoningEffort: eff } : {}) }\n}\n\n// ---------------------------------------------------------------------------\n// checklist + report validation (0.4.0)\n// ---------------------------------------------------------------------------\n\n/** Checklist size cap per task. */\nexport const MAX_CHECKLIST_ITEMS = 30\n\n/** Checklist item text cap (chars). */\nexport const MAX_CHECKLIST_TEXT = 200\n\n/**\n * Validate and normalize one checklist text line: trimmed, 1..200 chars.\n * @param raw - the raw text.\n * @throws when empty or too long.\n */\nexport function normalizeChecklistText(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > MAX_CHECKLIST_TEXT) {\n throw new Error(`checklist item text must be 1..${MAX_CHECKLIST_TEXT} characters`)\n }\n return t\n}\n\n/**\n * Build a fresh unchecked checklist from plain text lines (create route /\n * templates / tool adds).\n * @param texts - the item texts (validated individually).\n */\nexport function checklistFromTexts(texts: readonly string[]): ChecklistItem[] {\n const items = texts.map(text => ({ id: newChecklistItemId(), text: normalizeChecklistText(text), checked: false }))\n if (items.length > MAX_CHECKLIST_ITEMS) {\n throw new Error(`checklist may hold at most ${MAX_CHECKLIST_ITEMS} items`)\n }\n return items\n}\n\n/**\n * Validate and normalize a full checklist array (GUI update route, import):\n * missing ids are minted, text is checked, checked flags must be booleans,\n * checkedBy/checkedAt are kept only on checked items.\n * @param raw - untyped array from the wire.\n * @throws with a readable reason on any invalid entry.\n */\nexport function normalizeChecklist(raw: unknown): ChecklistItem[] {\n if (!Array.isArray(raw)) throw new Error('checklist must be an array')\n if (raw.length > MAX_CHECKLIST_ITEMS) {\n throw new Error(`checklist may hold at most ${MAX_CHECKLIST_ITEMS} items`)\n }\n return raw.map((entry): ChecklistItem => {\n if (typeof entry !== 'object' || entry === null) throw new Error('checklist item must be an object')\n const e = entry as Record<string, unknown>\n const text = normalizeChecklistText(typeof e.text === 'string' ? e.text : '')\n const id = typeof e.id === 'string' && e.id.trim().length > 0 ? e.id.trim() : newChecklistItemId()\n const checked = e.checked === true\n const checkedBy = typeof e.checkedBy === 'string' ? e.checkedBy.trim().slice(0, 100) : undefined\n const checkedAt = typeof e.checkedAt === 'number' && Number.isFinite(e.checkedAt) ? e.checkedAt : undefined\n const note = typeof e.note === 'string' && e.note.trim().length > 0 ? e.note.trim().slice(0, 400) : undefined\n if (!checked) return { id, text, checked: false }\n return {\n id,\n text,\n checked: true,\n ...(checkedBy !== undefined && checkedBy.length > 0 ? { checkedBy } : {}),\n ...(checkedAt !== undefined ? { checkedAt } : {}),\n ...(note !== undefined ? { note } : {}),\n }\n })\n}\n\n/** Checklist progress: how many items are checked (absent checklist → 0/0). */\nexport function checklistProgress(task: Pick<TaskRecord, 'checklist'>): { done: number; total: number } {\n const items = task.checklist ?? []\n return { done: items.filter(i => i.checked).length, total: items.length }\n}\n\n/** Report string-list caps. */\nconst REPORT_LIST_CAPS = { changedFiles: 50, checks: 50, artifacts: 30 } as const\n\n/** Per-entry cap for report lists (chars). */\nconst REPORT_ENTRY_MAX = 300\n\n/** Validate one report string list: strings trimmed 1..300 chars. */\nfunction normalizeReportList(raw: unknown, field: keyof typeof REPORT_LIST_CAPS): string[] {\n if (raw === undefined) return []\n if (!Array.isArray(raw)) throw new Error(`report.${field} must be an array of strings`)\n const out = raw.map(entry => {\n if (typeof entry !== 'string') throw new Error(`report.${field} must be an array of strings`)\n const t = entry.trim()\n if (t.length === 0 || t.length > REPORT_ENTRY_MAX) {\n throw new Error(`report.${field} entries must be 1..${REPORT_ENTRY_MAX} characters`)\n }\n return t\n })\n if (out.length > REPORT_LIST_CAPS[field]) {\n throw new Error(`report.${field} may hold at most ${REPORT_LIST_CAPS[field]} entries`)\n }\n return out\n}\n\n/**\n * Validate and normalize a structured execution report.\n * @param raw - untyped tool/route input.\n * @throws with a readable reason on any invalid field.\n */\nexport function normalizeExecutionReport(raw: unknown): ExecutionReport {\n if (typeof raw !== 'object' || raw === null) throw new Error('report must be an object')\n const e = raw as Record<string, unknown>\n const summary = typeof e.summary === 'string' ? e.summary.trim() : ''\n if (summary.length === 0 || summary.length > 2000) {\n throw new Error('report.summary must be 1..2000 characters')\n }\n const risk = typeof e.risk === 'string' ? e.risk.trim().slice(0, 2000) : ''\n return {\n summary,\n changedFiles: normalizeReportList(e.changedFiles, 'changedFiles'),\n checks: normalizeReportList(e.checks, 'checks'),\n artifacts: normalizeReportList(e.artifacts, 'artifacts'),\n risk,\n }\n}\n\n// ---------------------------------------------------------------------------\n// ledger import validation (0.4.0)\n// ---------------------------------------------------------------------------\n\n/** Result classifying every task in an import file against the live ledger. */\nexport type ImportPlan = {\n /** Structurally valid tasks whose ids are new (merge adds them). */\n create: TaskRecord[]\n /** Structurally valid tasks whose ids already exist (merge replaces them). */\n overwrite: TaskRecord[]\n /** Invalid entries with a human-readable reason (never imported). */\n invalid: Array<{ id?: string; reason: string }>\n /** The file's board settings (0.5.0); replace-mode swaps them, merge keeps the live ones. */\n settings?: BoardSettings\n}\n\n/** One unknown-value read helper: string fields with defaults. */\nfunction strOr(raw: Record<string, unknown>, key: string, fallback: string): string {\n const v = raw[key]\n return typeof v === 'string' ? v : fallback\n}\n\n/** One unknown-value read helper: finite numbers with defaults. */\nfunction numOr(raw: Record<string, unknown>, key: string, fallback: number): number {\n const v = raw[key]\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback\n}\n\n/** Evidence caps for imported per-repo facts (aligns the host's collect caps). */\nconst IMPORT_COMMIT_CAP = 50\nconst IMPORT_DIRTY_CAP = 100\n\n/**\n * Sanitize an imported per-task branches map (0.6.3): legal repo keys only,\n * non-empty branch strings, capped at {@link MAX_MIRROR_REPOS} entries.\n * @returns undefined when nothing legal remains.\n */\nexport function normalizeBranchesMap(raw: unknown): Record<string, string> | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n if (Object.keys(out).length >= MAX_MIRROR_REPOS) break\n if (typeof value !== 'string' || value.trim().length === 0 || value.length > 200) continue\n if (key === '' || !isValidRelRepoPath(key)) continue\n out[key] = value.trim()\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\n/**\n * Sanitize ONE imported per-repo evidence entry (0.6.3): rebuilds the record\n * field by field, capping evidence arrays like the host's own collection.\n * @returns undefined for a structurally illegal entry (dropped, not fatal).\n */\nexport function normalizeRepoEvidence(raw: unknown): ExecutionRepoEvidence | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const e = raw as Record<string, unknown>\n if (typeof e.repo !== 'string' || !isValidRelRepoPath(e.repo)) return undefined\n if (typeof e.branch !== 'string' || e.branch.length === 0 || e.branch.length > 200) return undefined\n if (typeof e.worktreePath !== 'string' || e.worktreePath.length === 0 || e.worktreePath.length > 1000) return undefined\n const commits = Array.isArray(e.commits)\n ? e.commits.filter((c): c is CommitInfo =>\n typeof c === 'object' && c !== null && typeof (c as CommitInfo).hash === 'string'\n && typeof (c as CommitInfo).subject === 'string').slice(0, IMPORT_COMMIT_CAP)\n : undefined\n const dirtyFiles = Array.isArray(e.dirtyFiles)\n ? e.dirtyFiles.filter((l): l is string => typeof l === 'string').slice(0, IMPORT_DIRTY_CAP)\n : undefined\n return {\n repo: e.repo,\n branch: e.branch,\n worktreePath: e.worktreePath,\n ...(typeof e.baseCommit === 'string' ? { baseCommit: e.baseCommit.slice(0, 100) } : {}),\n ...(typeof e.headCommit === 'string' ? { headCommit: e.headCommit.slice(0, 100) } : {}),\n ...(commits !== undefined && commits.length > 0 ? { commits } : {}),\n ...(typeof e.commitsTotal === 'number' && Number.isFinite(e.commitsTotal) ? { commitsTotal: e.commitsTotal } : {}),\n ...(dirtyFiles !== undefined && dirtyFiles.length > 0 ? { dirtyFiles } : {}),\n ...(typeof e.dirtyFilesTotal === 'number' && Number.isFinite(e.dirtyFilesTotal) ? { dirtyFilesTotal: e.dirtyFilesTotal } : {}),\n ...(typeof e.diffStat === 'string' ? { diffStat: e.diffStat.slice(0, 500) } : {}),\n ...(typeof e.changedFiles === 'number' && Number.isFinite(e.changedFiles) ? { changedFiles: e.changedFiles } : {}),\n }\n}\n\n/**\n * Validate ONE imported task record (pure): rebuilds it field by field with\n * the normal validators, minting missing ids and re-arming cron. Executions\n * left `running` by the exporting machine are marked failed — their\n * settlement watchers died there and can never settle here.\n * @param raw - the untyped record.\n * @param now - current epoch ms (defaults for timestamps).\n * @returns the rebuilt record, or a rejection reason.\n */\nexport function validateImportedTask(raw: unknown, now: number): { ok: true; task: TaskRecord } | { ok: false; reason: string } {\n if (typeof raw !== 'object' || raw === null) return { ok: false, reason: 'not an object' }\n const e = raw as Record<string, unknown>\n const id = typeof e.id === 'string' ? e.id.trim() : ''\n const fail = (reason: string): { ok: false; reason: string } => ({ ok: false, reason })\n // R4①: length alone let traversal-shaped ids (`../../x`, `..\\..\\x`) into\n // the ledger; the charset gate is the primary defense for every downstream\n // filesystem use of a task id.\n if (!isValidTaskId(id)) return fail('missing/invalid id (must match ^[A-Za-z0-9][A-Za-z0-9_-]{0,99}$)')\n try {\n const rawExecution = typeof e.execution === 'object' && e.execution !== null\n ? e.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown; autoReuseSessionId?: unknown; autoReuseSessionKey?: unknown; queuedRunAt?: unknown; queuedAt?: unknown; dispatchingRunAt?: unknown }\n : {}\n const execution = normalizeExecution(\n rawExecution,\n now,\n { allowPastRunAt: true },\n )\n // These pointers are host-owned runtime state. Retain only well-formed\n // values from an exported ledger; a stale pointer merely falls back to a\n // fresh conversation when the next scheduled run starts.\n if (execution.sessionReuseMode !== 'fresh' && typeof rawExecution.autoReuseSessionId === 'string'\n && rawExecution.autoReuseSessionId.length > 0 && rawExecution.autoReuseSessionId.length <= 256) {\n execution.autoReuseSessionId = rawExecution.autoReuseSessionId\n if (typeof rawExecution.autoReuseSessionKey === 'string' && rawExecution.autoReuseSessionKey.length <= 1024) {\n execution.autoReuseSessionKey = rawExecution.autoReuseSessionKey\n }\n }\n // Queue markers are scheduler-owned, but must survive export/import: an\n // online-and-queued window is not equivalent to an offline missed window.\n const queuedWindow = typeof rawExecution.queuedRunAt === 'number' && Number.isFinite(rawExecution.queuedRunAt)\n ? rawExecution.queuedRunAt\n : typeof rawExecution.dispatchingRunAt === 'number' && Number.isFinite(rawExecution.dispatchingRunAt)\n ? rawExecution.dispatchingRunAt\n : undefined\n if (execution.mode === 'scheduled' && queuedWindow !== undefined && typeof rawExecution.queuedAt === 'number'\n && Number.isFinite(rawExecution.queuedAt)) {\n execution.queuedRunAt = queuedWindow\n execution.queuedAt = rawExecution.queuedAt\n }\n const comments: CommentRecord[] = []\n if (Array.isArray(e.comments)) {\n for (const c of e.comments) {\n if (typeof c !== 'object' || c === null) return fail('invalid comment entry')\n const ce = c as Record<string, unknown>\n const body = typeof ce.body === 'string' ? ce.body : ''\n if (body.trim().length === 0 || body.length > 4000) return fail('invalid comment body')\n comments.push({\n id: typeof ce.id === 'string' && ce.id.length > 0 ? ce.id : newCommentId(),\n body,\n version: numOr(ce, 'version', 1),\n createdAt: numOr(ce, 'createdAt', now),\n ...(typeof ce.threadId === 'string' ? { threadId: ce.threadId } : {}),\n ...(typeof ce.systemKey === 'string' && /^sys\\.[A-Za-z0-9]+$/.test(ce.systemKey) && ce.systemKey.length <= 100\n ? {\n systemKey: ce.systemKey,\n ...(typeof ce.systemParams === 'object' && ce.systemParams !== null && !Array.isArray(ce.systemParams)\n ? { systemParams: Object.fromEntries(Object.entries(ce.systemParams).filter(([key, value]) => key.length <= 100 && typeof value === 'string' && value.length <= 4000).slice(0, 20)) as Record<string, string> }\n : {}),\n ...(Array.isArray(ce.systemRows)\n ? { systemRows: ce.systemRows.filter((row): row is SystemCommentRow => typeof row === 'object' && row !== null\n && typeof row.repo === 'string' && (row.repo === '' || isValidRelRepoPath(row.repo))\n && ['merged', 'noop', 'failed'].includes(row.outcome)\n && (row.error === undefined || typeof row.error === 'string'))\n .slice(0, MAX_MIRROR_REPOS).map(row => ({ repo: row.repo, outcome: row.outcome, ...(row.error !== undefined ? { error: row.error.slice(0, 4000) } : {}) })) }\n : {}),\n }\n : {}),\n })\n }\n } else return fail('comments must be an array')\n const executions: ExecutionRecord[] = []\n if (Array.isArray(e.executions)) {\n for (const x of e.executions) {\n if (typeof x !== 'object' || x === null) return fail('invalid execution entry')\n const xe = x as Record<string, unknown>\n const trigger = xe.trigger === 'scheduled' ? 'scheduled' : 'manual'\n const outcomeRaw = xe.outcome\n if (outcomeRaw !== 'running' && outcomeRaw !== 'succeeded' && outcomeRaw !== 'failed' && outcomeRaw !== 'cancelled') {\n return fail('invalid execution outcome')\n }\n // A running execution from the exporting machine can never settle\n // here — import it as failed with the reason recorded.\n const outcome = outcomeRaw === 'running' ? 'failed' as const : outcomeRaw\n executions.push({\n id: typeof xe.id === 'string' && xe.id.length > 0 ? xe.id : newExecutionId(),\n ...(typeof xe.sessionId === 'string' ? { sessionId: xe.sessionId } : {}),\n trigger,\n ...(typeof xe.startedAt === 'number' ? { startedAt: xe.startedAt } : {}),\n ...(typeof xe.lastActivityAt === 'number' ? { lastActivityAt: xe.lastActivityAt } : {}),\n ...(typeof xe.endedAt === 'number' ? { endedAt: xe.endedAt } : {}),\n outcome,\n ...(outcomeRaw === 'running' ? { error: 'imported while still running (settlement watcher died with the exporting host)' } : (typeof xe.error === 'string' ? { error: xe.error } : {})),\n ...(typeof xe.isolation === 'string' && (xe.isolation === 'worktree' || xe.isolation === 'none') ? { isolation: xe.isolation } : {}),\n ...(typeof xe.isolationNote === 'string' ? { isolationNote: xe.isolationNote } : {}),\n ...(typeof xe.branch === 'string' ? { branch: xe.branch } : {}),\n ...(typeof xe.worktreePath === 'string' ? { worktreePath: xe.worktreePath } : {}),\n ...(typeof xe.baseCommit === 'string' ? { baseCommit: xe.baseCommit } : {}),\n ...(typeof xe.headCommit === 'string' ? { headCommit: xe.headCommit } : {}),\n ...(Array.isArray(xe.commits) ? { commits: xe.commits.filter((c): c is CommitInfo =>\n typeof c === 'object' && c !== null && typeof (c as CommitInfo).hash === 'string' && typeof (c as CommitInfo).subject === 'string') } : {}),\n ...(typeof xe.commitsTotal === 'number' ? { commitsTotal: xe.commitsTotal } : {}),\n ...(Array.isArray(xe.dirtyFiles) ? { dirtyFiles: xe.dirtyFiles.filter((l): l is string => typeof l === 'string') } : {}),\n ...(typeof xe.dirtyFilesTotal === 'number' ? { dirtyFilesTotal: xe.dirtyFilesTotal } : {}),\n ...(typeof xe.diffStat === 'string' ? { diffStat: xe.diffStat } : {}),\n ...(typeof xe.changedFiles === 'number' ? { changedFiles: xe.changedFiles } : {}),\n ...(Array.isArray(xe.repos) ? { repos: xe.repos.slice(0, MAX_MIRROR_REPOS).map(normalizeRepoEvidence).filter((r): r is ExecutionRepoEvidence => r !== undefined) } : {}),\n ...(typeof xe.report === 'object' && xe.report !== null ? { report: normalizeExecutionReport(xe.report) } : {}),\n })\n }\n } else return fail('executions must be an array')\n const status = asStatus(strOr(e, 'status', 'todo'))\n const actorOf = (v: unknown): Actor => (typeof v === 'object' && v !== null && (v as Actor).kind === 'agent' && typeof (v as { sessionId?: unknown }).sessionId === 'string'\n ? { kind: 'agent', sessionId: (v as { sessionId: string }).sessionId }\n : { kind: 'user' })\n const branchesMap = normalizeBranchesMap(e.branches)\n const task: TaskRecord = {\n id,\n title: normalizeTitle(strOr(e, 'title', '')),\n description: strOr(e, 'description', '').trim(),\n prompt: normalizePrompt(strOr(e, 'prompt', '')),\n workspaceId: strOr(e, 'workspaceId', ''),\n urgency: asUrgency(strOr(e, 'urgency', 'normal')),\n status,\n blocked: e.blocked === true,\n execution,\n ...(typeof e.model === 'object' && e.model !== null ? { model: normalizeModel(e.model) } : {}),\n ...(typeof e.isolation === 'string' && (e.isolation === 'worktree' || e.isolation === 'none') ? { isolation: e.isolation } : {}),\n ...(typeof e.presetId === 'string' && e.presetId.trim().length > 0 ? { presetId: e.presetId.trim() } : {}),\n ...(typeof e.permission === 'string' ? { permission: asPermission(e.permission) } : {}),\n ...(Array.isArray(e.checklist) ? { checklist: normalizeChecklist(e.checklist) } : {}),\n ...(typeof e.branch === 'string' ? { branch: e.branch } : {}),\n ...(branchesMap !== undefined ? { branches: branchesMap } : {}),\n ...(status === 'in_progress' && typeof e.claimedBy === 'string' ? { claimedBy: e.claimedBy } : {}),\n ...(status === 'in_progress' && typeof e.claimedAt === 'number' ? { claimedAt: e.claimedAt } : {}),\n version: Math.max(1, Math.trunc(numOr(e, 'version', 1))),\n createdAt: numOr(e, 'createdAt', now),\n updatedAt: numOr(e, 'updatedAt', now),\n createdBy: actorOf(e.createdBy),\n updatedBy: actorOf(e.updatedBy),\n comments,\n executions,\n ...(typeof e.executionsPruned === 'number' ? { executionsPruned: e.executionsPruned } : {}),\n ...(typeof e.trashedAt === 'number' ? { trashedAt: e.trashedAt } : {}),\n }\n if (task.workspaceId.length === 0) return fail('missing workspaceId')\n return { ok: true, task }\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error))\n }\n}\n\n/**\n * Minimal structural check for ONE ledger record at load time (S11): unlike\n * {@link validateImportedTask} this REBUILDS NOTHING (cron state, ids and\n * timestamps must survive a load untouched) — it only rejects entries whose\n * shape would break downstream consumers, including the R4 id charset.\n * @param raw - the untyped record.\n */\nexport function isPlausibleTaskRecord(raw: unknown): boolean {\n if (typeof raw !== 'object' || raw === null) return false\n const t = raw as Record<string, unknown>\n return typeof t.id === 'string' && isValidTaskId(t.id)\n && typeof t.title === 'string' && t.title.length > 0\n && typeof t.workspaceId === 'string' && t.workspaceId.length > 0\n && ALL_STATUSES.includes(t.status as TaskStatus)\n && typeof t.version === 'number' && Number.isFinite(t.version) && t.version >= 1\n && Array.isArray(t.comments) && Array.isArray(t.executions)\n && typeof t.execution === 'object' && t.execution !== null\n && (t.execution as { mode?: unknown }).mode !== undefined\n}\n\n/**\n * Validate a whole imported ledger and classify its tasks against the live\n * one (pure). Duplicate ids INSIDE the file are invalid (first wins, later\n * copies reported); schemaVersion must match {@link LEDGER_SCHEMA_VERSION}.\n * @param raw - the parsed import file.\n * @param knownIds - live ledger task ids.\n * @param now - current epoch ms.\n * @throws when the file is not a ledger or the schemaVersion is unsupported.\n */\nexport function validateLedgerImport(raw: unknown, knownIds: ReadonlySet<string>, now: number): ImportPlan {\n if (typeof raw !== 'object' || raw === null) throw new Error('导入文件不是 JSON 对象')\n const e = raw as Record<string, unknown>\n if (e.schemaVersion !== LEDGER_SCHEMA_VERSION) {\n throw new Error(`不支持的 schemaVersion ${String(e.schemaVersion)}(当前支持 ${LEDGER_SCHEMA_VERSION})`)\n }\n if (!Array.isArray(e.tasks)) throw new Error('导入文件的 tasks 不是数组')\n const plan: ImportPlan = { create: [], overwrite: [], invalid: [], ...(e.settings !== undefined ? { settings: asBoardSettings(e.settings) } : {}) }\n const seen = new Set<string>()\n for (const entry of e.tasks) {\n const id = typeof (entry as { id?: unknown })?.id === 'string' ? (entry as { id: string }).id : undefined\n const result = validateImportedTask(entry, now)\n if (!result.ok) {\n plan.invalid.push({ ...(id !== undefined ? { id } : {}), reason: result.reason })\n continue\n }\n if (seen.has(result.task.id)) {\n plan.invalid.push({ id: result.task.id, reason: '文件内重复 id' })\n continue\n }\n seen.add(result.task.id)\n if (knownIds.has(result.task.id)) plan.overwrite.push(result.task)\n else plan.create.push(result.task)\n }\n return plan\n}\n\n/**\n * Compact list-projection of a task (token-friendly for `taskboard_list`).\n * @param task - the task.\n */\nexport type TaskSummary = {\n id: string\n title: string\n workspaceId: string\n urgency: Urgency\n status: TaskStatus\n blocked: boolean\n executionMode: ExecutionMode\n nextRunAt?: number\n model?: TaskModel\n permission?: PermissionMode\n version: number\n claimOwner?: string\n commentCount: number\n lastExecutionOutcome?: ExecutionRecord['outcome']\n /** Checklist progress (present only when the task has a checklist). */\n checklist?: { done: number; total: number }\n trashed: boolean\n}\n\n/**\n * Build the compact summary of a task.\n * @param task - the task.\n */\nexport function summarize(task: TaskRecord): TaskSummary {\n const last = task.executions.length > 0 ? task.executions[task.executions.length - 1] : undefined\n const checklist = task.checklist !== undefined && task.checklist.length > 0 ? checklistProgress(task) : undefined\n return {\n id: task.id,\n title: task.title,\n workspaceId: task.workspaceId,\n urgency: task.urgency,\n status: task.status,\n blocked: task.blocked,\n executionMode: task.execution.mode,\n nextRunAt: task.execution.nextRunAt,\n model: task.model,\n permission: task.permission,\n version: task.version,\n claimOwner: isClaimedBy(task),\n commentCount: task.comments.length,\n lastExecutionOutcome: last?.outcome,\n ...(checklist !== undefined ? { checklist } : {}),\n trashed: task.trashedAt !== undefined,\n }\n}\n"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"protocol.js","names":[],"sources":["../../src/shared/protocol.ts"],"sourcesContent":["/**\n * Task domain model, state machine, urgency classes, and cron math — the\n * framework-free core shared verbatim by the host half (tools, store, routes,\n * scheduler) and, from P2 on, the browser half (board view).\n *\n * Everything here is a pure function over plain data: no imports beyond the\n * standard library, no I/O, no globals. Tests drive it directly.\n *\n * @module dsh-taskboard/shared/protocol\n */\n\n// ---------------------------------------------------------------------------\n// Status vocabulary\n// ---------------------------------------------------------------------------\n\n/**\n * Task lifecycle states. Main board columns render `backlog → todo →\n * in_progress → in_review → done`; `canceled` and `archived` are secondary\n * states collected under an \"other tasks\" tab. `blocked` is NOT a status —\n * it is a horizontal marker any non-terminal state may carry.\n */\nexport type TaskStatus =\n | 'backlog'\n | 'todo'\n | 'in_progress'\n | 'in_review'\n | 'done'\n | 'canceled'\n | 'archived'\n\n/** Statuses shown as the five main board columns, in order. */\nexport const MAIN_STATUSES: readonly TaskStatus[] = [\n 'backlog',\n 'todo',\n 'in_progress',\n 'in_review',\n 'done',\n]\n\n/** Statuses collected under the secondary tab. */\nexport const SECONDARY_STATUSES: readonly TaskStatus[] = ['canceled', 'archived']\n\n/** Every valid status, main first. */\nexport const ALL_STATUSES: readonly TaskStatus[] = [...MAIN_STATUSES, ...SECONDARY_STATUSES]\n\n/**\n * Legal forward/sideways transitions. Anything not listed is rejected with\n * `invalid_transition`. `archived` is terminal.\n */\nconst TRANSITIONS: Readonly<Record<TaskStatus, readonly TaskStatus[]>> = {\n backlog: ['todo', 'canceled'],\n todo: ['in_progress', 'backlog', 'canceled'],\n in_progress: ['in_review', 'todo', 'canceled'],\n in_review: ['in_progress', 'todo', 'done', 'canceled'],\n done: ['archived', 'todo'],\n canceled: ['archived', 'todo'],\n archived: [],\n}\n\n/**\n * Whether a status move is legal per the state machine.\n * @param from - current status.\n * @param to - requested status.\n * @returns true when the transition is allowed.\n */\nexport function canTransition(from: TaskStatus, to: TaskStatus): boolean {\n return TRANSITIONS[from].includes(to)\n}\n\n/**\n * The claim move: the one transition that transfers ownership of a task to\n * the calling session. Guarded by the project (workspace) boundary in the\n * tool layer.\n */\nexport function isClaim(from: TaskStatus, to: TaskStatus): boolean {\n return from === 'todo' && to === 'in_progress'\n}\n\n/** Statuses a `done` move may depart from (user confirmation only). */\nexport function canCompleteFrom(from: TaskStatus): boolean {\n return from === 'in_review'\n}\n\n// ---------------------------------------------------------------------------\n// Urgency\n// ---------------------------------------------------------------------------\n\n/** Urgency classes with fixed UI colors. */\nexport type Urgency = 'urgent' | 'normal' | 'relaxed'\n\n/** All valid urgency values. */\nexport const URGENCIES: readonly Urgency[] = ['urgent', 'normal', 'relaxed']\n\n/** CSS color token per urgency: red / purple / blue. */\nexport const URGENCY_COLOR: Readonly<Record<Urgency, string>> = {\n urgent: '#e5484d',\n normal: '#8e4ec6',\n relaxed: '#3e63dd',\n}\n\n// ---------------------------------------------------------------------------\n// Execution\n// ---------------------------------------------------------------------------\n\n/**\n * Per-task code isolation mode (0.3.0).\n * - `worktree`: each execution runs in a fresh `git worktree` on a dedicated\n * task branch (`task/<标题>+<taskId>`) under `<workspace>/.dsh-worktrees/`.\n * - `none`: run in the workspace directory as before, zero git interaction.\n * Omitted = {@link DEFAULT_ISOLATION}; since 0.5.0 creation materializes the\n * board default onto the record (看板设置 → 执行隔离), and non-git projects\n * still auto-degrade at run time (the execution record carries an\n * `isolationNote` explaining why).\n */\nexport type IsolationMode = 'worktree' | 'none'\n\n/**\n * Factory-default isolation (0.5.0): 原目录执行. Applies when neither the\n * task record nor the board setting (`BoardSettings.defaultIsolation`)\n * says otherwise. Before 0.5.0 the implicit default was 'worktree'.\n */\nexport const DEFAULT_ISOLATION: IsolationMode = 'none'\n\n/** Factory default and safe UI/API range for concurrent task executions. */\nexport const DEFAULT_MAX_CONCURRENT = 3\nexport const MIN_MAX_CONCURRENT = 1\nexport const MAX_MAX_CONCURRENT = 100\n/** Factory default and safe UI/API range for offline missed-window handling. */\nexport const DEFAULT_SCHEDULE_MISSED_AFTER_MINUTES = 5\nexport const MIN_SCHEDULE_MISSED_AFTER_MINUTES = 1\nexport const MAX_SCHEDULE_MISSED_AFTER_MINUTES = 1_440\n/** A queued entry's optional shelf life. Zero deliberately preserves durable replay. */\nexport const DEFAULT_QUEUE_MAX_AGE_MINUTES = 0\nexport const MAX_QUEUE_MAX_AGE_MINUTES = 10_080\n/** Smallest user-selectable delay; zero explicitly disables dispatch throttling. */\nexport const MIN_DISPATCH_INTERVAL_MS = 0\n/** Default spacing between scheduler-created sessions. */\nexport const DEFAULT_DISPATCH_INTERVAL_MS = 1_000\nexport const MAX_DISPATCH_INTERVAL_MS = 60_000\n\n/** Validate an isolation value. */\nexport function asIsolation(raw: string): IsolationMode {\n if (raw !== 'worktree' && raw !== 'none') {\n throw new Error(\"isolation must be 'worktree' or 'none'\")\n }\n return raw\n}\n\n/** Resolve a task's effective isolation (omitted → the factory default). */\nexport function effectiveIsolation(task: Pick<TaskRecord, 'isolation'>): IsolationMode {\n return task.isolation === undefined ? DEFAULT_ISOLATION : task.isolation\n}\n\n/**\n * Execution permission preset (0.5.5). Matches DSH permission presets:\n * - 'workspace-write': 可写入工作区 (factory default)\n * - 'read-only': 仅可查看\n * - 'danger-full-access': 完全权限\n */\nexport type PermissionMode = 'workspace-write' | 'read-only' | 'danger-full-access'\n\n/** Factory default permission preset (0.5.5). */\nexport const DEFAULT_PERMISSION: PermissionMode = 'workspace-write'\n\n/** Canonical list of supported permission modes. */\nexport const ALL_PERMISSIONS: readonly PermissionMode[] = ['workspace-write', 'read-only', 'danger-full-access']\n\n/** Validate and normalize a permission string into a valid {@link PermissionMode}. */\nexport function asPermission(raw: unknown): PermissionMode {\n if (typeof raw !== 'string') return DEFAULT_PERMISSION\n const normalized = raw.trim()\n if (normalized === 'workspace-write' || normalized === 'workspaceWrite') return 'workspace-write'\n if (normalized === 'read-only' || normalized === 'readOnly') return 'read-only'\n if (normalized === 'danger-full-access' || normalized === 'fullAccess') return 'danger-full-access'\n throw new Error(\"permission must be 'workspace-write', 'read-only', or 'danger-full-access'\")\n}\n\n/**\n * Board-level settings persisted with the ledger (0.5.0). Only fields the\n * user explicitly set are present; absent fields follow factory defaults.\n */\nexport type BoardSettings = {\n /** Default code isolation applied when a NEW task is created without an explicit choice. */\n defaultIsolation?: IsolationMode\n /** Automatically capture external workspace sessions into the taskboard (default: false). */\n syncExternalSessions?: boolean\n /** Default permission preset applied when a NEW task is created without an explicit choice (0.5.5, default: 'workspace-write'). */\n defaultPermission?: PermissionMode\n /** Model and optional reasoning effort materialized onto NEW tasks; absent = deployment default. */\n defaultModel?: TaskModel\n /** Agent preset materialized onto NEW tasks; absent = deployment default. */\n defaultPresetId?: string\n /** Global cap for simultaneously running taskboard executions. */\n maxConcurrent?: number\n /** Only never-queued windows older than this are classified as offline misses. */\n scheduleMissedAfterMinutes?: number\n /** Drop an already queued window after this many minutes; zero keeps it indefinitely. */\n queueMaxAgeMinutes?: number\n /** Minimum delay between scheduled session starts; zero explicitly disables throttling. */\n dispatchIntervalMs?: number\n}\n\n/** Validate raw input into sanitized {@link BoardSettings} (unknown fields dropped). */\nexport function asBoardSettings(raw: unknown): BoardSettings {\n if (typeof raw !== 'object' || raw === null) {\n throw new Error('board settings must be an object')\n }\n const e = raw as Record<string, unknown>\n const out: BoardSettings = {}\n if (e.defaultIsolation !== undefined) {\n if (typeof e.defaultIsolation !== 'string') {\n throw new Error(\"defaultIsolation must be 'worktree' or 'none'\")\n }\n out.defaultIsolation = asIsolation(e.defaultIsolation)\n }\n if (e.syncExternalSessions !== undefined) {\n if (typeof e.syncExternalSessions !== 'boolean') {\n throw new Error('syncExternalSessions must be a boolean')\n }\n out.syncExternalSessions = e.syncExternalSessions\n }\n if (e.defaultPermission !== undefined) {\n out.defaultPermission = asPermission(e.defaultPermission)\n }\n if (e.defaultModel !== undefined) {\n out.defaultModel = normalizeModel(e.defaultModel)\n }\n if (e.defaultPresetId !== undefined) {\n if (typeof e.defaultPresetId !== 'string') throw new Error('defaultPresetId must be a string')\n const id = e.defaultPresetId.trim()\n if (id.length > 0) out.defaultPresetId = id\n }\n if (e.maxConcurrent !== undefined) {\n if (typeof e.maxConcurrent !== 'number' || !Number.isSafeInteger(e.maxConcurrent)\n || e.maxConcurrent < MIN_MAX_CONCURRENT || e.maxConcurrent > MAX_MAX_CONCURRENT) {\n throw new Error(`maxConcurrent must be an integer from ${MIN_MAX_CONCURRENT} to ${MAX_MAX_CONCURRENT}`)\n }\n out.maxConcurrent = e.maxConcurrent\n }\n if (e.scheduleMissedAfterMinutes !== undefined) {\n if (typeof e.scheduleMissedAfterMinutes !== 'number' || !Number.isSafeInteger(e.scheduleMissedAfterMinutes)\n || e.scheduleMissedAfterMinutes < MIN_SCHEDULE_MISSED_AFTER_MINUTES || e.scheduleMissedAfterMinutes > MAX_SCHEDULE_MISSED_AFTER_MINUTES) {\n throw new Error(`scheduleMissedAfterMinutes must be an integer from ${MIN_SCHEDULE_MISSED_AFTER_MINUTES} to ${MAX_SCHEDULE_MISSED_AFTER_MINUTES}`)\n }\n out.scheduleMissedAfterMinutes = e.scheduleMissedAfterMinutes\n }\n if (e.queueMaxAgeMinutes !== undefined) {\n if (typeof e.queueMaxAgeMinutes !== 'number' || !Number.isSafeInteger(e.queueMaxAgeMinutes)\n || e.queueMaxAgeMinutes < DEFAULT_QUEUE_MAX_AGE_MINUTES || e.queueMaxAgeMinutes > MAX_QUEUE_MAX_AGE_MINUTES) {\n throw new Error(`queueMaxAgeMinutes must be an integer from ${DEFAULT_QUEUE_MAX_AGE_MINUTES} to ${MAX_QUEUE_MAX_AGE_MINUTES}`)\n }\n out.queueMaxAgeMinutes = e.queueMaxAgeMinutes\n }\n if (e.dispatchIntervalMs !== undefined) {\n if (typeof e.dispatchIntervalMs !== 'number' || !Number.isSafeInteger(e.dispatchIntervalMs)\n || e.dispatchIntervalMs < MIN_DISPATCH_INTERVAL_MS || e.dispatchIntervalMs > MAX_DISPATCH_INTERVAL_MS) {\n throw new Error(`dispatchIntervalMs must be an integer from ${MIN_DISPATCH_INTERVAL_MS} to ${MAX_DISPATCH_INTERVAL_MS}`)\n }\n out.dispatchIntervalMs = e.dispatchIntervalMs\n }\n return out\n}\n\n/** The effective default isolation for NEW tasks (board setting → factory default). */\nexport function defaultIsolationOf(settings?: BoardSettings): IsolationMode {\n return settings?.defaultIsolation ?? DEFAULT_ISOLATION\n}\n\n/** The effective external session sync switch (board setting → factory default false). */\nexport function defaultSyncExternalSessionsOf(settings?: BoardSettings): boolean {\n return settings?.syncExternalSessions ?? false\n}\n\n/** The effective default permission preset for NEW tasks (board setting → factory default 'workspace-write'). */\nexport function defaultPermissionOf(settings?: BoardSettings): PermissionMode {\n return settings?.defaultPermission ?? DEFAULT_PERMISSION\n}\n\n/** Effective global execution cap (board setting → supplied deployment default). */\nexport function maxConcurrentOf(settings: BoardSettings | undefined, fallback = DEFAULT_MAX_CONCURRENT): number {\n return settings?.maxConcurrent ?? fallback\n}\n\n/** Effective offline missed-window threshold in minutes (board setting → factory default). */\nexport function scheduleMissedAfterMinutesOf(settings?: BoardSettings): number {\n return settings?.scheduleMissedAfterMinutes ?? DEFAULT_SCHEDULE_MISSED_AFTER_MINUTES\n}\n\n/** Effective queued-work shelf life in minutes; zero means durable replay. */\nexport function queueMaxAgeMinutesOf(settings?: BoardSettings): number {\n return settings?.queueMaxAgeMinutes ?? DEFAULT_QUEUE_MAX_AGE_MINUTES\n}\n\n/** Effective minimum spacing between scheduled session starts. */\nexport function dispatchIntervalMsOf(settings?: BoardSettings): number {\n return settings?.dispatchIntervalMs ?? DEFAULT_DISPATCH_INTERVAL_MS\n}\n\n/** How a task may run. */\nexport type ExecutionMode = 'claim' | 'scheduled'\n\n/** How a periodic cron task continues after a successful scheduled run. */\nexport type PeriodicCompletion = 'rearm' | 'spawn'\n/** Default periodic behavior: keep this round for review and create the next todo card. */\nexport const DEFAULT_PERIODIC_COMPLETION: PeriodicCompletion = 'spawn'\n\n/** How a scheduled task obtains its conversation when no existing session is selected. */\nexport type ScheduledSessionReuseMode = 'fresh' | 'reuse'\n\n/**\n * Per-task execution configuration. `claim` tasks wait for an in-project\n * session to claim them; `scheduled` tasks run on the host cron scheduler.\n */\nexport interface ExecutionConfig {\n mode: ExecutionMode\n /** Explicit existing project session to continue on scheduled runs. This is a mandatory selection. */\n reuseSessionId?: string\n /**\n * Periodic-cron-only policy: `fresh` creates a conversation for every\n * trigger; `reuse` creates one on the first trigger then continues it.\n * Omitted is the legacy `reuse` behavior, retained for already-saved tasks.\n */\n sessionReuseMode?: ScheduledSessionReuseMode\n /** Host-owned continuation pointer for `sessionReuseMode: 'reuse'`. */\n autoReuseSessionId?: string\n /** Effective configuration paired with {@link autoReuseSessionId}. */\n autoReuseSessionKey?: string\n /**\n * Five-field cron expression (minute hour day month weekday). Present on\n * PERIODIC scheduled tasks (定期执行): the scheduler refires the task each\n * time it comes due while the card sits in todo.\n */\n cron?: string\n /**\n * Optional host-owned completion policy for periodic cron tasks. `rearm`\n * returns this card to todo; `spawn` preserves it for review and creates a\n * fresh todo successor. Omitted records use the {@link DEFAULT_PERIODIC_COMPLETION}.\n */\n periodicCompletion?: PeriodicCompletion\n /**\n * One-shot trigger time (epoch ms). Present on ONE-SHOT scheduled tasks\n * (定时执行): the scheduler fires the task once when due and consumes the\n * field. Mutually exclusive with {@link cron}.\n */\n runAt?: number\n /** Next due time (epoch ms); maintained by the host scheduler. */\n nextRunAt?: number\n /**\n * Original due window of a scheduled dispatch waiting for global capacity.\n * Its presence is durable: a host restart must not reclassify an already\n * online-and-queued run as a window missed while the host was down.\n */\n queuedRunAt?: number\n /** When the scheduler put {@link queuedRunAt} into its durable FIFO queue. */\n queuedAt?: number\n /** A queue entry reserved for an execution gate but not yet running. */\n dispatchingRunAt?: number\n /** Last time the scheduler triggered this task (epoch ms). */\n lastTriggeredAt?: number\n}\n\n/**\n * Parse a five-field cron expression. Supported field syntax: star, star/step\n * (`* / n` without spaces), a single number, an `a-b` range, and comma lists\n * of those. Day-of-week accepts both 0 and 7 as Sunday (normalized to 0).\n *\n * @param expr - the expression to parse.\n * @returns the match sets per field, or null when invalid.\n */\nexport function parseCron(expr: string): CronMatch | null {\n const fields = expr.trim().split(/\\s+/)\n if (fields.length !== 5) return null\n const ranges: ReadonlyArray<readonly [number, number]> = [\n [0, 59],\n [0, 23],\n [1, 31],\n [1, 12],\n [0, 7],\n ]\n const sets: Array<Set<number>> = []\n for (let i = 0; i < 5; i++) {\n const [min, max] = ranges[i]!\n const set = new Set<number>()\n if (!parseCronField(fields[i]!, min, max, set)) return null\n sets.push(set)\n }\n const weekdays = new Set<number>()\n for (const day of sets[4]!) weekdays.add(day === 7 ? 0 : day)\n return { minutes: sets[0]!, hours: sets[1]!, days: sets[2]!, months: sets[3]!, weekdays }\n}\n\n/** Parsed cron field match sets. */\nexport type CronMatch = {\n minutes: ReadonlySet<number>\n hours: ReadonlySet<number>\n days: ReadonlySet<number>\n months: ReadonlySet<number>\n weekdays: ReadonlySet<number>\n}\n\n/** Parse one cron field into a match set; false on any syntax error. */\nfunction parseCronField(field: string, min: number, max: number, out: Set<number>): boolean {\n for (const part of field.split(',')) {\n const [range, stepRaw] = part.split('/')\n const step = stepRaw === undefined ? 1 : Number.parseInt(stepRaw, 10)\n if (!Number.isInteger(step) || step < 1) return false\n let lo: number\n let hi: number\n if (range === undefined || range === '') return false\n if (range === '*') {\n lo = min\n hi = max\n } else if (range.includes('-')) {\n const [a, b] = range.split('-')\n lo = Number.parseInt(a ?? '', 10)\n hi = Number.parseInt(b ?? '', 10)\n if (!Number.isInteger(lo) || !Number.isInteger(hi)) return false\n } else {\n lo = Number.parseInt(range, 10)\n if (!Number.isInteger(lo)) return false\n hi = stepRaw === undefined ? lo : max\n }\n if (lo < min || hi > max || lo > hi) return false\n for (let v = lo; v <= hi; v += step) out.add(v)\n }\n return out.size > 0\n}\n\n/**\n * The next time at or after `from` matching the cron sets (local time),\n * or null when no match exists within four years (e.g. Feb 30).\n * @param match - parsed cron sets.\n * @param from - epoch ms start point (inclusive match candidate).\n * @returns the next match's epoch ms, or null.\n */\nexport function nextCronTime(match: CronMatch, from: number): number | null {\n // Walk minute by minute from the next whole minute, capped at ~4 years.\n const start = new Date(from)\n start.setSeconds(0, 0)\n start.setMinutes(start.getMinutes() + 1)\n const cap = from + 4 * 366 * 24 * 60 * 60 * 1000\n let t = start.getTime()\n while (t <= cap) {\n const d = new Date(t)\n if (\n match.months.has(d.getMonth() + 1)\n && match.days.has(d.getDate())\n && match.weekdays.has(d.getDay())\n && match.hours.has(d.getHours())\n && match.minutes.has(d.getMinutes())\n ) {\n return t\n }\n t += 60_000\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Records\n// ---------------------------------------------------------------------------\n\n/** Who performed a write. */\nexport type Actor =\n | { kind: 'user' }\n | { kind: 'agent'; sessionId: string }\n | { kind: 'system' }\n\n/** Structured row of a multi-repo merge system comment (0.6.4). */\nexport type SystemCommentRow = {\n /** Repo path relative to the workspace ('' = the workspace root repo). */\n repo: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim) when outcome = 'failed'. */\n error?: string\n}\n\n/** A progress/report comment on a task. */\nexport type CommentRecord = {\n id: string\n /** Comment body (plain text; UI renders as pre-wrapped). */\n body: string\n /** Optimistic-concurrency version of this comment. */\n version: number\n createdAt: number\n /** The session that wrote this comment; absent for user-written ones. */\n threadId?: string\n /**\n * i18n key of a host-generated system message (0.6.4). The GUI localizes it\n * at render time; `body` stays a zh fallback for agent tools / CSV / raw\n * JSON views.\n */\n systemKey?: string\n /** Flat {name} interpolation params for the system message. */\n systemParams?: Record<string, string>\n /** Structured per-repo rows for the multi-repo merge summary (0.6.4). */\n systemRows?: SystemCommentRow[]\n}\n\n/** One commit produced by an isolated execution (hash + subject). */\nexport type CommitInfo = { hash: string; subject: string }\n\n// ---------------------------------------------------------------------------\n// Multi-repo mirror isolation (0.6.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Max repos ONE task mirror may cover (0.6.3): the workspace root repo plus\n * its parallel nested repos. A workspace discovering more degrades the whole\n * worktree run to the original directory (plan §4.1 — predictable, and never\n * a half-built mirror).\n */\nexport const MAX_MIRROR_REPOS = 8\n\n/**\n * Whether `rel` is a legal repo-path key (0.6.3): `''` = the workspace root\n * repo; otherwise a relative forward-slash path with no traversal/absolute\n * shape and no dot segments. These keys ride into filesystem joins\n * (`<workspace>/<rel>`) and ledger maps — the same R4 paranoia as task ids.\n */\nexport function isValidRelRepoPath(rel: string): boolean {\n if (rel === '') return true\n if (rel.length === 0 || rel.length > 300) return false\n if (/^[A-Za-z]:[\\\\/]/.test(rel) || rel.startsWith('\\\\\\\\') || rel.startsWith('/')) return false\n const parts = rel.split('/')\n return parts.every(p =>\n p.length > 0 && p !== '.' && p !== '..' && !p.startsWith('.') && !/[\\\\:*?\"<>|]/.test(p))\n}\n\n/**\n * Per-repo isolation + evidence facts for one execution (0.6.3 multi-repo\n * mirror). Present only when the mirror covers MORE than the workspace root\n * repo alone (or has skipped repos) — a plain single-repo run keeps the\n * legacy flat fields above, untouched, so old ledgers/clients stay blind to\n * this. The root repo (when mirrored) ALSO dual-writes the flat fields.\n */\nexport type ExecutionRepoEvidence = {\n /** Repo path relative to the workspace ('' = the workspace root repo). */\n repo: string\n /** The task branch checked out in this repo's worktree. */\n branch: string\n /** Absolute path of the repo's worktree inside the task mirror. */\n worktreePath: string\n /** HEAD of the task branch before the execution started. */\n baseCommit?: string\n /** HEAD at settlement. */\n headCommit?: string\n /** Commits between baseCommit and headCommit (capped, newest first). */\n commits?: CommitInfo[]\n commitsTotal?: number\n /** Uncommitted changes at settlement (`status --porcelain` lines, capped). */\n dirtyFiles?: string[]\n dirtyFilesTotal?: number\n diffStat?: string\n changedFiles?: number\n}\n\n/**\n * The structured execution report an agent submits at handoff (0.4.0).\n * Commits/dirty/diff facts are host-collected git evidence — the report\n * covers the BUSINESS side the host cannot see.\n */\nexport type ExecutionReport = {\n /** What was done (1..2000 chars, required). */\n summary: string\n /** Files the agent changed (paths, ≤50 × 300 chars). */\n changedFiles: string[]\n /** How the work was self-verified (≤50 × 300 chars). */\n checks: string[]\n /** Produced artifacts worth reviewing (≤30 × 300 chars). */\n artifacts: string[]\n /** Known remaining risks / follow-ups (≤2000 chars, '' allowed). */\n risk: string\n}\n\n/** One Definition-of-Done checklist item (0.4.0). */\nexport type ChecklistItem = {\n id: string\n /** What must be true for acceptance (1..200 chars). */\n text: string\n checked: boolean\n /** Who checked it: an agent session id, or 'user' for GUI toggles. */\n checkedBy?: string\n /** When it was checked (epoch ms). */\n checkedAt?: number\n /** Evidence note attached when checking (≤400 chars). */\n note?: string\n}\n\n/** One execution attempt of a task. */\nexport type ExecutionRecord = {\n id: string\n /** The session this execution ran in; set once the session is really started. */\n sessionId?: string\n /** Effective configuration of a scheduled session; used only for compatible reuse. */\n sessionReuseKey?: string\n /** Trigger: manual button or the host scheduler. */\n trigger: 'manual' | 'scheduled'\n startedAt?: number\n /** Most recent observed session activity, throttled by the host. */\n lastActivityAt?: number\n endedAt?: number\n outcome: 'running' | 'succeeded' | 'failed' | 'cancelled'\n error?: string\n /** Code isolation actually used (`none` also covers degraded worktree runs). */\n isolation?: IsolationMode\n /** Why worktree isolation degraded to running in the original directory. */\n isolationNote?: string\n /** The task branch this execution worked on (worktree runs only). */\n branch?: string\n /** Absolute path of the dedicated worktree (worktree runs only). */\n worktreePath?: string\n /** HEAD of the task branch before the execution started. */\n baseCommit?: string\n /** HEAD at settlement. */\n headCommit?: string\n /** Commits between baseCommit and headCommit (hash + subject; capped at 50, newest first). */\n commits?: CommitInfo[]\n /** Total commits before the evidence cap (equals commits.length when under it). */\n commitsTotal?: number\n /** Uncommitted changes present at settlement (`git status --porcelain` lines; capped at 100). */\n dirtyFiles?: string[]\n /** Total uncommitted lines before the evidence cap. */\n dirtyFilesTotal?: number\n /** Aggregate diff stat between baseCommit and headCommit. */\n diffStat?: string\n /** How many files differ between baseCommit and headCommit. */\n changedFiles?: number\n /** Per-repo facts of a multi-repo mirror run (0.6.3; absent on single-repo runs). */\n repos?: ExecutionRepoEvidence[]\n /** The agent's structured report, submitted via taskboard_execution_report. */\n report?: ExecutionReport\n}\n\n/** The per-model override a task may carry; absent = session default model. */\nexport type TaskModel = {\n provider: string\n model: string\n /** Thinking intensity / reasoning effort (optional; e.g. 'low', 'medium', 'high', 'none'). */\n reasoningEffort?: string\n}\n\n/** One task on the board. */\nexport type TaskRecord = {\n id: string\n title: string\n description: string\n /** Extra execution prompt; the execution session receives title+description+prompt. */\n prompt: string\n /** Owning project: a DSH workspace id. */\n workspaceId: string\n urgency: Urgency\n status: TaskStatus\n /** Horizontal marker: work cannot continue right now (any non-terminal status). */\n blocked: boolean\n execution: ExecutionConfig\n model?: TaskModel\n /** Code isolation for executions (omitted = the worktree default; see {@link IsolationMode}). */\n isolation?: IsolationMode\n /**\n * The agent preset execution sessions are composed from (omitted = the\n * deployment default preset). Recorded on the session header and mounted\n * via the presets service at creation — this is what hands the session its\n * tool set. Editable any time (each run composes fresh).\n */\n presetId?: string\n /**\n * Execution permission preset (0.5.5; see {@link PermissionMode}).\n */\n permission?: PermissionMode\n /**\n * Definition-of-Done acceptance checklist (0.4.0). Agents may append items\n * and check/uncheck them (with evidence); the GUI may edit the whole list.\n * Unchecked items highlight at review time; done stays user-only.\n */\n checklist?: ChecklistItem[]\n /**\n * The task branch fixed at the FIRST worktree creation (`task/<标题>+<taskId>`).\n * Renaming the task afterwards never changes it (history preservation).\n * Multi-repo mirror runs (0.6.3): this stays the WORKSPACE ROOT repo's\n * branch; every nested repo pins its own under {@link branches}.\n */\n branch?: string\n /**\n * Per-repo task branches of a multi-repo mirror task (0.6.3): repo path\n * relative to the workspace (never `''` — the root uses {@link branch})\n * → branch name. Pinned at the repo's FIRST successful worktree creation,\n * same rename-proof semantics as {@link branch}.\n */\n branches?: Record<string, string>\n /**\n * The session currently holding the in-progress claim (explicit claim or a\n * live execution). Present only while `status === 'in_progress'`: any move\n * out of in_progress releases it. `updatedBy` is audit-only — user edits no\n * longer erase the holder.\n */\n claimedBy?: string\n /** When the current holder claimed the task (epoch ms). */\n claimedAt?: number\n version: number\n createdAt: number\n updatedAt: number\n createdBy: Actor\n updatedBy: Actor\n comments: CommentRecord[]\n executions: ExecutionRecord[]\n /** How many older execution records were pruned by the retention cap. */\n executionsPruned?: number\n /**\n * Id of the task this card continues (0.7.x periodic execution): when a\n * PERIODIC scheduled task succeeds, the finished card moves to in_review\n * and a fresh todo card carrying the cron is minted to keep the cycle\n * going. Absent on originally created tasks.\n */\n spawnedFrom?: string\n /** Soft-delete marker set by agent `taskboard_delete`; user confirms the purge. */\n trashedAt?: number\n}\n\n/** Retention cap: how many execution records each task keeps (oldest pruned). */\nexport const MAX_EXECUTIONS = 20\n\n/**\n * Enforce the execution-record retention cap on one task (in place): keep the\n * newest {@link MAX_EXECUTIONS} records, count the dropped ones in\n * `executionsPruned`. Running records are always the newest, never dropped.\n * @param task - the task to prune.\n */\nexport function pruneExecutions(task: TaskRecord): void {\n if (task.executions.length <= MAX_EXECUTIONS) return\n const dropped = task.executions.length - MAX_EXECUTIONS\n task.executions = task.executions.slice(-MAX_EXECUTIONS)\n task.executionsPruned = (task.executionsPruned ?? 0) + dropped\n}\n\n/**\n * Mint the successor card of a PERIODIC scheduled task that just succeeded\n * (0.7.x): the finished card goes to in_review for acceptance while this\n * fresh todo card carries the cron onward, keeping the cycle alive. The\n * next run is recomputed from `now` (no compensating catch-up burst).\n * Pure: the caller pushes the returned record into the ledger.\n * @param source - the finished periodic task (still carrying its cron).\n * @param prevExecutionId - id of the execution that just succeeded.\n * @param now - current epoch ms.\n * @returns the successor task record.\n */\nexport function spawnNextCycle(source: TaskRecord, prevExecutionId: string | undefined, now: number): TaskRecord {\n const cron = source.execution.cron\n if (cron === undefined) throw new Error('spawnNextCycle: source task has no cron')\n const match = parseCron(cron)\n const next = match === null ? undefined : nextCronTime(match, now) ?? undefined\n if (next === undefined) throw new Error('spawnNextCycle: cron has no upcoming match within 4 years')\n return {\n id: newTaskId(),\n title: source.title,\n description: source.description,\n prompt: source.prompt,\n workspaceId: source.workspaceId,\n urgency: source.urgency,\n status: 'todo',\n blocked: false,\n execution: { mode: 'scheduled', cron, nextRunAt: next, periodicCompletion: source.execution.periodicCompletion ?? DEFAULT_PERIODIC_COMPLETION,\n ...(source.execution.reuseSessionId !== undefined ? { reuseSessionId: source.execution.reuseSessionId } : {}),\n ...(source.execution.sessionReuseMode !== undefined ? { sessionReuseMode: source.execution.sessionReuseMode } : {}),\n ...(source.execution.autoReuseSessionId !== undefined ? { autoReuseSessionId: source.execution.autoReuseSessionId } : {}),\n ...(source.execution.autoReuseSessionKey !== undefined ? { autoReuseSessionKey: source.execution.autoReuseSessionKey } : {}) },\n ...(source.model !== undefined ? { model: structuredClone(source.model) } : {}),\n ...(source.isolation !== undefined ? { isolation: source.isolation } : {}),\n ...(source.presetId !== undefined ? { presetId: source.presetId } : {}),\n ...(source.permission !== undefined ? { permission: source.permission } : {}),\n ...(source.checklist !== undefined\n ? { checklist: source.checklist.map(item => ({ ...item, checked: false, checkedBy: undefined, checkedAt: undefined, note: undefined })) }\n : {}),\n ...(source.branch !== undefined ? { branch: source.branch } : {}),\n ...(source.branches !== undefined ? { branches: { ...source.branches } } : {}),\n spawnedFrom: source.id,\n version: 1,\n createdAt: now,\n updatedAt: now,\n createdBy: { kind: 'system' },\n updatedBy: { kind: 'system' },\n comments: [{\n id: newCommentId(),\n body: normalizeBody(`[系统] 定期任务上一轮执行完毕(执行 ${prevExecutionId ?? '未知'}),本卡承接定时继续下一轮;上一轮成果见 ${source.id} 的待验收。`),\n systemKey: 'sys.spawnedFrom',\n systemParams: { sourceId: source.id, executionId: prevExecutionId ?? '' },\n version: 1,\n createdAt: now,\n }],\n executions: [],\n }\n}\n\n/** The whole durable ledger. */\nexport type TaskLedger = {\n schemaVersion: number\n /** Global monotonic revision; every mutation bumps it. */\n revision: number\n tasks: TaskRecord[]\n /** Board-level settings (0.5.0); absent on ledgers never touched by 设置. */\n settings?: BoardSettings\n}\n\n/** Current ledger format version. */\nexport const LEDGER_SCHEMA_VERSION = 1\n\n/** An empty ledger. */\nexport function emptyLedger(): TaskLedger {\n return { schemaVersion: LEDGER_SCHEMA_VERSION, revision: 0, tasks: [] }\n}\n\n// ---------------------------------------------------------------------------\n// ids\n// ---------------------------------------------------------------------------\n\n/** Random base36 suffix. */\nfunction suffix(): string {\n return Math.random().toString(36).slice(2, 8)\n}\n\n/**\n * Legal task id charset (R4): `t-<base36>-<base36>` from {@link newTaskId},\n * and the ONLY shape accepted from the outside (import) or used to build\n * filesystem paths (worktree dirs). Ids ride into `join(ws, '.dsh-worktrees',\n * id)` — a lax charset here is an arbitrary-directory delete primitive.\n */\nexport function isValidTaskId(id: string): boolean {\n return /^[A-Za-z0-9][A-Za-z0-9_-]{0,99}$/.test(id)\n}\n\n/** Mint a task id. */\nexport function newTaskId(): string {\n return `t-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint a comment id. */\nexport function newCommentId(): string {\n return `c-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint a checklist item id. */\nexport function newChecklistItemId(): string {\n return `k-${Date.now().toString(36)}-${suffix()}`\n}\n\n/** Mint an execution id. */\nexport function newExecutionId(): string {\n return `e-${Date.now().toString(36)}-${suffix()}`\n}\n\n// ---------------------------------------------------------------------------\n// validation helpers (input shaping for tools and routes)\n// ---------------------------------------------------------------------------\n\n/**\n * Validate and normalize a title: trimmed, 1..200 chars.\n * @param raw - the raw input.\n * @returns the normalized title.\n * @throws when empty or too long.\n */\nexport function normalizeTitle(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > 200) {\n throw new Error('title must be 1..200 characters')\n }\n return t\n}\n\n/**\n * Validate a task prompt: trimmed, at most 8000 chars; empty becomes ''.\n * @param raw - the raw input.\n */\nexport function normalizePrompt(raw: string | undefined): string {\n const t = (raw ?? '').trim()\n if (t.length > 8000) throw new Error('prompt must be at most 8000 characters')\n return t\n}\n\n/**\n * Validate and normalize a comment body: trimmed, 1..4000 chars.\n * @param raw - the raw input.\n */\nexport function normalizeBody(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > 4000) {\n throw new Error('comment body must be 1..4000 characters')\n }\n return t\n}\n\n/**\n * Validate an urgency value.\n * @param raw - the raw input.\n */\nexport function asUrgency(raw: string): Urgency {\n if (!URGENCIES.includes(raw as Urgency)) {\n throw new Error(`urgency must be one of: ${URGENCIES.join(', ')}`)\n }\n return raw as Urgency\n}\n\n/**\n * Validate a status value.\n * @param raw - the raw input.\n */\nexport function asStatus(raw: string): TaskStatus {\n if (!ALL_STATUSES.includes(raw as TaskStatus)) {\n throw new Error(`status must be one of: ${ALL_STATUSES.join(', ')}`)\n }\n return raw as TaskStatus\n}\n\n/**\n * Parse a raw runAt input: epoch ms number or ISO date string → epoch ms.\n * @param raw - untyped runAt value.\n * @returns the epoch ms, or undefined when absent.\n */\nfunction normalizeRunAt(raw: unknown): number | undefined {\n if (raw === undefined || raw === null) return undefined\n if (typeof raw === 'number' && Number.isFinite(raw)) return Math.trunc(raw)\n if (typeof raw === 'string') {\n const t = Date.parse(raw)\n if (Number.isNaN(t)) throw new Error('execution.runAt is not a valid time (epoch ms or ISO string)')\n return t\n }\n throw new Error('execution.runAt must be an epoch ms number or an ISO date string')\n}\n\n/**\n * Validate an execution config request from raw tool/route input.\n * `scheduled` requires either a valid cron (periodic, 定期执行) or a runAt\n * instant (one-shot, 定时执行); the two are mutually exclusive. A cron's\n * first `nextRunAt` is computed from `now`.\n * @param raw - raw execution input ({@link ExecutionConfig} fields, untyped).\n * @param now - current epoch ms.\n * @param opts - `allowPastRunAt` lets the import path keep a historical\n * one-shot instant instead of rejecting it.\n * @returns the normalized config.\n */\nexport function normalizeExecution(\n raw: { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown },\n now: number,\n opts?: { allowPastRunAt?: boolean },\n): ExecutionConfig {\n const mode = raw.mode ?? 'claim'\n if (mode !== 'claim' && mode !== 'scheduled') {\n throw new Error(\"execution.mode must be 'claim' or 'scheduled'\")\n }\n if (mode === 'claim') {\n if (raw.periodicCompletion !== undefined) throw new Error('execution.periodicCompletion requires a cron schedule')\n if (raw.reuseSessionId !== undefined) throw new Error('execution.reuseSessionId requires a scheduled task')\n if (raw.sessionReuseMode !== undefined) throw new Error('execution.sessionReuseMode requires a scheduled task')\n return { mode }\n }\n if (raw.reuseSessionId !== undefined && (typeof raw.reuseSessionId !== 'string' || raw.reuseSessionId.length === 0\n || raw.reuseSessionId.length > 256 || raw.reuseSessionId.trim() !== raw.reuseSessionId || /[\\x00-\\x1f\\x7f]/.test(raw.reuseSessionId))) {\n throw new Error('execution.reuseSessionId must be a valid session id')\n }\n const reuseSession = raw.reuseSessionId === undefined ? {} : { reuseSessionId: raw.reuseSessionId as string }\n if (raw.sessionReuseMode !== undefined && raw.sessionReuseMode !== 'fresh' && raw.sessionReuseMode !== 'reuse') {\n throw new Error(\"execution.sessionReuseMode must be 'fresh' or 'reuse'\")\n }\n const sessionReuseMode = raw.sessionReuseMode === undefined ? {} : { sessionReuseMode: raw.sessionReuseMode as ScheduledSessionReuseMode }\n const runAt = normalizeRunAt(raw.runAt)\n const cron = (raw.cron ?? '').trim()\n if (cron.length > 0 && runAt !== undefined) {\n throw new Error('execution: cron and runAt are mutually exclusive (periodic vs one-shot)')\n }\n if (runAt !== undefined) {\n if (raw.periodicCompletion !== undefined) throw new Error('execution.periodicCompletion requires a cron schedule')\n if (raw.sessionReuseMode !== undefined) throw new Error('execution.sessionReuseMode requires a cron schedule')\n if (!opts?.allowPastRunAt && runAt <= now) throw new Error('execution.runAt must be in the future')\n return { mode, runAt, nextRunAt: runAt, ...reuseSession }\n }\n const match = parseCron(cron)\n if (match === null) throw new Error('execution.cron is not a valid 5-field cron expression')\n const next = nextCronTime(match, now)\n if (next === null) throw new Error('execution.cron never matches within 4 years')\n const periodicCompletion = raw.periodicCompletion ?? DEFAULT_PERIODIC_COMPLETION\n if (periodicCompletion !== 'rearm' && periodicCompletion !== 'spawn') {\n throw new Error(\"execution.periodicCompletion must be 'rearm' or 'spawn'\")\n }\n return { mode, cron, nextRunAt: next, periodicCompletion, ...reuseSession, ...sessionReuseMode }\n}\n\n/**\n * The effective prompt of a task: title+description, with the explicit\n * prompt appended when set — title+description+prompt.\n * @param task - the task.\n */\nexport function effectivePrompt(task: TaskRecord): string {\n const head = task.title\n const body = task.description.length > 0 ? `${head}\\n\\n${task.description}` : head\n return task.prompt.length > 0 ? `${body}\\n\\n${task.prompt}` : body\n}\n\n/**\n * Whether the task is currently claimed by a session (running state).\n * @param task - the task.\n */\nexport function isClaimedBy(task: TaskRecord): string | undefined {\n return task.status === 'in_progress' && task.claimedBy !== undefined ? task.claimedBy : undefined\n}\n\n/**\n * Maintain the explicit claim fields around a status change: entering\n * in_progress under a session records the holder (an execution-start or an\n * agent claim); every move out of in_progress releases the claim (handoff,\n * give-back, cancel). A user-driven move into in_progress records no holder —\n * no session works on it yet.\n * @param task - the task being written (mutated in place).\n * @param to - the target status.\n * @param now - current epoch ms.\n * @param holder - the session id claiming the task, when applicable.\n */\nexport function syncClaim(task: TaskRecord, to: TaskStatus, now: number, holder?: string): void {\n if (to !== 'in_progress') {\n delete task.claimedBy\n delete task.claimedAt\n } else if (holder !== undefined) {\n task.claimedBy = holder\n task.claimedAt = now\n }\n}\n\n/**\n * Collect unique execution session IDs associated with a task:\n * - executions with a non-empty `sessionId`\n * Creator and claim sessions may serve other tasks and are never included.\n * @param task - the task record to inspect.\n * @returns an array of distinct session IDs in stable discovery order.\n */\nexport function taskAssociatedSessionIds(task: TaskRecord): string[] {\n const seen = new Set<string>()\n const result: string[] = []\n const push = (raw: unknown) => {\n if (typeof raw === 'string') {\n const trimmed = raw.trim()\n if (trimmed.length > 0 && !seen.has(trimmed)) {\n seen.add(trimmed)\n result.push(trimmed)\n }\n }\n }\n\n if (Array.isArray(task.executions)) {\n for (const ex of task.executions) {\n if (ex !== null && typeof ex === 'object') {\n push((ex as { sessionId?: unknown }).sessionId)\n }\n }\n }\n return result\n}\n\n/**\n * Validate and normalize a pinned model: `{ provider, model, reasoningEffort? }`,\n * provider and model must be non-empty trimmed strings.\n * @param raw - the raw input.\n * @returns the normalized model.\n * @throws when the shape or the fields are invalid.\n */\nexport function normalizeModel(raw: unknown): TaskModel {\n if (typeof raw !== 'object' || raw === null) {\n throw new Error('model must be { provider: string, model: string }')\n }\n const { provider, model, reasoningEffort } = raw as { provider?: unknown; model?: unknown; reasoningEffort?: unknown }\n if (typeof provider !== 'string' || typeof model !== 'string') {\n throw new Error('model must be { provider: string, model: string }')\n }\n const p = provider.trim()\n const m = model.trim()\n if (p.length === 0 || m.length === 0) {\n throw new Error('model.provider and model.model must be non-empty strings')\n }\n const eff = typeof reasoningEffort === 'string' && reasoningEffort.trim().length > 0 ? reasoningEffort.trim() : undefined\n return { provider: p, model: m, ...(eff !== undefined ? { reasoningEffort: eff } : {}) }\n}\n\n// ---------------------------------------------------------------------------\n// checklist + report validation (0.4.0)\n// ---------------------------------------------------------------------------\n\n/** Checklist size cap per task. */\nexport const MAX_CHECKLIST_ITEMS = 30\n\n/** Checklist item text cap (chars). */\nexport const MAX_CHECKLIST_TEXT = 200\n\n/**\n * Validate and normalize one checklist text line: trimmed, 1..200 chars.\n * @param raw - the raw text.\n * @throws when empty or too long.\n */\nexport function normalizeChecklistText(raw: string): string {\n const t = raw.trim()\n if (t.length === 0 || t.length > MAX_CHECKLIST_TEXT) {\n throw new Error(`checklist item text must be 1..${MAX_CHECKLIST_TEXT} characters`)\n }\n return t\n}\n\n/**\n * Build a fresh unchecked checklist from plain text lines (create route /\n * templates / tool adds).\n * @param texts - the item texts (validated individually).\n */\nexport function checklistFromTexts(texts: readonly string[]): ChecklistItem[] {\n const items = texts.map(text => ({ id: newChecklistItemId(), text: normalizeChecklistText(text), checked: false }))\n if (items.length > MAX_CHECKLIST_ITEMS) {\n throw new Error(`checklist may hold at most ${MAX_CHECKLIST_ITEMS} items`)\n }\n return items\n}\n\n/**\n * Validate and normalize a full checklist array (GUI update route, import):\n * missing ids are minted, text is checked, checked flags must be booleans,\n * checkedBy/checkedAt are kept only on checked items.\n * @param raw - untyped array from the wire.\n * @throws with a readable reason on any invalid entry.\n */\nexport function normalizeChecklist(raw: unknown): ChecklistItem[] {\n if (!Array.isArray(raw)) throw new Error('checklist must be an array')\n if (raw.length > MAX_CHECKLIST_ITEMS) {\n throw new Error(`checklist may hold at most ${MAX_CHECKLIST_ITEMS} items`)\n }\n return raw.map((entry): ChecklistItem => {\n if (typeof entry !== 'object' || entry === null) throw new Error('checklist item must be an object')\n const e = entry as Record<string, unknown>\n const text = normalizeChecklistText(typeof e.text === 'string' ? e.text : '')\n const id = typeof e.id === 'string' && e.id.trim().length > 0 ? e.id.trim() : newChecklistItemId()\n const checked = e.checked === true\n const checkedBy = typeof e.checkedBy === 'string' ? e.checkedBy.trim().slice(0, 100) : undefined\n const checkedAt = typeof e.checkedAt === 'number' && Number.isFinite(e.checkedAt) ? e.checkedAt : undefined\n const note = typeof e.note === 'string' && e.note.trim().length > 0 ? e.note.trim().slice(0, 400) : undefined\n if (!checked) return { id, text, checked: false }\n return {\n id,\n text,\n checked: true,\n ...(checkedBy !== undefined && checkedBy.length > 0 ? { checkedBy } : {}),\n ...(checkedAt !== undefined ? { checkedAt } : {}),\n ...(note !== undefined ? { note } : {}),\n }\n })\n}\n\n/** Checklist progress: how many items are checked (absent checklist → 0/0). */\nexport function checklistProgress(task: Pick<TaskRecord, 'checklist'>): { done: number; total: number } {\n const items = task.checklist ?? []\n return { done: items.filter(i => i.checked).length, total: items.length }\n}\n\n/** Report string-list caps. */\nconst REPORT_LIST_CAPS = { changedFiles: 50, checks: 50, artifacts: 30 } as const\n\n/** Per-entry cap for report lists (chars). */\nconst REPORT_ENTRY_MAX = 300\n\n/** Validate one report string list: strings trimmed 1..300 chars. */\nfunction normalizeReportList(raw: unknown, field: keyof typeof REPORT_LIST_CAPS): string[] {\n if (raw === undefined) return []\n if (!Array.isArray(raw)) throw new Error(`report.${field} must be an array of strings`)\n const out = raw.map(entry => {\n if (typeof entry !== 'string') throw new Error(`report.${field} must be an array of strings`)\n const t = entry.trim()\n if (t.length === 0 || t.length > REPORT_ENTRY_MAX) {\n throw new Error(`report.${field} entries must be 1..${REPORT_ENTRY_MAX} characters`)\n }\n return t\n })\n if (out.length > REPORT_LIST_CAPS[field]) {\n throw new Error(`report.${field} may hold at most ${REPORT_LIST_CAPS[field]} entries`)\n }\n return out\n}\n\n/**\n * Validate and normalize a structured execution report.\n * @param raw - untyped tool/route input.\n * @throws with a readable reason on any invalid field.\n */\nexport function normalizeExecutionReport(raw: unknown): ExecutionReport {\n if (typeof raw !== 'object' || raw === null) throw new Error('report must be an object')\n const e = raw as Record<string, unknown>\n const summary = typeof e.summary === 'string' ? e.summary.trim() : ''\n if (summary.length === 0 || summary.length > 2000) {\n throw new Error('report.summary must be 1..2000 characters')\n }\n const risk = typeof e.risk === 'string' ? e.risk.trim().slice(0, 2000) : ''\n return {\n summary,\n changedFiles: normalizeReportList(e.changedFiles, 'changedFiles'),\n checks: normalizeReportList(e.checks, 'checks'),\n artifacts: normalizeReportList(e.artifacts, 'artifacts'),\n risk,\n }\n}\n\n// ---------------------------------------------------------------------------\n// ledger import validation (0.4.0)\n// ---------------------------------------------------------------------------\n\n/** Result classifying every task in an import file against the live ledger. */\nexport type ImportPlan = {\n /** Structurally valid tasks whose ids are new (merge adds them). */\n create: TaskRecord[]\n /** Structurally valid tasks whose ids already exist (merge replaces them). */\n overwrite: TaskRecord[]\n /** Invalid entries with a human-readable reason (never imported). */\n invalid: Array<{ id?: string; reason: string }>\n /** The file's board settings (0.5.0); replace-mode swaps them, merge keeps the live ones. */\n settings?: BoardSettings\n}\n\n/** One unknown-value read helper: string fields with defaults. */\nfunction strOr(raw: Record<string, unknown>, key: string, fallback: string): string {\n const v = raw[key]\n return typeof v === 'string' ? v : fallback\n}\n\n/** One unknown-value read helper: finite numbers with defaults. */\nfunction numOr(raw: Record<string, unknown>, key: string, fallback: number): number {\n const v = raw[key]\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback\n}\n\n/** Evidence caps for imported per-repo facts (aligns the host's collect caps). */\nconst IMPORT_COMMIT_CAP = 50\nconst IMPORT_DIRTY_CAP = 100\n\n/**\n * Sanitize an imported per-task branches map (0.6.3): legal repo keys only,\n * non-empty branch strings, capped at {@link MAX_MIRROR_REPOS} entries.\n * @returns undefined when nothing legal remains.\n */\nexport function normalizeBranchesMap(raw: unknown): Record<string, string> | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n if (Object.keys(out).length >= MAX_MIRROR_REPOS) break\n if (typeof value !== 'string' || value.trim().length === 0 || value.length > 200) continue\n if (key === '' || !isValidRelRepoPath(key)) continue\n out[key] = value.trim()\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\n/**\n * Sanitize ONE imported per-repo evidence entry (0.6.3): rebuilds the record\n * field by field, capping evidence arrays like the host's own collection.\n * @returns undefined for a structurally illegal entry (dropped, not fatal).\n */\nexport function normalizeRepoEvidence(raw: unknown): ExecutionRepoEvidence | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const e = raw as Record<string, unknown>\n if (typeof e.repo !== 'string' || !isValidRelRepoPath(e.repo)) return undefined\n if (typeof e.branch !== 'string' || e.branch.length === 0 || e.branch.length > 200) return undefined\n if (typeof e.worktreePath !== 'string' || e.worktreePath.length === 0 || e.worktreePath.length > 1000) return undefined\n const commits = Array.isArray(e.commits)\n ? e.commits.filter((c): c is CommitInfo =>\n typeof c === 'object' && c !== null && typeof (c as CommitInfo).hash === 'string'\n && typeof (c as CommitInfo).subject === 'string').slice(0, IMPORT_COMMIT_CAP)\n : undefined\n const dirtyFiles = Array.isArray(e.dirtyFiles)\n ? e.dirtyFiles.filter((l): l is string => typeof l === 'string').slice(0, IMPORT_DIRTY_CAP)\n : undefined\n return {\n repo: e.repo,\n branch: e.branch,\n worktreePath: e.worktreePath,\n ...(typeof e.baseCommit === 'string' ? { baseCommit: e.baseCommit.slice(0, 100) } : {}),\n ...(typeof e.headCommit === 'string' ? { headCommit: e.headCommit.slice(0, 100) } : {}),\n ...(commits !== undefined && commits.length > 0 ? { commits } : {}),\n ...(typeof e.commitsTotal === 'number' && Number.isFinite(e.commitsTotal) ? { commitsTotal: e.commitsTotal } : {}),\n ...(dirtyFiles !== undefined && dirtyFiles.length > 0 ? { dirtyFiles } : {}),\n ...(typeof e.dirtyFilesTotal === 'number' && Number.isFinite(e.dirtyFilesTotal) ? { dirtyFilesTotal: e.dirtyFilesTotal } : {}),\n ...(typeof e.diffStat === 'string' ? { diffStat: e.diffStat.slice(0, 500) } : {}),\n ...(typeof e.changedFiles === 'number' && Number.isFinite(e.changedFiles) ? { changedFiles: e.changedFiles } : {}),\n }\n}\n\n/**\n * Validate ONE imported task record (pure): rebuilds it field by field with\n * the normal validators, minting missing ids and re-arming cron. Executions\n * left `running` by the exporting machine are marked failed — their\n * settlement watchers died there and can never settle here.\n * @param raw - the untyped record.\n * @param now - current epoch ms (defaults for timestamps).\n * @returns the rebuilt record, or a rejection reason.\n */\nexport function validateImportedTask(raw: unknown, now: number): { ok: true; task: TaskRecord } | { ok: false; reason: string } {\n if (typeof raw !== 'object' || raw === null) return { ok: false, reason: 'not an object' }\n const e = raw as Record<string, unknown>\n const id = typeof e.id === 'string' ? e.id.trim() : ''\n const fail = (reason: string): { ok: false; reason: string } => ({ ok: false, reason })\n // R4①: length alone let traversal-shaped ids (`../../x`, `..\\..\\x`) into\n // the ledger; the charset gate is the primary defense for every downstream\n // filesystem use of a task id.\n if (!isValidTaskId(id)) return fail('missing/invalid id (must match ^[A-Za-z0-9][A-Za-z0-9_-]{0,99}$)')\n try {\n const rawExecution = typeof e.execution === 'object' && e.execution !== null\n ? e.execution as { mode?: string; cron?: string; runAt?: unknown; periodicCompletion?: unknown; reuseSessionId?: unknown; sessionReuseMode?: unknown; autoReuseSessionId?: unknown; autoReuseSessionKey?: unknown; queuedRunAt?: unknown; queuedAt?: unknown; dispatchingRunAt?: unknown }\n : {}\n const execution = normalizeExecution(\n rawExecution,\n now,\n { allowPastRunAt: true },\n )\n // These pointers are host-owned runtime state. Retain only well-formed\n // values from an exported ledger; a stale pointer merely falls back to a\n // fresh conversation when the next scheduled run starts.\n if (execution.sessionReuseMode !== 'fresh' && typeof rawExecution.autoReuseSessionId === 'string'\n && rawExecution.autoReuseSessionId.length > 0 && rawExecution.autoReuseSessionId.length <= 256) {\n execution.autoReuseSessionId = rawExecution.autoReuseSessionId\n if (typeof rawExecution.autoReuseSessionKey === 'string' && rawExecution.autoReuseSessionKey.length <= 1024) {\n execution.autoReuseSessionKey = rawExecution.autoReuseSessionKey\n }\n }\n // Queue markers are scheduler-owned, but must survive export/import: an\n // online-and-queued window is not equivalent to an offline missed window.\n const queuedWindow = typeof rawExecution.queuedRunAt === 'number' && Number.isFinite(rawExecution.queuedRunAt)\n ? rawExecution.queuedRunAt\n : typeof rawExecution.dispatchingRunAt === 'number' && Number.isFinite(rawExecution.dispatchingRunAt)\n ? rawExecution.dispatchingRunAt\n : undefined\n if (execution.mode === 'scheduled' && queuedWindow !== undefined && typeof rawExecution.queuedAt === 'number'\n && Number.isFinite(rawExecution.queuedAt)) {\n execution.queuedRunAt = queuedWindow\n execution.queuedAt = rawExecution.queuedAt\n }\n const comments: CommentRecord[] = []\n if (Array.isArray(e.comments)) {\n for (const c of e.comments) {\n if (typeof c !== 'object' || c === null) return fail('invalid comment entry')\n const ce = c as Record<string, unknown>\n const body = typeof ce.body === 'string' ? ce.body : ''\n if (body.trim().length === 0 || body.length > 4000) return fail('invalid comment body')\n comments.push({\n id: typeof ce.id === 'string' && ce.id.length > 0 ? ce.id : newCommentId(),\n body,\n version: numOr(ce, 'version', 1),\n createdAt: numOr(ce, 'createdAt', now),\n ...(typeof ce.threadId === 'string' ? { threadId: ce.threadId } : {}),\n ...(typeof ce.systemKey === 'string' && /^sys\\.[A-Za-z0-9]+$/.test(ce.systemKey) && ce.systemKey.length <= 100\n ? {\n systemKey: ce.systemKey,\n ...(typeof ce.systemParams === 'object' && ce.systemParams !== null && !Array.isArray(ce.systemParams)\n ? { systemParams: Object.fromEntries(Object.entries(ce.systemParams).filter(([key, value]) => key.length <= 100 && typeof value === 'string' && value.length <= 4000).slice(0, 20)) as Record<string, string> }\n : {}),\n ...(Array.isArray(ce.systemRows)\n ? { systemRows: ce.systemRows.filter((row): row is SystemCommentRow => typeof row === 'object' && row !== null\n && typeof row.repo === 'string' && (row.repo === '' || isValidRelRepoPath(row.repo))\n && ['merged', 'noop', 'failed'].includes(row.outcome)\n && (row.error === undefined || typeof row.error === 'string'))\n .slice(0, MAX_MIRROR_REPOS).map(row => ({ repo: row.repo, outcome: row.outcome, ...(row.error !== undefined ? { error: row.error.slice(0, 4000) } : {}) })) }\n : {}),\n }\n : {}),\n })\n }\n } else return fail('comments must be an array')\n const executions: ExecutionRecord[] = []\n if (Array.isArray(e.executions)) {\n for (const x of e.executions) {\n if (typeof x !== 'object' || x === null) return fail('invalid execution entry')\n const xe = x as Record<string, unknown>\n const trigger = xe.trigger === 'scheduled' ? 'scheduled' : 'manual'\n const outcomeRaw = xe.outcome\n if (outcomeRaw !== 'running' && outcomeRaw !== 'succeeded' && outcomeRaw !== 'failed' && outcomeRaw !== 'cancelled') {\n return fail('invalid execution outcome')\n }\n // A running execution from the exporting machine can never settle\n // here — import it as failed with the reason recorded.\n const outcome = outcomeRaw === 'running' ? 'failed' as const : outcomeRaw\n executions.push({\n id: typeof xe.id === 'string' && xe.id.length > 0 ? xe.id : newExecutionId(),\n ...(typeof xe.sessionId === 'string' ? { sessionId: xe.sessionId } : {}),\n trigger,\n ...(typeof xe.startedAt === 'number' ? { startedAt: xe.startedAt } : {}),\n ...(typeof xe.lastActivityAt === 'number' ? { lastActivityAt: xe.lastActivityAt } : {}),\n ...(typeof xe.endedAt === 'number' ? { endedAt: xe.endedAt } : {}),\n outcome,\n ...(outcomeRaw === 'running' ? { error: 'imported while still running (settlement watcher died with the exporting host)' } : (typeof xe.error === 'string' ? { error: xe.error } : {})),\n ...(typeof xe.isolation === 'string' && (xe.isolation === 'worktree' || xe.isolation === 'none') ? { isolation: xe.isolation } : {}),\n ...(typeof xe.isolationNote === 'string' ? { isolationNote: xe.isolationNote } : {}),\n ...(typeof xe.branch === 'string' ? { branch: xe.branch } : {}),\n ...(typeof xe.worktreePath === 'string' ? { worktreePath: xe.worktreePath } : {}),\n ...(typeof xe.baseCommit === 'string' ? { baseCommit: xe.baseCommit } : {}),\n ...(typeof xe.headCommit === 'string' ? { headCommit: xe.headCommit } : {}),\n ...(Array.isArray(xe.commits) ? { commits: xe.commits.filter((c): c is CommitInfo =>\n typeof c === 'object' && c !== null && typeof (c as CommitInfo).hash === 'string' && typeof (c as CommitInfo).subject === 'string') } : {}),\n ...(typeof xe.commitsTotal === 'number' ? { commitsTotal: xe.commitsTotal } : {}),\n ...(Array.isArray(xe.dirtyFiles) ? { dirtyFiles: xe.dirtyFiles.filter((l): l is string => typeof l === 'string') } : {}),\n ...(typeof xe.dirtyFilesTotal === 'number' ? { dirtyFilesTotal: xe.dirtyFilesTotal } : {}),\n ...(typeof xe.diffStat === 'string' ? { diffStat: xe.diffStat } : {}),\n ...(typeof xe.changedFiles === 'number' ? { changedFiles: xe.changedFiles } : {}),\n ...(Array.isArray(xe.repos) ? { repos: xe.repos.slice(0, MAX_MIRROR_REPOS).map(normalizeRepoEvidence).filter((r): r is ExecutionRepoEvidence => r !== undefined) } : {}),\n ...(typeof xe.report === 'object' && xe.report !== null ? { report: normalizeExecutionReport(xe.report) } : {}),\n })\n }\n } else return fail('executions must be an array')\n const status = asStatus(strOr(e, 'status', 'todo'))\n const actorOf = (v: unknown): Actor => (typeof v === 'object' && v !== null && (v as Actor).kind === 'agent' && typeof (v as { sessionId?: unknown }).sessionId === 'string'\n ? { kind: 'agent', sessionId: (v as { sessionId: string }).sessionId }\n : { kind: 'user' })\n const branchesMap = normalizeBranchesMap(e.branches)\n const task: TaskRecord = {\n id,\n title: normalizeTitle(strOr(e, 'title', '')),\n description: strOr(e, 'description', '').trim(),\n prompt: normalizePrompt(strOr(e, 'prompt', '')),\n workspaceId: strOr(e, 'workspaceId', ''),\n urgency: asUrgency(strOr(e, 'urgency', 'normal')),\n status,\n blocked: e.blocked === true,\n execution,\n ...(typeof e.model === 'object' && e.model !== null ? { model: normalizeModel(e.model) } : {}),\n ...(typeof e.isolation === 'string' && (e.isolation === 'worktree' || e.isolation === 'none') ? { isolation: e.isolation } : {}),\n ...(typeof e.presetId === 'string' && e.presetId.trim().length > 0 ? { presetId: e.presetId.trim() } : {}),\n ...(typeof e.permission === 'string' ? { permission: asPermission(e.permission) } : {}),\n ...(Array.isArray(e.checklist) ? { checklist: normalizeChecklist(e.checklist) } : {}),\n ...(typeof e.branch === 'string' ? { branch: e.branch } : {}),\n ...(branchesMap !== undefined ? { branches: branchesMap } : {}),\n ...(status === 'in_progress' && typeof e.claimedBy === 'string' ? { claimedBy: e.claimedBy } : {}),\n ...(status === 'in_progress' && typeof e.claimedAt === 'number' ? { claimedAt: e.claimedAt } : {}),\n version: Math.max(1, Math.trunc(numOr(e, 'version', 1))),\n createdAt: numOr(e, 'createdAt', now),\n updatedAt: numOr(e, 'updatedAt', now),\n createdBy: actorOf(e.createdBy),\n updatedBy: actorOf(e.updatedBy),\n comments,\n executions,\n ...(typeof e.executionsPruned === 'number' ? { executionsPruned: e.executionsPruned } : {}),\n ...(typeof e.trashedAt === 'number' ? { trashedAt: e.trashedAt } : {}),\n }\n if (task.workspaceId.length === 0) return fail('missing workspaceId')\n return { ok: true, task }\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error))\n }\n}\n\n/**\n * Minimal structural check for ONE ledger record at load time (S11): unlike\n * {@link validateImportedTask} this REBUILDS NOTHING (cron state, ids and\n * timestamps must survive a load untouched) — it only rejects entries whose\n * shape would break downstream consumers, including the R4 id charset.\n * @param raw - the untyped record.\n */\nexport function isPlausibleTaskRecord(raw: unknown): boolean {\n if (typeof raw !== 'object' || raw === null) return false\n const t = raw as Record<string, unknown>\n return typeof t.id === 'string' && isValidTaskId(t.id)\n && typeof t.title === 'string' && t.title.length > 0\n && typeof t.workspaceId === 'string' && t.workspaceId.length > 0\n && ALL_STATUSES.includes(t.status as TaskStatus)\n && typeof t.version === 'number' && Number.isFinite(t.version) && t.version >= 1\n && Array.isArray(t.comments) && Array.isArray(t.executions)\n && typeof t.execution === 'object' && t.execution !== null\n && (t.execution as { mode?: unknown }).mode !== undefined\n}\n\n/**\n * Validate a whole imported ledger and classify its tasks against the live\n * one (pure). Duplicate ids INSIDE the file are invalid (first wins, later\n * copies reported); schemaVersion must match {@link LEDGER_SCHEMA_VERSION}.\n * @param raw - the parsed import file.\n * @param knownIds - live ledger task ids.\n * @param now - current epoch ms.\n * @throws when the file is not a ledger or the schemaVersion is unsupported.\n */\nexport function validateLedgerImport(raw: unknown, knownIds: ReadonlySet<string>, now: number): ImportPlan {\n if (typeof raw !== 'object' || raw === null) throw new Error('导入文件不是 JSON 对象')\n const e = raw as Record<string, unknown>\n if (e.schemaVersion !== LEDGER_SCHEMA_VERSION) {\n throw new Error(`不支持的 schemaVersion ${String(e.schemaVersion)}(当前支持 ${LEDGER_SCHEMA_VERSION})`)\n }\n if (!Array.isArray(e.tasks)) throw new Error('导入文件的 tasks 不是数组')\n const plan: ImportPlan = { create: [], overwrite: [], invalid: [], ...(e.settings !== undefined ? { settings: asBoardSettings(e.settings) } : {}) }\n const seen = new Set<string>()\n for (const entry of e.tasks) {\n const id = typeof (entry as { id?: unknown })?.id === 'string' ? (entry as { id: string }).id : undefined\n const result = validateImportedTask(entry, now)\n if (!result.ok) {\n plan.invalid.push({ ...(id !== undefined ? { id } : {}), reason: result.reason })\n continue\n }\n if (seen.has(result.task.id)) {\n plan.invalid.push({ id: result.task.id, reason: '文件内重复 id' })\n continue\n }\n seen.add(result.task.id)\n if (knownIds.has(result.task.id)) plan.overwrite.push(result.task)\n else plan.create.push(result.task)\n }\n return plan\n}\n\n/**\n * Compact list-projection of a task (token-friendly for `taskboard_list`).\n * @param task - the task.\n */\nexport type TaskSummary = {\n id: string\n title: string\n workspaceId: string\n urgency: Urgency\n status: TaskStatus\n blocked: boolean\n executionMode: ExecutionMode\n nextRunAt?: number\n model?: TaskModel\n permission?: PermissionMode\n version: number\n claimOwner?: string\n commentCount: number\n lastExecutionOutcome?: ExecutionRecord['outcome']\n /** Checklist progress (present only when the task has a checklist). */\n checklist?: { done: number; total: number }\n trashed: boolean\n}\n\n/**\n * Build the compact summary of a task.\n * @param task - the task.\n */\nexport function summarize(task: TaskRecord): TaskSummary {\n const last = task.executions.length > 0 ? task.executions[task.executions.length - 1] : undefined\n const checklist = task.checklist !== undefined && task.checklist.length > 0 ? checklistProgress(task) : undefined\n return {\n id: task.id,\n title: task.title,\n workspaceId: task.workspaceId,\n urgency: task.urgency,\n status: task.status,\n blocked: task.blocked,\n executionMode: task.execution.mode,\n nextRunAt: task.execution.nextRunAt,\n model: task.model,\n permission: task.permission,\n version: task.version,\n claimOwner: isClaimedBy(task),\n commentCount: task.comments.length,\n lastExecutionOutcome: last?.outcome,\n ...(checklist !== undefined ? { checklist } : {}),\n trashed: task.trashedAt !== undefined,\n }\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/src/client/board/SettingsModal.tsx b/src/client/board/SettingsModal.tsx index 36f16ab..a132dc3 100644 --- a/src/client/board/SettingsModal.tsx +++ b/src/client/board/SettingsModal.tsx @@ -8,6 +8,7 @@ */ import { useEffect, useState } from 'react' import type { BoardController } from '../controller.ts' +import type { CatalogModelItem, CatalogPresetItem } from '../../shared/api.ts' import { DEFAULT_DISPATCH_INTERVAL_MS, DEFAULT_ISOLATION, DEFAULT_MAX_CONCURRENT, DEFAULT_QUEUE_MAX_AGE_MINUTES, DEFAULT_SCHEDULE_MISSED_AFTER_MINUTES, defaultPermissionOf, defaultSyncExternalSessionsOf, dispatchIntervalMsOf, maxConcurrentOf, queueMaxAgeMinutesOf, scheduleMissedAfterMinutesOf, type IsolationMode, type PermissionMode } from '../../shared/protocol.ts' import { useT, type Translate } from '../i18n/runtime.ts' @@ -32,6 +33,19 @@ export function SettingsModal({ controller }: { controller: BoardController }) { const currentMissedAfterMinutes = scheduleMissedAfterMinutesOf(state.ledger.settings) const currentQueueMaxAgeMinutes = queueMaxAgeMinutesOf(state.ledger.settings) const currentDispatchIntervalMs = dispatchIntervalMsOf(state.ledger.settings) + const currentModel = state.ledger.settings?.defaultModel + const currentModelKey = currentModel === undefined ? '' : JSON.stringify({ provider: currentModel.provider, model: currentModel.model }) + const currentEffort = currentModel?.reasoningEffort ?? '' + const currentPreset = state.ledger.settings?.defaultPresetId ?? '' + const [catalog, setCatalog] = useState<CatalogModelItem[]>([]) + const [presets, setPresets] = useState<CatalogPresetItem[]>([]) + const [presetDefault, setPresetDefault] = useState<string | undefined>() + const [draftModel, setDraftModel] = useState(currentModelKey) + const [draftEffort, setDraftEffort] = useState(currentEffort) + const [draftPreset, setDraftPreset] = useState(currentPreset) + const parsedModel = draftModel === '' ? undefined : JSON.parse(draftModel) as { provider: string; model: string } + const selectedModel = catalog.find(m => m.provider === parsedModel?.provider && m.model === parsedModel?.model) + const reasoning = selectedModel?.reasoning const [draftIso, setDraftIso] = useState<IsolationMode>(currentIso) const [draftSync, setDraftSync] = useState<boolean>(currentSync) const [draftPerm, setDraftPerm] = useState<PermissionMode>(currentPerm) @@ -50,7 +64,8 @@ export function SettingsModal({ controller }: { controller: BoardController }) { && Number.isSafeInteger(missedAfterMinutes) && missedAfterMinutes >= 1 && missedAfterMinutes <= 1440 && Number.isSafeInteger(queueMaxAgeMinutes) && queueMaxAgeMinutes >= 0 && queueMaxAgeMinutes <= 10080 && Number.isSafeInteger(dispatchIntervalMs) && dispatchIntervalMs >= 0 && dispatchIntervalMs <= 60000 - const dirty = draftIso !== currentIso || draftSync !== currentSync || draftPerm !== currentPerm + const dirty = draftModel !== currentModelKey || draftEffort !== currentEffort || draftPreset !== currentPreset + || draftIso !== currentIso || draftSync !== currentSync || draftPerm !== currentPerm || draftMaxConcurrent !== String(currentMaxConcurrent) || draftMissedAfterMinutes !== String(currentMissedAfterMinutes) || draftQueueMaxAgeMinutes !== String(currentQueueMaxAgeMinutes) || draftDispatchIntervalMs !== String(currentDispatchIntervalMs) const effectiveStoragePath = storagePath.trim().length === 0 ? state.storage?.defaultDirectory ?? '' : storagePath.trim() @@ -60,11 +75,22 @@ export function SettingsModal({ controller }: { controller: BoardController }) { if (!storageTouched && state.storage !== undefined) setStoragePath(state.storage.currentDirectory) }, [state.storage, storageTouched]) + useEffect(() => { + let active = true + void controller.fetchModelCatalog().then(models => { if (active) setCatalog(models) }).catch(() => {}) + void controller.fetchPresetCatalog().then(roster => { + if (active) { setPresets(roster.presets); setPresetDefault(roster.defaultId) } + }).catch(() => {}) + return () => { active = false } + }, [controller]) + const save = (): void => { void controller.updateSettings({ defaultIsolation: draftIso, syncExternalSessions: draftSync, defaultPermission: draftPerm, + ...(parsedModel !== undefined ? { defaultModel: { ...parsedModel, ...(draftEffort !== '' ? { reasoningEffort: draftEffort } : {}) } } : {}), + ...(draftPreset !== '' ? { defaultPresetId: draftPreset } : {}), maxConcurrent, scheduleMissedAfterMinutes: missedAfterMinutes, queueMaxAgeMinutes, @@ -176,6 +202,53 @@ export function SettingsModal({ controller }: { controller: BoardController }) { </span> </section> + <section className="dsh-atb-diag-sec"> + <h4>{t('set.agent.heading')}</h4> + <p className="dsh-atb-isolation-note">{t('set.agent.hint')}</p> + <div className="dsh-atb-form-subgrid"> + <label className="dsh-atb-field"> + <span className="dsh-atb-field-label">{t('set.agent.model')}</span> + <select className="dsh-atb-input" value={draftModel} onChange={e => { setDraftModel(e.target.value); setDraftEffort('') }}> + <option value="">{t('form.field.modelDefault')}</option> + {parsedModel !== undefined && selectedModel === undefined && ( + <option value={draftModel}>{parsedModel.model} ({parsedModel.provider})</option> + )} + {catalog.map(m => ( + <option key={`${m.provider}/${m.model}`} value={JSON.stringify({ provider: m.provider, model: m.model })}> + {t('form.model.option', { name: m.name ?? m.model, provider: m.provider })} + </option> + ))} + </select> + </label> + <label className="dsh-atb-field"> + <span className="dsh-atb-field-label">{t('set.agent.effort')}</span> + <select className="dsh-atb-input" value={draftEffort} disabled={parsedModel === undefined} onChange={e => setDraftEffort(e.target.value)}> + <option value="">{t('form.effort.follow')}{reasoning?.defaultEffort !== undefined ? t('shared.current', { name: reasoning.efforts.find(e => e.id === reasoning.defaultEffort)?.name ?? reasoning.defaultEffort }) : ''}</option> + {draftEffort !== '' && reasoning !== undefined && !reasoning.efforts.some(e => e.id === draftEffort) && ( + <option value={draftEffort}>{draftEffort}</option> + )} + {reasoning !== undefined ? reasoning.efforts.map(e => ( + <option key={e.id} value={e.id}>{e.name}{e.description ? ` (${e.description})` : ''}</option> + )) : <> + {!['', 'low', 'medium', 'high', 'none'].includes(draftEffort) && <option value={draftEffort}>{draftEffort}</option>} + <option value="low">{t('form.effort.low')}</option> + <option value="medium">{t('form.effort.medium')}</option> + <option value="high">{t('form.effort.high')}</option> + <option value="none">{t('form.effort.none')}</option> + </>} + </select> + </label> + <label className="dsh-atb-field"> + <span className="dsh-atb-field-label">{t('set.agent.preset')}</span> + <select className="dsh-atb-input" value={draftPreset} onChange={e => setDraftPreset(e.target.value)}> + <option value="">{t('form.preset.follow')}{presetDefault !== undefined ? t('shared.current', { name: presets.find(p => p.id === presetDefault)?.name ?? presetDefault }) : ''}</option> + {draftPreset !== '' && !presets.some(p => p.id === draftPreset) && <option value={draftPreset}>{draftPreset}</option>} + {presets.map(p => <option key={p.id} value={p.id}>{p.name ?? p.id}{p.id === presetDefault ? t('form.preset.defaultTag') : ''}</option>)} + </select> + </label> + </div> + </section> + <section className="dsh-atb-diag-sec"> <h4>{t('set.schedule.heading')}</h4> <p className="dsh-atb-isolation-note">{t('set.schedule.hint')}</p> diff --git a/src/client/board/TaskFormModal.tsx b/src/client/board/TaskFormModal.tsx index a0b63ae..c3d3df3 100644 --- a/src/client/board/TaskFormModal.tsx +++ b/src/client/board/TaskFormModal.tsx @@ -209,13 +209,14 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle const [catalog, setCatalog] = useState<CatalogModel[]>([]) // Model & reasoning effort selection: - // In create mode (when not pinned by template), prefill from remembered last choice. - const initialModel = task?.model ?? prefill?.model ?? (!editing ? loadLastModel() : undefined) + // Explicit templates override board defaults, which override the remembered last choice. + // Editing never inherits new-task defaults. + const initialModel = editing ? task.model : prefill?.model ?? state.ledger.settings?.defaultModel ?? loadLastModel() const [model, setModel] = useState(initialModel !== undefined ? JSON.stringify({ provider: initialModel.provider, model: initialModel.model }) : '') const [reasoningEffort, setReasoningEffort] = useState(initialModel?.reasoningEffort ?? '') - // Preset roster (0.3.3): create mode PRE-SELECTS the deployment default - // (标准模式 in this deployment); '' = 跟随部署默认 (submit omits the field). - const initialPreset = task?.presetId ?? prefill?.presetId ?? '' + // Create mode starts from the template/board preset, then the deployment default. + // '' explicitly follows deployment defaults (submit null to bypass board defaults). + const initialPreset = editing ? task.presetId ?? '' : prefill?.presetId ?? state.ledger.settings?.defaultPresetId ?? '' const [presetId, setPresetId] = useState(initialPreset) const [presets, setPresets] = useState<Array<{ id: string; name?: string }>>([]) const [presetDefault, setPresetDefault] = useState<string | undefined>(undefined) @@ -329,7 +330,7 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle return isolation } - /** Preset payload: '' = follow the deployment default (submit omits). */ + /** Preset payload: '' explicitly follows the deployment default (submit null). */ const presetPayload = (): string | undefined => (presetId.trim().length > 0 ? presetId.trim() : undefined) /** Checklist rows with non-empty text (blank rows are dropped on submit). */ @@ -380,9 +381,9 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle description: description.length > 0 ? description : undefined, prompt: prompt.length > 0 ? prompt : undefined, execution: executionPayload(), - model: picked, + model: picked ?? null, ...(isolationOut !== undefined ? { isolation: isolationOut } : {}), - ...(presetOut !== undefined ? { presetId: presetOut } : {}), + presetId: presetOut ?? null, permission, ...(rows.length > 0 ? { checklist: rows.map(r => r.text) } : {}), }) @@ -422,9 +423,9 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle description: description.length > 0 ? description : undefined, prompt: prompt.length > 0 ? prompt : undefined, execution: executionPayload(), - model: picked, + model: picked ?? null, ...(isolationOut !== undefined ? { isolation: isolationOut } : {}), - ...(presetOut !== undefined ? { presetId: presetOut } : {}), + presetId: presetOut ?? null, permission, ...(rows.length > 0 ? { checklist: rows.map(r => r.text) } : {}), }) @@ -493,6 +494,7 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle }} > <option value="">{t('form.field.modelDefault')}</option> + {parsedModel !== undefined && currentCatalogModel === undefined && <option value={model}>{parsedModel.model} ({parsedModel.provider})</option>} {catalog.map(m => ( <option key={`${m.provider}/${m.model}`} value={JSON.stringify({ provider: m.provider, model: m.model })}> {t('form.model.option', { name: m.name ?? m.model, provider: m.provider })} @@ -527,10 +529,11 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle </Field> )} - {presets.length > 0 && ( + {(presets.length > 0 || presetId !== '') && ( <Field label={t('form.field.preset')}> <select value={presetId} onChange={e => setPresetId(e.target.value)} title={t('form.field.presetTitle')}> <option value="">{t('form.preset.follow')}{presetDefault !== undefined ? t('shared.current', { name: presets.find(p => p.id === presetDefault)?.name ?? presetDefault }) : ''}</option> + {presetId !== '' && !presets.some(p => p.id === presetId) && <option value={presetId}>{presetId}</option>} {presets.map(p => ( <option key={p.id} value={p.id}> {p.name ?? p.id}{p.id === presetDefault ? t('form.preset.defaultTag') : ''} diff --git a/src/client/i18n/en.ts b/src/client/i18n/en.ts index 88793b7..86be65c 100644 --- a/src/client/i18n/en.ts +++ b/src/client/i18n/en.ts @@ -397,6 +397,11 @@ export const en: TaskboardDict = { 'set.aria': 'Board settings', 'set.title': 'Board settings', 'set.subtitle': 'New-task defaults, scheduling, session sync, and local data storage', + 'set.agent.heading': 'Default model and Agent preset', + 'set.agent.hint': 'New tasks only: explicit task or template choices take priority. Existing tasks are unchanged. Reasoning effort is saved with the model and resets when switching models.', + 'set.agent.model': 'Default model', + 'set.agent.effort': 'Default reasoning effort', + 'set.agent.preset': 'Default Agent preset', 'set.iso.heading': 'Default execution isolation', 'set.iso.noneHint': 'No git; works directly in the project directory (factory default)', 'set.iso.worktreeHint': 'Each execution runs on its own worktree branch (task/title+ID), isolated from the others; multi-repo workspaces are mirrored whole (one branch per repo)', diff --git a/src/client/i18n/zh.ts b/src/client/i18n/zh.ts index cf72de5..afe5cce 100644 --- a/src/client/i18n/zh.ts +++ b/src/client/i18n/zh.ts @@ -399,6 +399,11 @@ export const zh = { 'set.aria': '看板设置', 'set.title': '看板设置', 'set.subtitle': '新建任务、调度、会话同步与本地数据存储', + 'set.agent.heading': '默认模型与 Agent preset', + 'set.agent.hint': '仅用于新建任务:任务或模板的显式选择优先;修改设置不会改写已有任务。推理强度随所选模型保存,切换模型时重置。', + 'set.agent.model': '默认模型', + 'set.agent.effort': '默认推理强度', + 'set.agent.preset': '默认 Agent preset', 'set.iso.heading': '默认执行隔离', 'set.iso.noneHint': '不使用 git,直接在项目目录工作(出厂默认)', 'set.iso.worktreeHint': '每次执行在独立 worktree 分支上进行(task/标题+ID),互不污染;多仓库工作区自动整区镜像(每仓库独立分支)', diff --git a/src/host/routes.ts b/src/host/routes.ts index 2fad74b..51a9546 100644 --- a/src/host/routes.ts +++ b/src/host/routes.ts @@ -699,13 +699,15 @@ export function registerTaskboardRoutes(ctx: Context, options: TaskboardRoutesOp if (execution.reuseSessionId !== undefined && !workspaces.sessionIds?.(workspaceId)?.includes(execution.reuseSessionId)) { throw new Error('Error: invalid_input: selected session is not available in this workspace') } - const model = body.model === undefined ? undefined : checkModel(body.model, options.modelProviders) + const settings = store.snapshot().settings + const modelRaw = body.model === undefined ? settings?.defaultModel : body.model + const model = modelRaw == null ? undefined : checkModel(modelRaw, options.modelProviders) const isolationRaw = str(body, 'isolation') // 0.5.0: an omitted isolation is MATERIALIZED from the board // setting (看板设置) at creation, so later setting changes never // rewrite existing tasks. const isolation = isolationRaw === null ? defaultIsolationOf(store.snapshot().settings) : asIsolation(isolationRaw) - const presetId = normalizePresetId(str(body, 'presetId')) + const presetId = body.presetId === undefined ? settings?.defaultPresetId : normalizePresetId(str(body, 'presetId')) const permissionRaw = str(body, 'permission') const permission = permissionRaw === null ? defaultPermissionOf(store.snapshot().settings) : asPermission(permissionRaw) let checklist: TaskRecord['checklist'] = undefined diff --git a/src/host/tools.ts b/src/host/tools.ts index 854a5b7..b953848 100644 --- a/src/host/tools.ts +++ b/src/host/tools.ts @@ -409,7 +409,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar model: { type: 'object', additionalProperties: false, - description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omit to use the default model.', + description: 'Pin executions to one configured model: { provider, model, reasoningEffort? }. Omitted → board default model and effort, then deployment default.', properties: { provider: { type: 'string', description: 'Provider route id.' }, model: { type: 'string', description: 'Provider-owned model id.' }, @@ -426,7 +426,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar }, presetId: { type: 'string', - description: 'Agent preset the execution session is composed from (its tool set / persona); default = the deployment default preset. Optional.', + description: 'Agent preset for execution sessions (tool set / persona). Omitted → board default, then deployment default; empty string follows deployment default.', }, checklist: { type: 'array', @@ -450,7 +450,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar description?: string prompt?: string execution?: { mode?: string; cron?: string; runAt?: string | number } - model?: { provider?: string; model?: string } + model?: { provider?: string; model?: string; reasoningEffort?: string } isolation?: string permission?: string presetId?: string @@ -468,7 +468,8 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar throw new ToolError(ERR.invalidTransition, 'a new task must start as backlog or todo (in_progress requires claiming the task)') } const execution = normalizeExecution(args.execution ?? {}, deps.now()) - const model = args.model !== undefined ? checkModel(deps, args.model) : undefined + const modelRaw = args.model ?? store.snapshot().settings?.defaultModel + const model = modelRaw !== undefined ? checkModel(deps, modelRaw) : undefined // 0.5.0: an omitted isolation is MATERIALIZED from the board setting // (看板设置) at creation, so later setting changes never rewrite // existing tasks. @@ -476,7 +477,7 @@ export function registerTaskboardTools(ctx: ToolContextFace, deps: ToolDeps): Ar // Match the GUI create route: freeze the current board default onto // the task so later settings changes do not silently alter a schedule. const permission = args.permission === undefined ? defaultPermissionOf(store.snapshot().settings) : asPermission(args.permission) - const presetId = args.presetId?.trim() || undefined + const presetId = args.presetId === undefined ? store.snapshot().settings?.defaultPresetId : args.presetId.trim() || undefined // T9: match the GUI create route — trim and drop blank lines instead // of failing the whole call over one empty string. const checklistTexts = args.checklist?.map(c => c.trim()).filter(c => c.length > 0) diff --git a/src/shared/api.ts b/src/shared/api.ts index 0812f90..5d60aad 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -68,11 +68,12 @@ export type CreateTaskBody = { description?: string prompt?: string execution?: { mode?: string; cron?: string; runAt?: string | number; periodicCompletion?: 'rearm' | 'spawn'; reuseSessionId?: string; sessionReuseMode?: 'fresh' | 'reuse' } - model?: TaskModel + /** Omitted = board default; null = follow the deployment default explicitly. */ + model?: TaskModel | null /** Code isolation for executions ('worktree' | 'none'); omitted = default. */ isolation?: string - /** Agent preset for execution sessions; omitted = deployment default. */ - presetId?: string + /** Omitted = board default; null = follow the deployment default explicitly. */ + presetId?: string | null /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */ permission?: string /** Acceptance checklist item texts (host mints ids, all unchecked). */ @@ -235,6 +236,10 @@ export type UpdateSettingsBody = { syncExternalSessions?: boolean /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */ defaultPermission?: string + /** Default model and optional reasoning effort for NEW tasks; omitted = deployment default. */ + defaultModel?: TaskModel + /** Default agent preset for NEW tasks; omitted = deployment default. */ + defaultPresetId?: string /** Global simultaneous execution cap (1–100). */ maxConcurrent?: number /** Offline missed-window threshold in whole minutes (1–1440). */ diff --git a/src/shared/protocol.ts b/src/shared/protocol.ts index d4b3dbf..ec035ee 100644 --- a/src/shared/protocol.ts +++ b/src/shared/protocol.ts @@ -186,6 +186,10 @@ export type BoardSettings = { syncExternalSessions?: boolean /** Default permission preset applied when a NEW task is created without an explicit choice (0.5.5, default: 'workspace-write'). */ defaultPermission?: PermissionMode + /** Model and optional reasoning effort materialized onto NEW tasks; absent = deployment default. */ + defaultModel?: TaskModel + /** Agent preset materialized onto NEW tasks; absent = deployment default. */ + defaultPresetId?: string /** Global cap for simultaneously running taskboard executions. */ maxConcurrent?: number /** Only never-queued windows older than this are classified as offline misses. */ @@ -218,6 +222,14 @@ export function asBoardSettings(raw: unknown): BoardSettings { if (e.defaultPermission !== undefined) { out.defaultPermission = asPermission(e.defaultPermission) } + if (e.defaultModel !== undefined) { + out.defaultModel = normalizeModel(e.defaultModel) + } + if (e.defaultPresetId !== undefined) { + if (typeof e.defaultPresetId !== 'string') throw new Error('defaultPresetId must be a string') + const id = e.defaultPresetId.trim() + if (id.length > 0) out.defaultPresetId = id + } if (e.maxConcurrent !== undefined) { if (typeof e.maxConcurrent !== 'number' || !Number.isSafeInteger(e.maxConcurrent) || e.maxConcurrent < MIN_MAX_CONCURRENT || e.maxConcurrent > MAX_MAX_CONCURRENT) { diff --git a/tests/default-settings.spec.ts b/tests/default-settings.spec.ts new file mode 100644 index 0000000..03472af --- /dev/null +++ b/tests/default-settings.spec.ts @@ -0,0 +1,140 @@ +// @vitest-environment jsdom +import React from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { BoardController } from '../src/client/controller.ts' +import { SettingsModal } from '../src/client/board/SettingsModal.tsx' +import { TaskFormModal, saveLastModel } from '../src/client/board/TaskFormModal.tsx' +import type { BoardSettings, TaskRecord } from '../src/shared/protocol.ts' +import type { CreateTaskBody, UpdateSettingsBody, UpdateTaskBody } from '../src/shared/api.ts' +import { waitFor } from './wait-for.ts' +import { disposeI18n } from '../src/client/i18n/runtime.ts' + +const boardModel = { provider: 'p', model: 'reasoner', reasoningEffort: 'high' } +const modelKey = (model: { provider: string; model: string }) => JSON.stringify({ provider: model.provider, model: model.model }) +const cleanups: Array<() => void> = [] +beforeEach(() => { localStorage.clear(); document.documentElement.lang = 'zh-CN'; disposeI18n() }) +afterEach(() => { cleanups.splice(0).reverse().forEach(dispose => dispose()); localStorage.clear() }) + +async function setup(initial: BoardSettings = {}) { + let settings = initial + const saved: UpdateSettingsBody[] = [] + const created: CreateTaskBody[] = [] + const updated: UpdateTaskBody[] = [] + const controller = new BoardController({ + state: async () => ({ schemaVersion: 1, revision: 1, tasks: [], settings }), + workspaces: async () => [{ id: 'ws-a', path: '/p/a', title: 'A', sessionCount: 0 }], + stream: () => () => {}, + updateSettings: async (body: UpdateSettingsBody) => { saved.push(body); settings = body as BoardSettings; return settings }, + create: async (body: CreateTaskBody) => { created.push(body); return { id: 'new-task', version: 1 } }, + update: async (_id: string, body: UpdateTaskBody) => { updated.push(body); return { id: 'old-task', version: 2 } }, + } as never) + controller.installModelCatalog(async () => [ + { provider: 'p', model: 'reasoner', name: 'Reasoner', reasoning: { efforts: [{ id: 'high', name: 'High' }, { id: 'low', name: 'Low' }], defaultEffort: 'low' } }, + { provider: 'p', model: 'chat', name: 'Chat', reasoning: { efforts: [{ id: 'medium', name: 'Medium' }], defaultEffort: 'medium' } }, + ]) + controller.installPresetRoster(async () => ({ presets: [{ id: 'standard', name: 'Standard' }, { id: 'custom', name: 'Custom' }], defaultId: 'standard' })) + controller.start() + cleanups.push(() => controller.dispose()) + await waitFor(() => controller.getSnapshot().workspaces.length === 1) + const host = document.createElement('div') + document.body.append(host) + const root: Root = createRoot(host) + cleanups.push(() => { root.unmount(); host.remove() }) + const field = (label: string): HTMLSelectElement => { + const found = Array.from(host.querySelectorAll('label')).find(el => el.querySelector('.dsh-atb-field-label')?.textContent === label)?.querySelector('select') + expect(found).toBeTruthy() + return found! + } + const change = (select: HTMLSelectElement, value: string) => { select.value = value; select.dispatchEvent(new Event('change', { bubbles: true })) } + const button = (text: string) => Array.from(host.querySelectorAll<HTMLButtonElement>('button')).find(el => el.textContent === text)! + const title = () => { + const input = host.querySelector<HTMLInputElement>('input[maxlength="200"]')! + Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')!.set!.call(input, 'Defaults task') + input.dispatchEvent(new Event('input', { bubbles: true })) + } + return { controller, root, host, saved, created, updated, field, change, button, title } +} + +describe('board model, effort and preset defaults', () => { + it('stages and saves defaults, resets effort on model change, and can clear back to deployment defaults', async () => { + const view = await setup({ defaultModel: boardModel, defaultPresetId: 'custom' }) + const { root, controller, field, change, button, saved } = view + root.render(React.createElement(SettingsModal, { controller })) + await waitFor(() => field('默认模型').options.length === 3) + expect(field('默认模型').value).toBe(modelKey(boardModel)) + expect(field('默认推理强度').value).toBe('high') + expect(field('默认 Agent preset').value).toBe('custom') + expect(button('保存设置').disabled).toBe(true) + change(field('默认模型'), modelKey({ provider: 'p', model: 'chat' })) + await waitFor(() => field('默认推理强度').value === '') + expect(Array.from(field('默认推理强度').options).map(o => o.value)).toEqual(['', 'medium']) + change(field('默认推理强度'), 'medium') + change(field('默认 Agent preset'), 'standard') + await waitFor(() => !button('保存设置').disabled) + button('保存设置').click() + await waitFor(() => saved.length === 1) + expect(saved[0]).toMatchObject({ defaultModel: { provider: 'p', model: 'chat', reasoningEffort: 'medium' }, defaultPresetId: 'standard' }) + root.render(null) + await waitFor(() => view.host.childElementCount === 0) + root.render(React.createElement(SettingsModal, { controller })) + await waitFor(() => field('默认模型').value === modelKey({ provider: 'p', model: 'chat' })) + change(field('默认模型'), '') + change(field('默认 Agent preset'), '') + await waitFor(() => field('默认推理强度').disabled && !button('保存设置').disabled) + button('保存设置').click() + await waitFor(() => saved.length === 2) + expect(saved[1]).not.toHaveProperty('defaultModel') + expect(saved[1]).not.toHaveProperty('defaultPresetId') + }) + + it('preserves saved selections when the runtime catalog is unavailable', async () => { + const view = await setup({ defaultModel: { ...boardModel, reasoningEffort: 'xhigh' }, defaultPresetId: 'missing-preset' }) + view.controller.installModelCatalog(async () => []) + view.controller.installPresetRoster(async () => ({ presets: [] })) + view.root.render(React.createElement(SettingsModal, { controller: view.controller })) + await waitFor(() => view.field('默认模型').value === modelKey(boardModel)) + expect(view.field('默认推理强度').value).toBe('xhigh') + expect(view.field('默认 Agent preset').value).toBe('missing-preset') + expect(view.button('保存设置').disabled).toBe(true) + }) + + it('new tasks inherit board defaults ahead of remembered model, and can explicitly follow deployment defaults', async () => { + saveLastModel({ provider: 'p', model: 'chat', reasoningEffort: 'medium' }) + const view = await setup({ defaultModel: boardModel, defaultPresetId: 'custom' }) + const { root, controller, field, change, button, title, created } = view + root.render(React.createElement(TaskFormModal, { controller })) + await waitFor(() => field('执行模式(preset)').options.length === 3) + expect(field('模型(默认 = 会话默认模型)').value).toBe(modelKey(boardModel)) + expect(field('思考强度(Reasoning Effort)').value).toBe('high') + expect(field('执行模式(preset)').value).toBe('custom') + title() + await waitFor(() => !button('创建任务').disabled) + button('创建任务').click() + await waitFor(() => created.length === 1) + expect(created[0]).toMatchObject({ model: boardModel, presetId: 'custom' }) + change(field('模型(默认 = 会话默认模型)'), '') + change(field('执行模式(preset)'), '') + await waitFor(() => field('模型(默认 = 会话默认模型)').value === '' && field('执行模式(preset)').value === '' && !button('创建任务').disabled) + button('创建任务').click() + await waitFor(() => created.length === 2) + expect(created[1]).toMatchObject({ model: null, presetId: null }) + }) + + it('template choices override defaults and editing an unpinned task never inherits defaults', async () => { + const view = await setup({ defaultModel: boardModel, defaultPresetId: 'custom' }) + view.controller.newFromTemplate({ model: { provider: 'p', model: 'chat' }, presetId: 'standard' }) + view.root.render(React.createElement(TaskFormModal, { controller: view.controller })) + await waitFor(() => view.field('执行模式(preset)').options.length === 3) + expect(view.field('模型(默认 = 会话默认模型)').value).toBe(modelKey({ provider: 'p', model: 'chat' })) + expect(view.field('思考强度(Reasoning Effort)').value).toBe('') + expect(view.field('执行模式(preset)').value).toBe('standard') + view.root.render(null) + await waitFor(() => view.host.childElementCount === 0) + const task: TaskRecord = { id: 'old-task', title: 'Old task', description: '', prompt: '', workspaceId: 'ws-a', urgency: 'normal', status: 'todo', blocked: false, execution: { mode: 'claim' }, version: 1, createdAt: 0, updatedAt: 0, createdBy: { kind: 'user' }, updatedBy: { kind: 'user' }, comments: [], executions: [] } + view.root.render(React.createElement(TaskFormModal, { controller: view.controller, task })) + await waitFor(() => view.field('执行模式(preset)').options.length === 3) + expect(view.field('模型(默认 = 会话默认模型)').value).toBe('') + expect(view.field('执行模式(preset)').value).toBe('') + }) +}) diff --git a/tests/protocol.spec.ts b/tests/protocol.spec.ts index 17925f1..9a177fb 100644 --- a/tests/protocol.spec.ts +++ b/tests/protocol.spec.ts @@ -270,6 +270,21 @@ describe('model normalization (reasoning effort support)', () => { // --------------------------------------------------------------------------- describe('board settings & default isolation (0.5.0)', () => { + it('normalizes model, reasoning effort and preset defaults; rejects malformed values', () => { + expect(asBoardSettings({ + defaultModel: { provider: ' p ', model: ' m ', reasoningEffort: ' high ', junk: true }, + defaultPresetId: ' standard ', + })).toEqual({ defaultModel: { provider: 'p', model: 'm', reasoningEffort: 'high' }, defaultPresetId: 'standard' }) + expect(asBoardSettings({ defaultPresetId: ' ' })).toEqual({}) + expect(() => asBoardSettings({ defaultPresetId: 42 })).toThrow('defaultPresetId') + expect(() => asBoardSettings({ defaultModel: null })).toThrow('model must be') + expect(() => asBoardSettings({ defaultModel: { provider: 'p', model: '' } })).toThrow('non-empty') + const imported = validateLedgerImport({ schemaVersion: 1, tasks: [], settings: { + defaultModel: { provider: 'p', model: 'm', reasoningEffort: 'high' }, defaultPresetId: 'standard', + } }, new Set(), 7_000) + expect(imported.settings).toEqual({ defaultModel: { provider: 'p', model: 'm', reasoningEffort: 'high' }, defaultPresetId: 'standard' }) + }) + it('factory default is 原目录执行 (none); explicit task values always win', () => { expect(DEFAULT_ISOLATION).toBe('none') expect(effectiveIsolation({ isolation: undefined })).toBe('none') @@ -429,32 +444,20 @@ describe('TaskStore', () => { expect(second.snapshot().revision).toBe(2) }) - it('persists and restores board settings across store reloads (0.5.5)', async () => { + it('persists and restores board settings across store reloads', async () => { const file = join(dir, 'settings-reload.json') const store = new TaskStore({ file }) - await store.mutate('settings-updated', ledger => { - ledger.settings = { - defaultIsolation: 'worktree', - syncExternalSessions: true, - defaultPermission: 'read-only', - } - return [] - }) - - expect(store.snapshot().settings).toEqual({ - defaultIsolation: 'worktree', - syncExternalSessions: true, - defaultPermission: 'read-only', + const settings = asBoardSettings({ + defaultIsolation: 'worktree', syncExternalSessions: true, defaultPermission: 'read-only', + defaultModel: { provider: 'deepseek', model: 'reasoner', reasoningEffort: 'high' }, + defaultPresetId: 'standard', }) - - // Reload in a completely fresh store instance simulating server restart + await store.mutate('settings-updated', ledger => { ledger.settings = settings; return [] }) + expect(store.snapshot().settings).toEqual(settings) + // A fresh store instance simulates a server restart. const restarted = new TaskStore({ file }) await restarted.load() - expect(restarted.snapshot().settings).toEqual({ - defaultIsolation: 'worktree', - syncExternalSessions: true, - defaultPermission: 'read-only', - }) + expect(restarted.snapshot().settings).toEqual(settings) }) it('quarantines a corrupt ledger instead of throwing', async () => { diff --git a/tests/routes.spec.ts b/tests/routes.spec.ts index 30a83c2..43ba6dc 100644 --- a/tests/routes.spec.ts +++ b/tests/routes.spec.ts @@ -1163,6 +1163,36 @@ describe('taskboard routes 0.5.0 (board settings → default isolation)', () => expect(state.value.settings).toEqual({ defaultIsolation: 'worktree', maxConcurrent: 8, scheduleMissedAfterMinutes: 15 }) }) + it('materializes model/effort/preset defaults, honors overrides and null, and leaves existing tasks unchanged', async () => { + const defaultModel = { provider: 'prov-a', model: 'model-a', reasoningEffort: 'high' } + const saved = await post('/dsh-taskboard/settings/update', { defaultModel, defaultPresetId: ' standard ' }) + expect(saved.status).toBe(200) + expect(saved.json.value).toEqual({ defaultModel, defaultPresetId: 'standard' }) + const create = async (extra: object = {}) => { + const res = await post('/dsh-taskboard/tasks', { title: 'Defaults task', workspaceId: 'ws-a', urgency: 'normal', ...extra }) + expect(res.status).toBe(201) + return store.get(res.json.value.id)! + } + const inherited = await create() + expect(inherited.model).toEqual(defaultModel) + expect(inherited.presetId).toBe('standard') + const overridden = await create({ model: { provider: 'prov-a', model: 'other' }, presetId: 'custom' }) + expect(overridden.model).toEqual({ provider: 'prov-a', model: 'other' }) + expect(overridden.presetId).toBe('custom') + const deployment = await create({ model: null, presetId: null }) + expect(deployment.model).toBeUndefined() + expect(deployment.presetId).toBeUndefined() + const invalid = await post('/dsh-taskboard/settings/update', { defaultModel: { provider: '', model: 'bad' } }) + expect(invalid.status).toBe(400) + expect(store.snapshot().settings?.defaultModel).toEqual(defaultModel) + await post('/dsh-taskboard/settings/update', {}) + expect(store.get(inherited.id)?.model).toEqual(defaultModel) + expect(store.get(inherited.id)?.presetId).toBe('standard') + const reset = await create() + expect(reset.model).toBeUndefined() + expect(reset.presetId).toBeUndefined() + }) + it('create materializes the board default on omitted isolation; explicit wins; earlier tasks unaffected', async () => { // Self-contained precondition: pin the board default to worktree here. const pinned = await post('/dsh-taskboard/settings/update', { defaultIsolation: 'worktree' }) diff --git a/tests/tools.spec.ts b/tests/tools.spec.ts index 1ee8993..2b495f5 100644 --- a/tests/tools.spec.ts +++ b/tests/tools.spec.ts @@ -224,6 +224,34 @@ describe('taskboard tool outputs', () => { // ---------------------------------------------------------------- 0.5.0 describe('taskboard_create default isolation (0.5.0 board settings)', () => { + it('inherits board model/effort/preset, explicit choices win, and reset does not rewrite old tasks', async () => { + const { disposers, tool, exec, store } = await setup({ modelProviders: () => ['deepseek'] }) + const defaultModel = { provider: 'deepseek', model: 'reasoner', reasoningEffort: 'high' } + await store.mutate('settings-updated', ledger => { + ledger.settings = { defaultModel, defaultPresetId: 'standard' } + return [] + }) + const create = async (extra: object = {}) => { + const result = await tool('taskboard_create').execute({ title: 'Defaults', workspaceId: 'ws-a', urgency: 'normal', ...extra }, exec) as { task: { id: string } } + assertLossless(result) + return store.get(result.task.id)! + } + const inherited = await create() + expect(inherited.model).toEqual(defaultModel) + expect(inherited.presetId).toBe('standard') + const explicit = await create({ model: { provider: 'deepseek', model: 'chat' }, presetId: 'custom' }) + expect(explicit.model).toEqual({ provider: 'deepseek', model: 'chat' }) + expect(explicit.presetId).toBe('custom') + expect((await create({ presetId: '' })).presetId).toBeUndefined() + await store.mutate('settings-updated', ledger => { ledger.settings = {}; return [] }) + expect(store.get(inherited.id)?.model).toEqual(defaultModel) + expect(store.get(inherited.id)?.presetId).toBe('standard') + const reset = await create() + expect(reset.model).toBeUndefined() + expect(reset.presetId).toBeUndefined() + for (const dispose of disposers) dispose() + }) + it('omitted isolation materializes the board default (factory none); explicit wins; invalid rejected', async () => { const { disposers, tool, exec, store } = await setup() From baed064d25b655cbbe3e33cdf48845918246f543 Mon Sep 17 00:00:00 2001 From: chengang <cg3940@qq.com> Date: Thu, 1 Oct 2026 17:54:16 +0800 Subject: [PATCH 2/2] fix: preserve unavailable reasoning effort in task form --- lib/client.js | 4 ++-- src/client/board/TaskFormModal.tsx | 5 +++++ tests/default-settings.spec.ts | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/client.js b/lib/client.js index 605817a..ba6d8f8 100644 --- a/lib/client.js +++ b/lib/client.js @@ -16,7 +16,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object. From: {from} To: {to} -Writes queue briefly during migration.`,"set.storage.migrated":`Migration succeeded — data now lives in {path}`,"set.storage.warnings":`Migration finished, but some old files could not be removed: {warnings}`,"form.field.permission":`Execution permission`,"form.perm.write":`Workspace write`,"form.perm.readOnly":`Read-only`,"form.perm.fullAccess":`Full access`,"form.perm.defaultTag":` (default)`,"set.perm.heading":`Default execution permission`,"set.perm.writeName":`📁 Workspace write (factory default)`,"set.perm.writeHint":`Read-write access to the workspace and temp directories; writes need no extra confirmation`,"set.perm.readOnlyName":`🔒 Read-only`,"set.perm.readOnlyHint":`Read-only viewing and search only; no file changes or destructive commands`,"set.perm.fullName":`⚡ Full access`,"set.perm.fullHint":`Unrestricted: whole-disk access and external system commands`,"set.perm.current":`Currently saved default: {current}. The execution permission preset applied to new tasks.`,"set.schedule.heading":`Scheduled execution`,"set.schedule.hint":`Takes effect for subsequent scheduler and execution checks. The offline window only classifies historical triggers while the host was down; a queue shelf life of 0 retains queued work across restarts.`,"set.schedule.concurrent":`Maximum concurrent executions (1–100)`,"set.schedule.missedAfter":`Offline missed window, minutes (1–1440)`,"board.queue.chip":`Queued {n}`,"queue.panel.title":`Execution queue`,"queue.depth":`Queued`,"queue.wait":`Longest wait`,"queue.clear":`Clear queue`,"queue.confirm":`Confirm clear`,"queue.cleared":`Cleared {n} queued task(s).`,"set.schedule.queueMaxAge":`Queue shelf life, minutes (0 = retain indefinitely; max 10080)`,"set.schedule.dispatchInterval":`Scheduled-session start interval, ms (default 1000; 0 = no throttle; max 60000)`,"set.schedule.invalid":`Enter whole numbers within the allowed ranges before saving.`,"card.badge.permReadOnly":`🔒 Read-only`,"card.badge.permReadOnlyTitle":`Permission: read-only`,"card.badge.permFull":`⚡ Full access`,"card.badge.permFullTitle":`Permission: full access`,"detail.chip.permReadOnly":`Read-only`,"detail.chip.permFull":`Full access`,"detail.chip.permWrite":`Workspace write`,"tpl.meta.permReadOnly":`read-only`,"tpl.meta.permFull":`full access`,"md.imageAlt":`Image {n}`,"md.imageTitle":`Click to view full size ({alt})`,"md.lightboxAlt":`Full-size preview`,"md.closePreview":`Close preview`,"image.add":`Insert image`,"image.uploading":`Uploading…`,"image.hint":`Choose, paste, or drop PNG/JPEG/GIF/WebP images up to 5 MB each`,"image.defaultAlt":`Image`,"slash.aria":`Quick commands and skills`,"slash.title":`Quick command and skill completion`,"slash.hint":`↑↓ navigate · Enter / Tab pick · Esc close`,"slash.badge.command":`⚡ command`,"slash.badge.skill":`🧩 skill`,"slash.tipA":`💡 Type`,"slash.tipB":`to autocomplete slash commands and agent skills`,"slash.cmd.goal.desc":`Autonomously drive long-horizon goals to completion`,"slash.cmd.goal.hint":`<goal description>`,"slash.cmd.schedule.desc":`Set one-off or recurring cron schedules`,"slash.cmd.schedule.hint":`<time or expression>`,"slash.cmd.plan.desc":`Draft a step-by-step plan and get user confirmation before acting`,"slash.cmd.browser.desc":`Launch a browser for page interaction and live search`,"slash.cmd.grill-me.desc":`Align requirements and design intent through one-question-at-a-time interviews`,"slash.cmd.teamwork-preview.desc":`Multi-agent collaboration and team workflow preview`,"slash.cmd.learn.desc":`Capture solutions and new rules into the knowledge base`,"slash.cmd.review.desc":`Multi-axis code review (correctness, architecture, security)`,"slash.cmd.security.desc":`Security hardening and vulnerability scanning`,"slash.cmd.permission.desc":`Switch the session permission level (read-only / workspace-write / full-access)`,"slash.cmd.permission.hint":`<preset>`,"slash.skill.frontend-ui-engineering":`Build production-grade, accessible frontend interfaces and components`,"slash.skill.api-and-interface-design":`Design stable, well-bounded REST / RPC interfaces`,"slash.skill.test-driven-development":`Test-driven development (TDD): unit and integration tests`,"slash.skill.debugging-and-error-recovery":`Systematically locate bug root causes and recover`,"slash.skill.performance-optimization":`Frontend/backend performance tuning and query optimization`,"slash.skill.ci-cd-and-automation":`Build automation, CI/CD pipelines and quality gates`,"slash.skill.code-review-and-quality":`Multi-axis code review and refactoring guidance`,"slash.skill.code-simplification":`Simplify complex logic for readability and maintainability`,"slash.skill.context-engineering":`Optimize context structure and prompt engineering`,"slash.skill.doubt-driven-development":`Adversarial, doubt-driven review of core logic`,"slash.skill.git-workflow-and-versioning":`Git workflow, branching, semantic versioning and changelogs`,"slash.skill.idea-refine":`Refine ideas and test hypotheses via divergent-convergent thinking`,"slash.skill.incremental-implementation":`Deliver multi-file changes in small, incremental steps`,"slash.skill.interview-me":`Deep interviews to surface true intent`,"slash.skill.memory-leak-debugging":`Diagnose JavaScript/Node.js memory leaks`,"slash.skill.observability-and-instrumentation":`Add logging, metrics and distributed tracing`,"slash.skill.planning-and-task-breakdown":`Break complex requirements into ordered, executable tasks`,"slash.skill.security-and-hardening":`Harden against vulnerabilities; input filtering and auth hardening`,"slash.skill.shipping-and-launch":`Pre-launch checklists and rollback strategies`,"slash.skill.source-driven-development":`Design from authoritative docs and source code`,"slash.skill.spec-driven-development":`Write clear technical specs before coding`,"slash.skill.using-agent-skills":`Discover and invoke agent skills dynamically`,"sys.execFailed":`[System] Execution failed: {error}; the task was returned to todo.`,"sys.endedWithComment":`[System] The execution session ended with comments but was not moved to in review; the system moved it to in review automatically.`,"sys.endedNoHandoff":`[System] The execution session ended without a protocol handoff (no comments, not moved to in review); the system moved it to in review automatically — review it, then send back or accept.`,"sys.sessionError":`[System] Session execution error: {error}; the task was returned to todo.`,"sys.sessionDone":`[System] The session finished; automatically moved to in review.`,"sys.cronDead":`[System] The cron expression {cron} has no trigger time within 4 years; scheduling disabled — fix the cron and re-enable.`,"sys.periodicHandoff":`[System] Periodic run finished; the schedule now continues on the new todo card {nextTaskId}. Review this card, then accept.`,"sys.spawnedFrom":`[System] The previous periodic run finished (execution {executionId}); this card carries the schedule onward. See {sourceId} for the finished round.`,"sys.runAtMissed":`[System] The one-shot trigger time was missed (host was down); it will not re-fire. Run manually or reschedule.`,"sys.mergeSingle":`[System] Branch {branch} merged into the main worktree (--no-ff).`,"sys.mergeMulti":`[System] Branches merged per repo (--no-ff): {summary}`}};let P,F,I={active:B(),revision:0};const L=new Set;let R,re;function z(){re!==void 0&&(clearInterval(re),re=void 0);try{R?.disconnect()}catch{}R=void 0}function ie(e){return e===`zh`||e===`en`}function B(){try{if(typeof document<`u`){let e=document.documentElement.lang;if(typeof e==`string`&&e.length>0){let t=e.toLowerCase();if(t.startsWith(`zh`))return`zh`;if(t.startsWith(`en`))return`en`}}}catch{}try{if(typeof navigator<`u`)return(navigator.language??`en`).toLowerCase().startsWith(`zh`)?`zh`:`en`}catch{}return`en`}function ae(e){let t=e;if(!(t==null||typeof t.getSnapshot!=`function`||typeof t.subscribe!=`function`))return t}function oe(e){z();try{typeof MutationObserver<`u`&&typeof document<`u`&&(R=new MutationObserver(()=>{if(P!==void 0)return;let e=B();e!==I.active&&se({active:e,revision:I.revision+1})}),R.observe(document.documentElement,{attributes:!0,attributeFilter:[`lang`]}))}catch{}if(e===void 0)return;let t=0;re=setInterval(()=>{t+=1;let n;try{n=ae(e())}catch{n=void 0}if(n!==void 0){V(n);return}t>=8&&z()},250)}function se(e){if(!(e.active===I.active&&e.revision===I.revision)){I=e;for(let e of L)e()}}function V(e,t){let n=ae(e);if(n===void 0){se({active:B(),revision:0}),oe(t);return}z(),P=n;let r=()=>{try{let e=n.getSnapshot();se({active:ie(e.active)?e.active:B(),revision:e.revision})}catch{}};r(),F=n.subscribe(r)}function ce(){z();try{F?.()}catch{}F=void 0,P=void 0,se({active:B(),revision:0})}const H={getSnapshot(){return I},subscribe(e){return L.add(e),()=>{L.delete(e)}}},le=/\{(\w+)\}/g,U=(e,t)=>{let n=N[I.active][e]??N.en[e]??e;return t===void 0?n:n.replace(le,(e,n)=>{let r=t[n];return r===void 0?e:String(r)})};function W(){return(0,c.useSyncExternalStore)(H.subscribe,H.getSnapshot),U}const ue=`dsh-taskboard-view-v1`;function G(){try{let e=localStorage.getItem(ue);if(e===null)return{urgencies:[],sortBy:`default`};let t=JSON.parse(e),n=t.sortBy===`updated`||t.sortBy===`urgency`||t.sortBy===`created`||t.sortBy===`title`?t.sortBy:`default`;return{workspaceId:typeof t.workspaceId==`string`?t.workspaceId:void 0,urgencies:Array.isArray(t.urgencies)?t.urgencies.filter(e=>e===`urgent`||e===`normal`||e===`relaxed`):[],sortBy:n}}catch{return{urgencies:[],sortBy:`default`}}}function de(){let e=G();return{boardOpen:!1,ledger:j(),workspaces:[],filters:{workspaceId:e.workspaceId,urgencies:e.urgencies},search:``,sortBy:e.sortBy,composerOpen:!1,secondaryOpen:!1,diagOpen:!1,queueOpen:!1,templates:[],tplManagerOpen:!1,importOpen:!1,settingsOpen:!1}}var fe=class{client;state=de();subscribers=new Set;disposed=!1;closeRequester;disposeStream;refreshInFlight;seenRevision;sessionJumper;sessionTitles;catalogFaces={};constructor(e){this.client=e}getSnapshot(){return this.state}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}emit(){if(!this.disposed)for(let e of this.subscribers)e()}setState(e){this.state={...this.state,...e},this.emit()}start(){this.refresh(),this.disposeStream=this.client.stream(e=>{this.seenRevision=e.revision,this.refresh()},()=>{this.refresh()})}async refresh(){return this.refreshInFlight===void 0&&(this.refreshInFlight=(async()=>{try{for(let e=0;e<3;e++){let[e,t]=await Promise.allSettled([Promise.resolve().then(()=>this.client.state()),Promise.resolve().then(()=>this.client.workspaces())]),n={},r=[],i=!1;if(e.status===`fulfilled`){let t=e.value,r;this.state.selectedId!==void 0&&(r=t.tasks.find(e=>e.id===this.state.selectedId)),Object.assign(n,{archiveSessionsSupported:t.capabilities?.archiveSessions===!0,ledger:t,queue:t.queue,selectedId:r===void 0?void 0:this.state.selectedId})}else r.push(`state: ${e.reason instanceof Error?e.reason.message:String(e.reason)}`);if(t.status===`fulfilled`){let e=t.value;n.workspaces=e;let r=this.state.filters.workspaceId;r!==void 0&&!e.some(e=>e.id===r)&&(n.filters={...this.state.filters,workspaceId:void 0},i=!0)}else r.push(`workspaces: ${t.reason instanceof Error?t.reason.message:String(t.reason)}`);if(n.error=r.length>0?r.join(`; `):void 0,this.setState(n),i&&this.persistView(),e.status!==`fulfilled`||this.seenRevision===void 0||e.value.revision>=this.seenRevision)break}}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}finally{this.refreshInFlight=void 0}})()),this.refreshInFlight}dispose(){this.disposed=!0,this.disposeStream?.(),this.subscribers.clear()}openBoard(){this.setState({boardOpen:!0})}closeBoard(){if(this.closeRequester!==void 0){this.closeRequester();return}this.setState({boardOpen:!1})}installCloseRequester(e){this.closeRequester=e}toggleBoard(){this.setState({boardOpen:!this.state.boardOpen})}setWorkspaceFilter(e){this.setState({filters:{...this.state.filters,workspaceId:e}}),this.persistView()}toggleUrgency(e){let t=new Set(this.state.filters.urgencies);t.has(e)?t.delete(e):t.add(e),this.setState({filters:{...this.state.filters,urgencies:[...t]}}),this.persistView()}setSearch(e){this.setState({search:e})}setSortBy(e){this.setState({sortBy:e}),this.persistView()}persistView(){try{localStorage.setItem(ue,JSON.stringify({workspaceId:this.state.filters.workspaceId,urgencies:this.state.filters.urgencies,sortBy:this.state.sortBy}))}catch{}}select(e){this.setState({selectedId:e})}setComposer(e){this.setState({composerOpen:e,editingId:void 0,templatePrefill:void 0})}newFromTemplate(e){this.setState({composerOpen:!0,editingId:void 0,templatePrefill:e})}openEditor(e){this.setState({composerOpen:!0,editingId:e,templatePrefill:void 0})}closeForm(){this.setState({composerOpen:!1,editingId:void 0,templatePrefill:void 0})}toggleSecondary(){this.setState({secondaryOpen:!this.state.secondaryOpen})}gitAvailable(e){return e===void 0||this.state.workspaces.find(t=>t.id===e)?.gitAvailable===!0}repoCount(e){return e===void 0?1:this.state.workspaces.find(t=>t.id===e)?.repoCount??1}installSessionJumper(e){this.sessionJumper=e}installSessionTitles(e){this.sessionTitles=e}installModelCatalog(e){this.catalogFaces.models=e}installPresetRoster(e){this.catalogFaces.presets=e}get modelCatalog(){return this.catalogFaces.models}get presetCatalog(){return this.catalogFaces.presets}async fetchProjectSessions(e){let t=(await this.client.projectSessions(e)).sessions,n=this.sessionTitles?.()??{};return t.map(e=>({...e,title:n[e.id]?.displayTitle??n[e.id]?.title}))}async fetchModelCatalog(){if(this.catalogFaces.models!==void 0)try{let e=await this.catalogFaces.models();if(e!==void 0&&e.length>0)return e}catch{}try{return(await this.client.modelCatalog()).models??[]}catch{return[]}}async fetchPresetCatalog(){if(this.catalogFaces.presets!==void 0)try{let e=await this.catalogFaces.presets();if(e!==void 0&&e.presets!==void 0&&e.presets.length>0)return e}catch{}try{let e=await this.client.modelCatalog();return{presets:e.presets??[],...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{return{presets:[]}}}async openSession(e){if(this.sessionJumper===void 0)return`unavailable`;let t;try{t=await this.sessionJumper(e)}catch{return`unavailable`}return t===`opened`&&this.closeBoard(),t}async create(e){try{let t=await this.client.create(e);return this.setState({composerOpen:!1,error:void 0}),await this.refresh(),t.id}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async update(e,t,n){try{return await this.client.update(e,{ifVersion:t,...n}),this.setState({composerOpen:!1,editingId:void 0,error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async retryArchiveSessions(e){try{if(this.client.archiveSessions===void 0)return;let t=await this.client.archiveSessions(e);this.setState({sessionArchive:{taskId:e,result:t}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async move(e,t,n,r){try{let i=await this.client.move(e,{ifVersion:t,status:n,...r?.archiveSessions===void 0?{}:{archiveSessions:r.archiveSessions}});i.sessionArchive!==void 0&&this.setState({sessionArchive:{taskId:e,result:i.sessionArchive}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async reject(e,t,n){let r=n!==void 0&&n.trim().length>0?n.trim():void 0;try{return await this.client.reject(e,r===void 0?{ifVersion:t}:{ifVersion:t,body:r}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async toggleBlocked(e){try{await this.client.update(e.id,{ifVersion:e.version,blocked:!e.blocked}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async toggleChecklistItem(e,t){let n=(e.checklist??[]).map(e=>e.id===t?e.checked?{id:e.id,text:e.text,checked:!1}:{id:e.id,text:e.text,checked:!0,checkedBy:`user`,checkedAt:Date.now(),...e.note===void 0?{}:{note:e.note}}:e);try{await this.client.update(e.id,{ifVersion:e.version,checklist:n}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async fetchDiff(e,t){try{return await this.client.diff(e,t)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async comment(e,t){try{return await this.client.comment(e,t),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async uploadImage(e){try{return await this.client.uploadImage(e)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async run(e,t=!1){try{await this.client.run(e,t?{reuse:!0}:{}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async cancel(e){try{await this.client.cancel(e),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async mergeBranch(e){try{let t=await this.client.mergeBranch(e);return await this.refresh(),t.noop===!0?{ok:!0,noop:!0}:t.results===void 0?{ok:!0}:{ok:!0,results:t.results}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async removeWorktree(e,t){try{let n=await this.client.worktreeRemove(e,{deleteBranch:t});return await this.refresh(),n.branchError===void 0?{ok:!0}:{ok:!0,branchError:n.branchError}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}openDiagnostics(){this.setState({diagOpen:!0}),this.client.diagnostics().then(e=>this.setState({diagnostics:e})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeDiagnostics(){this.setState({diagOpen:!1})}openQueuePanel(){this.setState({queueOpen:!0}),this.refresh()}closeQueuePanel(){this.setState({queueOpen:!1})}async clearQueue(){try{let e=await this.client.clearQueue();return await this.refresh(),e}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}openSettings(){this.setState({settingsOpen:!0}),this.client.storage().then(e=>this.setState({storage:e,error:void 0})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeSettings(){this.setState({settingsOpen:!1,storageNotice:void 0})}async updateSettings(e){try{return await this.client.updateSettings(e),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async checkStorage(e){try{let t=await this.client.checkStorage(e);return this.setState({storage:t,error:void 0}),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async migrateStorage(e){try{let t=await this.client.migrateStorage(e);return this.setState({storage:t,storageNotice:{path:t.currentDirectory,warnings:t.warnings},error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}dismissStorageNotice(){this.state.storageNotice!==void 0&&this.setState({storageNotice:void 0})}async cleanupOrphan(e,t){try{await this.client.worktreeCleanup(e,t);let n=await this.client.diagnostics();this.setState({diagnostics:n,error:void 0})}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async remove(e,t,n){try{await this.client.remove(e,n?{purge:!0}:{ifVersion:t}),n&&this.setState({selectedId:void 0}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async duplicate(e){try{await this.client.create({title:e.title.slice(0,196)+U(`shared.duplicate.suffix`),workspaceId:e.workspaceId,urgency:e.urgency,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0&&e.execution.runAt>Date.now()?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async loadTemplates(){try{let e=await this.client.templates();return this.setState({templates:e.templates,error:void 0}),e.templates}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),[]}}prepareTemplateMenu(){this.state.templates.length===0&&this.loadTemplates()}openTemplateManager(){this.setState({tplManagerOpen:!0}),this.loadTemplates()}closeTemplateManager(){this.setState({tplManagerOpen:!1})}async upsertTemplate(e){try{return await this.client.templateUpsert(e),await this.loadTemplates(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async deleteTemplate(e){try{await this.client.templateDelete(e),await this.loadTemplates()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async saveAsTemplate(e){return this.upsertTemplate({name:e.title.slice(0,60),task:{title:e.title,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,urgency:e.urgency,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}})}async fetchPromptCompletions(){try{return await this.client.promptCompletions()}catch{return}}openImport(){this.setState({importOpen:!0})}closeImport(){this.setState({importOpen:!1})}async importPreview(e){try{return(await this.client.importPreview(e)).plan}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async importCommit(e,t){try{let n=await this.client.importCommit(e,t);return await this.refresh(),n}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}exportJson(){let e=new Date,t=e=>String(e).padStart(2,`0`),n=`dsh-taskboard-${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}.json`,r=JSON.stringify(this.state.ledger,null,2);this.download(n,r,`application/json`)}exportCsv(){let e=e=>{let t=String(e??``);return/^[=+\-@\t\r]/.test(t)&&(t=`'${t}`),/[",\n\r]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t},t=[`id`,`title`,`status`,`urgency`,`blocked`,`project`,`claimedBy`,`mode`,`cron`,`nextRunAt`,`model`,`createdAt`,`updatedAt`,`comments`,`executions`],n=this.state.ledger.tasks.map(t=>[t.id,t.title,t.status,t.urgency,t.blocked?`yes`:`no`,t.workspaceId,t.claimedBy??``,t.execution.mode,t.execution.cron??``,t.execution.nextRunAt===void 0?``:new Date(t.execution.nextRunAt).toISOString(),t.model===void 0?``:`${t.model.provider}/${t.model.model}`,new Date(t.createdAt).toISOString(),new Date(t.updatedAt).toISOString(),t.comments.length,t.executions.length].map(e).join(`,`)),r=new Date,i=e=>String(e).padStart(2,`0`),a=`dsh-taskboard-${r.getFullYear()}${i(r.getMonth()+1)}${i(r.getDate())}.csv`;this.download(a,`\uFEFF${[t.join(`,`),...n].join(`\r +Writes queue briefly during migration.`,"set.storage.migrated":`Migration succeeded — data now lives in {path}`,"set.storage.warnings":`Migration finished, but some old files could not be removed: {warnings}`,"form.field.permission":`Execution permission`,"form.perm.write":`Workspace write`,"form.perm.readOnly":`Read-only`,"form.perm.fullAccess":`Full access`,"form.perm.defaultTag":` (default)`,"set.perm.heading":`Default execution permission`,"set.perm.writeName":`📁 Workspace write (factory default)`,"set.perm.writeHint":`Read-write access to the workspace and temp directories; writes need no extra confirmation`,"set.perm.readOnlyName":`🔒 Read-only`,"set.perm.readOnlyHint":`Read-only viewing and search only; no file changes or destructive commands`,"set.perm.fullName":`⚡ Full access`,"set.perm.fullHint":`Unrestricted: whole-disk access and external system commands`,"set.perm.current":`Currently saved default: {current}. The execution permission preset applied to new tasks.`,"set.schedule.heading":`Scheduled execution`,"set.schedule.hint":`Takes effect for subsequent scheduler and execution checks. The offline window only classifies historical triggers while the host was down; a queue shelf life of 0 retains queued work across restarts.`,"set.schedule.concurrent":`Maximum concurrent executions (1–100)`,"set.schedule.missedAfter":`Offline missed window, minutes (1–1440)`,"board.queue.chip":`Queued {n}`,"queue.panel.title":`Execution queue`,"queue.depth":`Queued`,"queue.wait":`Longest wait`,"queue.clear":`Clear queue`,"queue.confirm":`Confirm clear`,"queue.cleared":`Cleared {n} queued task(s).`,"set.schedule.queueMaxAge":`Queue shelf life, minutes (0 = retain indefinitely; max 10080)`,"set.schedule.dispatchInterval":`Scheduled-session start interval, ms (default 1000; 0 = no throttle; max 60000)`,"set.schedule.invalid":`Enter whole numbers within the allowed ranges before saving.`,"card.badge.permReadOnly":`🔒 Read-only`,"card.badge.permReadOnlyTitle":`Permission: read-only`,"card.badge.permFull":`⚡ Full access`,"card.badge.permFullTitle":`Permission: full access`,"detail.chip.permReadOnly":`Read-only`,"detail.chip.permFull":`Full access`,"detail.chip.permWrite":`Workspace write`,"tpl.meta.permReadOnly":`read-only`,"tpl.meta.permFull":`full access`,"md.imageAlt":`Image {n}`,"md.imageTitle":`Click to view full size ({alt})`,"md.lightboxAlt":`Full-size preview`,"md.closePreview":`Close preview`,"image.add":`Insert image`,"image.uploading":`Uploading…`,"image.hint":`Choose, paste, or drop PNG/JPEG/GIF/WebP images up to 5 MB each`,"image.defaultAlt":`Image`,"slash.aria":`Quick commands and skills`,"slash.title":`Quick command and skill completion`,"slash.hint":`↑↓ navigate · Enter / Tab pick · Esc close`,"slash.badge.command":`⚡ command`,"slash.badge.skill":`🧩 skill`,"slash.tipA":`💡 Type`,"slash.tipB":`to autocomplete slash commands and agent skills`,"slash.cmd.goal.desc":`Autonomously drive long-horizon goals to completion`,"slash.cmd.goal.hint":`<goal description>`,"slash.cmd.schedule.desc":`Set one-off or recurring cron schedules`,"slash.cmd.schedule.hint":`<time or expression>`,"slash.cmd.plan.desc":`Draft a step-by-step plan and get user confirmation before acting`,"slash.cmd.browser.desc":`Launch a browser for page interaction and live search`,"slash.cmd.grill-me.desc":`Align requirements and design intent through one-question-at-a-time interviews`,"slash.cmd.teamwork-preview.desc":`Multi-agent collaboration and team workflow preview`,"slash.cmd.learn.desc":`Capture solutions and new rules into the knowledge base`,"slash.cmd.review.desc":`Multi-axis code review (correctness, architecture, security)`,"slash.cmd.security.desc":`Security hardening and vulnerability scanning`,"slash.cmd.permission.desc":`Switch the session permission level (read-only / workspace-write / full-access)`,"slash.cmd.permission.hint":`<preset>`,"slash.skill.frontend-ui-engineering":`Build production-grade, accessible frontend interfaces and components`,"slash.skill.api-and-interface-design":`Design stable, well-bounded REST / RPC interfaces`,"slash.skill.test-driven-development":`Test-driven development (TDD): unit and integration tests`,"slash.skill.debugging-and-error-recovery":`Systematically locate bug root causes and recover`,"slash.skill.performance-optimization":`Frontend/backend performance tuning and query optimization`,"slash.skill.ci-cd-and-automation":`Build automation, CI/CD pipelines and quality gates`,"slash.skill.code-review-and-quality":`Multi-axis code review and refactoring guidance`,"slash.skill.code-simplification":`Simplify complex logic for readability and maintainability`,"slash.skill.context-engineering":`Optimize context structure and prompt engineering`,"slash.skill.doubt-driven-development":`Adversarial, doubt-driven review of core logic`,"slash.skill.git-workflow-and-versioning":`Git workflow, branching, semantic versioning and changelogs`,"slash.skill.idea-refine":`Refine ideas and test hypotheses via divergent-convergent thinking`,"slash.skill.incremental-implementation":`Deliver multi-file changes in small, incremental steps`,"slash.skill.interview-me":`Deep interviews to surface true intent`,"slash.skill.memory-leak-debugging":`Diagnose JavaScript/Node.js memory leaks`,"slash.skill.observability-and-instrumentation":`Add logging, metrics and distributed tracing`,"slash.skill.planning-and-task-breakdown":`Break complex requirements into ordered, executable tasks`,"slash.skill.security-and-hardening":`Harden against vulnerabilities; input filtering and auth hardening`,"slash.skill.shipping-and-launch":`Pre-launch checklists and rollback strategies`,"slash.skill.source-driven-development":`Design from authoritative docs and source code`,"slash.skill.spec-driven-development":`Write clear technical specs before coding`,"slash.skill.using-agent-skills":`Discover and invoke agent skills dynamically`,"sys.execFailed":`[System] Execution failed: {error}; the task was returned to todo.`,"sys.endedWithComment":`[System] The execution session ended with comments but was not moved to in review; the system moved it to in review automatically.`,"sys.endedNoHandoff":`[System] The execution session ended without a protocol handoff (no comments, not moved to in review); the system moved it to in review automatically — review it, then send back or accept.`,"sys.sessionError":`[System] Session execution error: {error}; the task was returned to todo.`,"sys.sessionDone":`[System] The session finished; automatically moved to in review.`,"sys.cronDead":`[System] The cron expression {cron} has no trigger time within 4 years; scheduling disabled — fix the cron and re-enable.`,"sys.periodicHandoff":`[System] Periodic run finished; the schedule now continues on the new todo card {nextTaskId}. Review this card, then accept.`,"sys.spawnedFrom":`[System] The previous periodic run finished (execution {executionId}); this card carries the schedule onward. See {sourceId} for the finished round.`,"sys.runAtMissed":`[System] The one-shot trigger time was missed (host was down); it will not re-fire. Run manually or reschedule.`,"sys.mergeSingle":`[System] Branch {branch} merged into the main worktree (--no-ff).`,"sys.mergeMulti":`[System] Branches merged per repo (--no-ff): {summary}`}};let P,F,I={active:ae(),revision:0};const L=new Set;let R,re;function z(){re!==void 0&&(clearInterval(re),re=void 0);try{R?.disconnect()}catch{}R=void 0}function ie(e){return e===`zh`||e===`en`}function ae(){try{if(typeof document<`u`){let e=document.documentElement.lang;if(typeof e==`string`&&e.length>0){let t=e.toLowerCase();if(t.startsWith(`zh`))return`zh`;if(t.startsWith(`en`))return`en`}}}catch{}try{if(typeof navigator<`u`)return(navigator.language??`en`).toLowerCase().startsWith(`zh`)?`zh`:`en`}catch{}return`en`}function B(e){let t=e;if(!(t==null||typeof t.getSnapshot!=`function`||typeof t.subscribe!=`function`))return t}function oe(e){z();try{typeof MutationObserver<`u`&&typeof document<`u`&&(R=new MutationObserver(()=>{if(P!==void 0)return;let e=ae();e!==I.active&&se({active:e,revision:I.revision+1})}),R.observe(document.documentElement,{attributes:!0,attributeFilter:[`lang`]}))}catch{}if(e===void 0)return;let t=0;re=setInterval(()=>{t+=1;let n;try{n=B(e())}catch{n=void 0}if(n!==void 0){V(n);return}t>=8&&z()},250)}function se(e){if(!(e.active===I.active&&e.revision===I.revision)){I=e;for(let e of L)e()}}function V(e,t){let n=B(e);if(n===void 0){se({active:ae(),revision:0}),oe(t);return}z(),P=n;let r=()=>{try{let e=n.getSnapshot();se({active:ie(e.active)?e.active:ae(),revision:e.revision})}catch{}};r(),F=n.subscribe(r)}function ce(){z();try{F?.()}catch{}F=void 0,P=void 0,se({active:ae(),revision:0})}const H={getSnapshot(){return I},subscribe(e){return L.add(e),()=>{L.delete(e)}}},le=/\{(\w+)\}/g,U=(e,t)=>{let n=N[I.active][e]??N.en[e]??e;return t===void 0?n:n.replace(le,(e,n)=>{let r=t[n];return r===void 0?e:String(r)})};function W(){return(0,c.useSyncExternalStore)(H.subscribe,H.getSnapshot),U}const ue=`dsh-taskboard-view-v1`;function G(){try{let e=localStorage.getItem(ue);if(e===null)return{urgencies:[],sortBy:`default`};let t=JSON.parse(e),n=t.sortBy===`updated`||t.sortBy===`urgency`||t.sortBy===`created`||t.sortBy===`title`?t.sortBy:`default`;return{workspaceId:typeof t.workspaceId==`string`?t.workspaceId:void 0,urgencies:Array.isArray(t.urgencies)?t.urgencies.filter(e=>e===`urgent`||e===`normal`||e===`relaxed`):[],sortBy:n}}catch{return{urgencies:[],sortBy:`default`}}}function de(){let e=G();return{boardOpen:!1,ledger:j(),workspaces:[],filters:{workspaceId:e.workspaceId,urgencies:e.urgencies},search:``,sortBy:e.sortBy,composerOpen:!1,secondaryOpen:!1,diagOpen:!1,queueOpen:!1,templates:[],tplManagerOpen:!1,importOpen:!1,settingsOpen:!1}}var fe=class{client;state=de();subscribers=new Set;disposed=!1;closeRequester;disposeStream;refreshInFlight;seenRevision;sessionJumper;sessionTitles;catalogFaces={};constructor(e){this.client=e}getSnapshot(){return this.state}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}emit(){if(!this.disposed)for(let e of this.subscribers)e()}setState(e){this.state={...this.state,...e},this.emit()}start(){this.refresh(),this.disposeStream=this.client.stream(e=>{this.seenRevision=e.revision,this.refresh()},()=>{this.refresh()})}async refresh(){return this.refreshInFlight===void 0&&(this.refreshInFlight=(async()=>{try{for(let e=0;e<3;e++){let[e,t]=await Promise.allSettled([Promise.resolve().then(()=>this.client.state()),Promise.resolve().then(()=>this.client.workspaces())]),n={},r=[],i=!1;if(e.status===`fulfilled`){let t=e.value,r;this.state.selectedId!==void 0&&(r=t.tasks.find(e=>e.id===this.state.selectedId)),Object.assign(n,{archiveSessionsSupported:t.capabilities?.archiveSessions===!0,ledger:t,queue:t.queue,selectedId:r===void 0?void 0:this.state.selectedId})}else r.push(`state: ${e.reason instanceof Error?e.reason.message:String(e.reason)}`);if(t.status===`fulfilled`){let e=t.value;n.workspaces=e;let r=this.state.filters.workspaceId;r!==void 0&&!e.some(e=>e.id===r)&&(n.filters={...this.state.filters,workspaceId:void 0},i=!0)}else r.push(`workspaces: ${t.reason instanceof Error?t.reason.message:String(t.reason)}`);if(n.error=r.length>0?r.join(`; `):void 0,this.setState(n),i&&this.persistView(),e.status!==`fulfilled`||this.seenRevision===void 0||e.value.revision>=this.seenRevision)break}}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}finally{this.refreshInFlight=void 0}})()),this.refreshInFlight}dispose(){this.disposed=!0,this.disposeStream?.(),this.subscribers.clear()}openBoard(){this.setState({boardOpen:!0})}closeBoard(){if(this.closeRequester!==void 0){this.closeRequester();return}this.setState({boardOpen:!1})}installCloseRequester(e){this.closeRequester=e}toggleBoard(){this.setState({boardOpen:!this.state.boardOpen})}setWorkspaceFilter(e){this.setState({filters:{...this.state.filters,workspaceId:e}}),this.persistView()}toggleUrgency(e){let t=new Set(this.state.filters.urgencies);t.has(e)?t.delete(e):t.add(e),this.setState({filters:{...this.state.filters,urgencies:[...t]}}),this.persistView()}setSearch(e){this.setState({search:e})}setSortBy(e){this.setState({sortBy:e}),this.persistView()}persistView(){try{localStorage.setItem(ue,JSON.stringify({workspaceId:this.state.filters.workspaceId,urgencies:this.state.filters.urgencies,sortBy:this.state.sortBy}))}catch{}}select(e){this.setState({selectedId:e})}setComposer(e){this.setState({composerOpen:e,editingId:void 0,templatePrefill:void 0})}newFromTemplate(e){this.setState({composerOpen:!0,editingId:void 0,templatePrefill:e})}openEditor(e){this.setState({composerOpen:!0,editingId:e,templatePrefill:void 0})}closeForm(){this.setState({composerOpen:!1,editingId:void 0,templatePrefill:void 0})}toggleSecondary(){this.setState({secondaryOpen:!this.state.secondaryOpen})}gitAvailable(e){return e===void 0||this.state.workspaces.find(t=>t.id===e)?.gitAvailable===!0}repoCount(e){return e===void 0?1:this.state.workspaces.find(t=>t.id===e)?.repoCount??1}installSessionJumper(e){this.sessionJumper=e}installSessionTitles(e){this.sessionTitles=e}installModelCatalog(e){this.catalogFaces.models=e}installPresetRoster(e){this.catalogFaces.presets=e}get modelCatalog(){return this.catalogFaces.models}get presetCatalog(){return this.catalogFaces.presets}async fetchProjectSessions(e){let t=(await this.client.projectSessions(e)).sessions,n=this.sessionTitles?.()??{};return t.map(e=>({...e,title:n[e.id]?.displayTitle??n[e.id]?.title}))}async fetchModelCatalog(){if(this.catalogFaces.models!==void 0)try{let e=await this.catalogFaces.models();if(e!==void 0&&e.length>0)return e}catch{}try{return(await this.client.modelCatalog()).models??[]}catch{return[]}}async fetchPresetCatalog(){if(this.catalogFaces.presets!==void 0)try{let e=await this.catalogFaces.presets();if(e!==void 0&&e.presets!==void 0&&e.presets.length>0)return e}catch{}try{let e=await this.client.modelCatalog();return{presets:e.presets??[],...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{return{presets:[]}}}async openSession(e){if(this.sessionJumper===void 0)return`unavailable`;let t;try{t=await this.sessionJumper(e)}catch{return`unavailable`}return t===`opened`&&this.closeBoard(),t}async create(e){try{let t=await this.client.create(e);return this.setState({composerOpen:!1,error:void 0}),await this.refresh(),t.id}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async update(e,t,n){try{return await this.client.update(e,{ifVersion:t,...n}),this.setState({composerOpen:!1,editingId:void 0,error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async retryArchiveSessions(e){try{if(this.client.archiveSessions===void 0)return;let t=await this.client.archiveSessions(e);this.setState({sessionArchive:{taskId:e,result:t}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async move(e,t,n,r){try{let i=await this.client.move(e,{ifVersion:t,status:n,...r?.archiveSessions===void 0?{}:{archiveSessions:r.archiveSessions}});i.sessionArchive!==void 0&&this.setState({sessionArchive:{taskId:e,result:i.sessionArchive}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async reject(e,t,n){let r=n!==void 0&&n.trim().length>0?n.trim():void 0;try{return await this.client.reject(e,r===void 0?{ifVersion:t}:{ifVersion:t,body:r}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async toggleBlocked(e){try{await this.client.update(e.id,{ifVersion:e.version,blocked:!e.blocked}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async toggleChecklistItem(e,t){let n=(e.checklist??[]).map(e=>e.id===t?e.checked?{id:e.id,text:e.text,checked:!1}:{id:e.id,text:e.text,checked:!0,checkedBy:`user`,checkedAt:Date.now(),...e.note===void 0?{}:{note:e.note}}:e);try{await this.client.update(e.id,{ifVersion:e.version,checklist:n}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async fetchDiff(e,t){try{return await this.client.diff(e,t)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async comment(e,t){try{return await this.client.comment(e,t),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async uploadImage(e){try{return await this.client.uploadImage(e)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async run(e,t=!1){try{await this.client.run(e,t?{reuse:!0}:{}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async cancel(e){try{await this.client.cancel(e),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async mergeBranch(e){try{let t=await this.client.mergeBranch(e);return await this.refresh(),t.noop===!0?{ok:!0,noop:!0}:t.results===void 0?{ok:!0}:{ok:!0,results:t.results}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async removeWorktree(e,t){try{let n=await this.client.worktreeRemove(e,{deleteBranch:t});return await this.refresh(),n.branchError===void 0?{ok:!0}:{ok:!0,branchError:n.branchError}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}openDiagnostics(){this.setState({diagOpen:!0}),this.client.diagnostics().then(e=>this.setState({diagnostics:e})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeDiagnostics(){this.setState({diagOpen:!1})}openQueuePanel(){this.setState({queueOpen:!0}),this.refresh()}closeQueuePanel(){this.setState({queueOpen:!1})}async clearQueue(){try{let e=await this.client.clearQueue();return await this.refresh(),e}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}openSettings(){this.setState({settingsOpen:!0}),this.client.storage().then(e=>this.setState({storage:e,error:void 0})).catch(e=>this.setState({error:e instanceof Error?e.message:String(e)}))}closeSettings(){this.setState({settingsOpen:!1,storageNotice:void 0})}async updateSettings(e){try{return await this.client.updateSettings(e),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async checkStorage(e){try{let t=await this.client.checkStorage(e);return this.setState({storage:t,error:void 0}),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async migrateStorage(e){try{let t=await this.client.migrateStorage(e);return this.setState({storage:t,storageNotice:{path:t.currentDirectory,warnings:t.warnings},error:void 0}),await this.refresh(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}dismissStorageNotice(){this.state.storageNotice!==void 0&&this.setState({storageNotice:void 0})}async cleanupOrphan(e,t){try{await this.client.worktreeCleanup(e,t);let n=await this.client.diagnostics();this.setState({diagnostics:n,error:void 0})}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async remove(e,t,n){try{await this.client.remove(e,n?{purge:!0}:{ifVersion:t}),n&&this.setState({selectedId:void 0}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async duplicate(e){try{await this.client.create({title:e.title.slice(0,196)+U(`shared.duplicate.suffix`),workspaceId:e.workspaceId,urgency:e.urgency,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0&&e.execution.runAt>Date.now()?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}),await this.refresh()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async loadTemplates(){try{let e=await this.client.templates();return this.setState({templates:e.templates,error:void 0}),e.templates}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),[]}}prepareTemplateMenu(){this.state.templates.length===0&&this.loadTemplates()}openTemplateManager(){this.setState({tplManagerOpen:!0}),this.loadTemplates()}closeTemplateManager(){this.setState({tplManagerOpen:!1})}async upsertTemplate(e){try{return await this.client.templateUpsert(e),await this.loadTemplates(),!0}catch(e){return this.setState({error:e instanceof Error?e.message:String(e)}),!1}}async deleteTemplate(e){try{await this.client.templateDelete(e),await this.loadTemplates()}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}async saveAsTemplate(e){return this.upsertTemplate({name:e.title.slice(0,60),task:{title:e.title,description:e.description.length>0?e.description:void 0,prompt:e.prompt.length>0?e.prompt:void 0,urgency:e.urgency,execution:e.execution.mode===`scheduled`&&e.execution.cron!==void 0?{mode:`scheduled`,cron:e.execution.cron}:e.execution.mode===`scheduled`&&e.execution.runAt!==void 0?{mode:`scheduled`,runAt:new Date(e.execution.runAt).toISOString()}:{mode:`claim`},model:e.model,isolation:e.isolation,...e.presetId===void 0?{}:{presetId:e.presetId},...e.permission===void 0?{}:{permission:e.permission},...e.checklist!==void 0&&e.checklist.length>0?{checklist:e.checklist.map(e=>e.text)}:{}}})}async fetchPromptCompletions(){try{return await this.client.promptCompletions()}catch{return}}openImport(){this.setState({importOpen:!0})}closeImport(){this.setState({importOpen:!1})}async importPreview(e){try{return(await this.client.importPreview(e)).plan}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}async importCommit(e,t){try{let n=await this.client.importCommit(e,t);return await this.refresh(),n}catch(e){this.setState({error:e instanceof Error?e.message:String(e)});return}}exportJson(){let e=new Date,t=e=>String(e).padStart(2,`0`),n=`dsh-taskboard-${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}.json`,r=JSON.stringify(this.state.ledger,null,2);this.download(n,r,`application/json`)}exportCsv(){let e=e=>{let t=String(e??``);return/^[=+\-@\t\r]/.test(t)&&(t=`'${t}`),/[",\n\r]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t},t=[`id`,`title`,`status`,`urgency`,`blocked`,`project`,`claimedBy`,`mode`,`cron`,`nextRunAt`,`model`,`createdAt`,`updatedAt`,`comments`,`executions`],n=this.state.ledger.tasks.map(t=>[t.id,t.title,t.status,t.urgency,t.blocked?`yes`:`no`,t.workspaceId,t.claimedBy??``,t.execution.mode,t.execution.cron??``,t.execution.nextRunAt===void 0?``:new Date(t.execution.nextRunAt).toISOString(),t.model===void 0?``:`${t.model.provider}/${t.model.model}`,new Date(t.createdAt).toISOString(),new Date(t.updatedAt).toISOString(),t.comments.length,t.executions.length].map(e).join(`,`)),r=new Date,i=e=>String(e).padStart(2,`0`),a=`dsh-taskboard-${r.getFullYear()}${i(r.getMonth()+1)}${i(r.getDate())}.csv`;this.download(a,`\uFEFF${[t.join(`,`),...n].join(`\r `)}`,`text/csv`)}download(e,t,n){try{let r=new Blob([t],{type:n}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=e,a.click(),setTimeout(()=>URL.revokeObjectURL(i),5e3)}catch(e){this.setState({error:e instanceof Error?e.message:String(e)})}}};const pe=`dsh-taskboard-styles`;function me(){if(typeof document>`u`)return;let e=document.getElementById(pe);e===null&&(e=document.createElement(`style`),e.id=pe,e.textContent=` .dsh-atb-entry { display: flex; align-items: center; gap: 8px; position: relative; @@ -980,7 +980,7 @@ color: var(--dsw-alias-state-business-primary, #3e63dd); `)?` `:``}`;return{value:i+o+a,cursor:i.length+o.length}}function X(e){return Array.from(e).filter(e=>ke.has(e.type)).slice(0,10)}function Ne(e){return[`backlog`,`todo`,`in_progress`,`in_review`,`done`,`canceled`,`archived`].filter(t=>x(e.status,t))}function Z(e){return e===void 0?``:e.replace(/^session-(taskboard-)?/,``).slice(0,8)}function Pe(e,t){if(t.systemKey===void 0)return t.body;if(t.systemRows!==void 0){let n=t.systemRows.map(t=>{let n=t.repo===``?e(`iso.repo.root`):t.repo,r=t.outcome===`merged`?`✓`:t.outcome===`noop`?`⟲`:`✗`;return t.outcome===`failed`&&t.error!==void 0?`${n} ${r} ${t.error}`:`${n} ${r}`}).join(` · `);return e(t.systemKey,{summary:n})}return e(t.systemKey,t.systemParams)}function Fe(e,t){if(e===void 0||t===void 0)return``;let n=Math.max(0,Math.round((t-e)/1e3));return n<60?`${n}s`:n<3600?`${Math.floor(n/60)}m${n%60}s`:`${Math.floor(n/3600)}h${Math.floor(n%3600/60)}m`}function Q({icon:e,children:t,tone:n,title:r}){return(0,l.jsxs)(`span`,{className:`dsh-atb-chip2`,"data-tone":n,title:r,children:[e!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:e}),t]})}function Ie({text:e}){let t=W(),[n,r]=(0,c.useState)(null),i=/!\[(.*?)\]\(((?:data:image\/[^)]+)|(?:https?:\/\/[^)]+)|(?:[^)]+\.(?:png|jpg|jpeg|gif|webp|svg)))\)/gi,a=[],o=0,s,u=0;for(;(s=i.exec(e))!==null;){s.index>o&&a.push((0,l.jsx)(`span`,{children:e.slice(o,s.index)},`txt-${o}`));let n=s[1]||t(`md.imageAlt`,{n:++u}),c=s[2]??``;a.push((0,l.jsxs)(`div`,{className:`dsh-atb-detail-img-wrap`,children:[(0,l.jsx)(`img`,{src:c,alt:n,className:`dsh-atb-detail-img`,onClick:()=>r(c),title:t(`md.imageTitle`,{alt:n})}),(0,l.jsx)(`span`,{className:`dsh-atb-detail-img-caption`,children:n})]},`img-${s.index}`)),o=i.lastIndex}return o<e.length&&a.push((0,l.jsx)(`span`,{children:e.slice(o)},`txt-${o}`)),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-markdown-body`,children:a}),n!==null&&(0,l.jsx)(`div`,{className:`dsh-atb-lightbox-backdrop`,onClick:()=>r(null),children:(0,l.jsxs)(`div`,{className:`dsh-atb-lightbox-content`,onClick:e=>e.stopPropagation(),children:[(0,l.jsx)(`img`,{src:n,alt:t(`md.lightboxAlt`),className:`dsh-atb-lightbox-img`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-lightbox-close`,title:t(`md.closePreview`),onClick:()=>r(null),children:`✕`})]})})]})}function Le(e){return[...e.executions].reverse().find(e=>e.isolation!==void 0||e.worktreePath!==void 0||e.isolationNote!==void 0)}function Re(e){return e===void 0?``:e.slice(0,8)}function ze(e){let t=e.slice(3),n=t.indexOf(` -> `);return n>=0&&(t=t.slice(n+4)),t.startsWith(`"`)&&t.endsWith(`"`)&&t.length>1&&(t=t.slice(1,-1)),t}function Be({controller:e,task:t,execution:n,commit:r,path:i,repo:a}){let o=W(),[s,u]=(0,c.useState)({loading:!0});return(0,c.useEffect)(()=>{let o=!0;return u({loading:!0}),e.fetchDiff(t.id,{execution:n.id,...r===void 0?{path:i??``}:{commit:r},...a===void 0?{}:{repo:a}}).then(e=>{o&&u(e===void 0?{loading:!1,failed:!0}:{loading:!1,diff:e.diff,truncated:e.truncated})}),()=>{o=!1}},[e,t.id,n.id,r,i,a]),(0,l.jsxs)(`div`,{className:`dsh-atb-diffview`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diffview-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-diffview-title`,children:r===void 0?o(`diff.file`,{path:i??``}):o(`diff.commit`,{hash:Re(r)})}),s.loading&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`shared.loading`)}),s.truncated===!0&&(0,l.jsx)(`span`,{className:`dsh-atb-diffview-hint`,children:o(`diff.truncated`)})]}),s.failed===!0?(0,l.jsx)(`div`,{className:`dsh-atb-diffview-error`,children:o(`diff.failed`)}):(0,l.jsx)(`pre`,{className:`dsh-atb-diffview-pre`,children:s.diff??``})]})}function Ve({task:e,controller:t}){let n=W(),r=e.checklist??[];if(r.length===0)return null;let{done:i,total:a}=ne(e),o=a-i,s=e.status===`in_review`;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`checklist`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[n(`checklist.title`),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-progress`,"data-tone":s&&o>0?`bad`:void 0,children:[`☑ `,i,`/`,a,s&&o>0?n(`checklist.unchecked`,{n:o}):i===a?n(`checklist.allDone`):``]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-cl-items`,children:r.map(r=>(0,l.jsxs)(`label`,{className:`dsh-atb-cl-item`,"data-checked":r.checked?`true`:void 0,"data-alert":s&&!r.checked?`true`:void 0,children:[(0,l.jsx)(`input`,{type:`checkbox`,checked:r.checked,onChange:()=>void t.toggleChecklistItem(e,r.id)}),(0,l.jsx)(`span`,{className:`dsh-atb-cl-text`,children:r.text}),(0,l.jsxs)(`span`,{className:`dsh-atb-cl-meta`,children:[r.checked?`${r.checkedBy===`user`?n(`checklist.byUser`):`🤖 ${Z(r.checkedBy)}`} · ${J(r.checkedAt)}`:n(`checklist.uncheckedItem`),r.note!==void 0&&r.note.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cl-note`,title:r.note,children:n(`checklist.evidence`,{note:r.note})})]})]},r.id))})]})}function He({task:e}){let t=W(),n=[...e.executions].reverse().find(e=>e.report!==void 0),r=n?.report;if(n===void 0||r===void 0)return null;let i=(e,t)=>t!==void 0&&t.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:e}),(0,l.jsx)(`ul`,{className:`dsh-atb-rpt-list`,children:t.map((e,t)=>(0,l.jsx)(`li`,{children:e},t))})]}):null;return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`report`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard-label`,children:[t(`report.title`),(0,l.jsx)(`span`,{className:`dsh-atb-cl-progress`,children:t(`report.submitted`,{time:J(n.endedAt??n.startedAt)})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-summary`,children:r.summary}),i(t(`report.changedFiles`),r.changedFiles),i(t(`report.checks`),r.checks),i(t(`report.artifacts`),r.artifacts),r.risk.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-rpt-sec`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-rpt-label`,children:t(`report.risk`)}),(0,l.jsx)(`div`,{className:`dsh-atb-rpt-risk`,children:r.risk})]})]})}function Ue({task:e,controller:t}){let n=W(),{alert:r,el:i}=Y(),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(null),[d,f]=(0,c.useState)(!1),[p,m]=(0,c.useState)(null),[h,g]=(0,c.useState)(!1),_=Le(e),v=e.executions.some(e=>e.outcome===`running`);if(_===void 0)return null;let y=()=>{f(!0),t.mergeBranch(e.id).then(e=>{if(f(!1),o(!1),!e.ok)r(n(`iso.merge.failed`,{error:e.error}));else if(e.results!==void 0){let t=e=>e===``?n(`iso.repo.root`):e,i=e.results.map(e=>`${t(e.repo)} ${e.outcome===`merged`?`✓`:e.outcome===`noop`?`⟲`:`✗`}`).join(` · `),a=e.results.some(e=>e.outcome===`failed`),o=e.results.find(e=>e.outcome===`failed`)?.error;r(a?n(`iso.merge.partial`,{summary:i,error:o??``}):n(`iso.merge.done`,{summary:i}))}else e.noop===!0&&r(n(`iso.merge.noop`))})},b=i=>{f(!0),t.removeWorktree(e.id,i).then(e=>{f(!1),u(null),e.ok?e.branchError!==void 0&&r(n(`iso.remove.branchFailed`,{error:e.branchError})):r(n(`iso.remove.failed`,{error:e.error}))})};if(_.isolation!==`worktree`||_.worktreePath===void 0)return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.title`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-none`,children:[n(`iso.none`),_.isolationNote===void 0?``:` · ${_.isolationNote}`]}),i]});let x=_.repos!==void 0&&_.repos.length>0,S=x?(_.repos??[]).map(e=>({repo:e.repo,label:e.repo===``?n(`iso.repo.root`):e.repo,branch:e.branch,worktreePath:e.worktreePath,baseCommit:e.baseCommit,headCommit:e.headCommit,commits:e.commits??[],commitTotal:e.commitsTotal??(e.commits??[]).length,dirty:e.dirtyFiles??[],dirtyTotal:e.dirtyFilesTotal??(e.dirtyFiles??[]).length,...e.diffStat===void 0?{}:{diffStat:e.diffStat},...e.changedFiles===void 0?{}:{changedFiles:e.changedFiles}})):[{label:n(`iso.repo.root`),branch:_.branch??e.branch,worktreePath:_.worktreePath,baseCommit:_.baseCommit,headCommit:_.headCommit,commits:_.commits??[],commitTotal:_.commitsTotal??(_.commits??[]).length,dirty:_.dirtyFiles??[],dirtyTotal:_.dirtyFilesTotal??(_.dirtyFiles??[]).length,..._.diffStat===void 0?{}:{diffStat:_.diffStat},..._.changedFiles===void 0?{}:{changedFiles:_.changedFiles}}];return(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`isolation`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:n(`iso.worktreeTitle`)}),S.map(r=>(0,l.jsxs)(`section`,{className:`dsh-atb-iso-repo`,"data-multi":x?`true`:void 0,children:[x&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-repohead`,title:r.worktreePath,children:[(0,l.jsx)(`code`,{children:r.label}),(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-facts`,children:[!x&&(0,l.jsxs)(`span`,{className:`dsh-atb-iso-fact`,title:r.worktreePath,children:[n(`iso.branch`),` `,(0,l.jsx)(`b`,{children:r.branch})]}),(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.baseline`,{base:Re(r.baseCommit),head:Re(r.headCommit)})}),r.changedFiles!==void 0&&r.changedFiles>0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,children:n(`iso.changed`,{n:r.changedFiles})}),r.diffStat!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-fact`,title:r.diffStat,children:r.diffStat})]}),r.commits.length>0?(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commits`,children:[r.commits.slice(0,10).map(i=>(0,l.jsxs)(`div`,{className:`dsh-atb-iso-commit`,"data-open":p?.commit===i.hash&&p?.repo===r.repo?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-commit-btn`,title:n(`iso.commit.openTitle`),onClick:()=>m(p?.commit===i.hash&&p?.repo===r.repo?null:{commit:i.hash,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:Re(i.hash)}),(0,l.jsx)(`span`,{children:i.subject})]}),p?.commit===i.hash&&p?.repo===r.repo&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,commit:i.hash,repo:r.repo})]},i.hash)),r.commitTotal>10&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.commits.more`,{n:r.commitTotal})})]}):(0,l.jsx)(`div`,{className:`dsh-atb-iso-nocommit`,children:n(`iso.nocommit`)}),r.dirtyTotal>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-toggle`,onClick:()=>g(!h),children:[n(`iso.dirty.toggle`,{n:r.dirtyTotal}),n(h?`iso.dirty.collapse`:`iso.dirty.expand`)]}),h&&(0,l.jsxs)(`div`,{className:`dsh-atb-iso-dirty-files`,children:[r.dirty.slice(0,30).map((e,t)=>{let i=ze(e);return(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-iso-dirty-file`,title:n(`iso.dirty.openTitle`),onClick:()=>m(p?.path===i&&p?.repo===r.repo?null:{path:i,...r.repo===void 0?{}:{repo:r.repo}}),children:[(0,l.jsx)(`code`,{children:e.slice(0,2)}),` `,i]},`${e}-${t}`)}),r.dirtyTotal>30&&(0,l.jsx)(`div`,{className:`dsh-atb-iso-more`,children:n(`iso.dirty.more`,{n:r.dirtyTotal})})]}),p?.path!==void 0&&p?.repo===r.repo&&h&&(0,l.jsx)(Be,{controller:t,task:e,execution:_,path:p.path,repo:r.repo})]})]},r.repo??`root`)),(0,l.jsxs)(`div`,{className:`dsh-atb-iso-actions`,children:[v?(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.running`)}):a?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(`iso.merge.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:d,onClick:y,children:n(`iso.merge.go`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(!1),children:n(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:d,title:n(`iso.merge.title`),onClick:()=>o(!0),children:n(`iso.merge.button`)}),!v&&(s===null?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtTitle`),onClick:()=>u(`wt`),children:n(`iso.remove.wt`)}),e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,title:n(`iso.remove.wtbTitle`),onClick:()=>u(`wtb`),children:n(`iso.remove.wtb`)})]}):(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:n(s===`wtb`?`iso.remove.confirmWtb`:`iso.remove.confirmWt`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:d,onClick:()=>b(s===`wtb`),children:n(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>u(null),children:n(`shared.cancel`)})]})),!v&&s===null&&!a&&(0,l.jsx)(`span`,{className:`dsh-atb-iso-hint`,children:n(`iso.hint.keep`)})]}),i]})}function We({task:e,controller:t,now:n}){let r=W(),[i,a]=(0,c.useState)(``),[o,s]=(0,c.useState)(!1),u=(0,c.useRef)(null),d=(0,c.useRef)(null),[f,p]=(0,c.useState)(!1),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(!1),[v,y]=(0,c.useState)(!1),[b,x]=(0,c.useState)(!1),{alert:S,el:ee}=Y(),te=t.getSnapshot().workspaces.find(t=>t.id===e.workspaceId),C=e.status!==`in_progress`&&e.status!==`done`&&e.status!==`archived`,w=e.executions.find(e=>e.outcome===`running`),T=e.status===`in_progress`?e.claimedBy:void 0,E=n!==void 0&&Ce(e,n),D=(e.checklist??[]).filter(e=>!e.checked).length,O=[...e.executions].reverse().find(e=>e.sessionId!==void 0),k=w?.sessionId??O?.sessionId??(e.claimedBy?.startsWith(`session-`)?e.claimedBy:void 0),A=M(e),j=t.getSnapshot(),N=j.sessionArchive?.taskId===e.id?j.sessionArchive.result:void 0,P=e=>{b||(x(!0),e().catch(()=>void 0).finally(()=>x(!1)))},F=()=>{o||i.trim().length===0||t.comment(e.id,i).then(e=>{e&&a(``)})},I=async e=>{let n=X(e);if(n.length===0||o)return;s(!0);let c=u.current,l=c?.value??i,d=c?.selectionStart??l.length,f=c?.selectionEnd??d,p=!1;try{for(let e of n){let n=await t.uploadImage(e);if(n===void 0)continue;let i=Me(l,d,f,je(n,Ae(e.name,r(`image.defaultAlt`))));l=i.value,d=i.cursor,f=d,p=!0}p&&(a(l),setTimeout(()=>{u.current?.focus(),u.current?.setSelectionRange(d,d)},0))}finally{s(!1)}},L=e=>{t.openSession(e).then(t=>{t===`missing`?S(r(`card.session.missing`,{id:Z(e)})):t===`archived`?S(r(`card.session.archived`,{id:Z(e)})):t===`unavailable`&&S(r(`card.session.unavailable`,{id:e}))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-detail`,"data-urgency":e.urgency,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-head`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlewrap`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-detail-titlebar`,children:[(0,l.jsx)(`h3`,{children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-statuspill`,"data-status":e.status,children:r(ge[e.status]??e.status)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-chips`,children:[(0,l.jsxs)(Q,{tone:e.urgency,children:[`● `,r(q[e.urgency]??e.urgency)]}),(0,l.jsx)(Q,{icon:`📁`,children:te?.title??Z(e.workspaceId)}),e.model!==void 0&&(0,l.jsxs)(Q,{icon:`✦`,title:r(`card.badge.modelTitle`,{model:e.model.provider+`/`+e.model.model})+(e.model.reasoningEffort===void 0?``:r(`card.badge.modelEffort`,{effort:e.model.reasoningEffort})),children:[e.model.model,e.model.reasoningEffort===void 0?``:` · ${e.model.reasoningEffort}`]}),e.presetId!==void 0&&(0,l.jsx)(Q,{icon:`🎛`,children:e.presetId}),e.execution.mode===`scheduled`&&(0,l.jsx)(Q,{icon:`⏰`,children:r(`detail.chip.nextRun`,{cron:e.execution.cron??``,time:J(e.execution.nextRunAt)})}),e.execution.queuedRunAt!==void 0&&(0,l.jsx)(Q,{icon:`⏳`,tone:`urgent`,children:r(`detail.chip.queuedRun`,{time:J(e.execution.queuedRunAt)})}),e.blocked&&(0,l.jsx)(Q,{icon:`⛔`,tone:`urgent`,children:r(`shared.blocked`)}),e.checklist!==void 0&&e.checklist.length>0&&(0,l.jsx)(Q,{icon:`☑`,tone:e.status===`in_review`&&e.checklist.some(e=>!e.checked)?`urgent`:void 0,children:r(`detail.chip.checklist`,{done:ne(e).done,total:e.checklist.length})}),e.branch!==void 0&&(0,l.jsxs)(Q,{icon:`🌿`,tone:void 0,children:[`Worktree · `,e.branch.length>28?`${e.branch.slice(0,28)}…`:e.branch]}),(e.isolation===void 0||e.isolation===`worktree`)&&e.branch===void 0&&(0,l.jsx)(Q,{icon:`🌿`,children:r(`detail.chip.isolated`)}),e.permission===`read-only`&&(0,l.jsx)(Q,{icon:`🔒`,tone:`urgent`,children:r(`detail.chip.permReadOnly`)}),e.permission===`danger-full-access`&&(0,l.jsx)(Q,{icon:`⚡`,tone:`urgent`,children:r(`detail.chip.permFull`)}),(e.permission===`workspace-write`||e.permission===void 0)&&(0,l.jsx)(Q,{icon:`📁`,children:r(`detail.chip.permWrite`)}),T!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip2 dsh-atb-chip-btn`,"data-tone":E?`urgent`:void 0,title:r(`detail.chip.holderTitle`,{id:T}),onClick:()=>L(T),children:[(0,l.jsx)(`span`,{className:`dsh-atb-chip2-icon`,children:E?`⏱`:`🤖`}),r(E?`detail.chip.holderStale`:`detail.chip.holderBy`),Z(T),r(`detail.chip.holderSuffix`)]}),e.trashedAt!==void 0&&(0,l.jsx)(Q,{icon:`🗑`,tone:`urgent`,children:r(`detail.chip.trashed`)}),(0,l.jsxs)(Q,{children:[`v`,e.version]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-sub`,children:r(`detail.sub.line`,{time:J(e.updatedAt),who:e.updatedBy.kind===`agent`?`🤖 ${Z(e.updatedBy.sessionId)}`:e.updatedBy.kind===`system`?r(`detail.updatedBy.system`):r(`detail.updatedBy.user`)})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-detail-topbtns`,children:[k!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-session`,title:r(`detail.session.jumpTitle`,{id:k}),onClick:()=>L(k),children:r(`detail.session.jump`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,onClick:()=>t.openEditor(e.id),children:r(`detail.action.edit`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.duplicateTitle`),disabled:b,onClick:()=>P(()=>t.duplicate(e)),children:r(`detail.action.duplicate`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-edit`,title:r(`detail.action.saveTplTitle`),disabled:b,onClick:()=>P(async()=>{await t.saveAsTemplate(e)&&S(r(`detail.action.saveTplDone`))}),children:r(`detail.action.saveTpl`)}),C&&e.branch!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:r(`detail.action.reuseTitle`),disabled:b,onClick:()=>P(()=>t.run(e.id,!0)),children:r(`detail.action.reuse`)}),C&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,title:e.model===void 0?r(`detail.action.runTitleDefault`):r(`detail.action.runTitleModel`,{model:e.model.model}),disabled:b,onClick:()=>P(()=>t.run(e.id)),children:r(`detail.action.run`)}),w!==void 0&&(g?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.action.stopConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.cancel(e.id),_(!1)},children:r(`detail.action.stop`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>_(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-run`,"data-danger":`true`,title:r(`detail.action.stopTitle`,{id:w.sessionId??``}),onClick:()=>_(!0),children:r(`detail.action.stopExec`)})),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-detail-close`,"aria-label":r(`shared.close`),onClick:()=>t.select(void 0),children:`✕`})]})]}),e.description.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.description`)}),(0,l.jsx)(`div`,{className:`dsh-atb-desc`,children:(0,l.jsx)(Ie,{text:e.description})})]}),e.prompt.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-fieldcard`,"data-kind":`prompt`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-fieldcard-label`,children:r(`detail.field.prompt`)}),(0,l.jsx)(`div`,{className:`dsh-atb-promptbox`,children:(0,l.jsx)(Ie,{text:e.prompt})})]}),(0,l.jsx)(Ue,{task:e,controller:t}),(0,l.jsx)(He,{task:e}),(0,l.jsx)(Ve,{task:e,controller:t}),N!==void 0&&(0,l.jsxs)(`div`,{role:`status`,className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{children:r(`detail.move.archiveResult`,{n:N.archived.length})}),N.failed.map(e=>(0,l.jsxs)(`span`,{children:[e.sessionId,`: `,e.error]},e.sessionId)),N.unsupported.length>0&&(0,l.jsxs)(`span`,{children:[r(`detail.move.archiveUnsupported`),` `,N.unsupported.join(`, `)]})]}),e.status===`archived`&&A.length>0&&j.archiveSessionsSupported&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:b,onClick:()=>P(()=>t.retryArchiveSessions(e.id)),children:r(`detail.move.archiveRetry`)}),(0,l.jsx)(`div`,{className:`dsh-atb-detail-actions`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-movebtns`,children:[Ne(e).map(n=>n===`done`?f?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,"data-tone":D>0?`bad`:void 0,children:D>0?r(`detail.move.confirmDoneUnchecked`,{n:D}):r(`detail.move.confirmDone`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`done`),p(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>p(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{p(!0),y(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):n===`archived`?v?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.length===1?r(`detail.move.confirmArchiveSessionWithId`,{id:Z(A[0])}):A.length>1?r(`detail.move.confirmArchiveSessionCount`,{n:A.length}):r(`detail.move.confirmArchive`)}),A.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:A.join(`, `)}),A.length>0?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!j.archiveSessionsSupported||b,title:j.archiveSessionsSupported?void 0:r(`detail.move.archiveUnsupported`),onClick:()=>{P(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!0})),y(!1)},children:r(`detail.move.archiveWithSession`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>{P(()=>t.move(e.id,e.version,`archived`,{archiveSessions:!1})),y(!1)},children:r(`detail.move.archiveCardOnly`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{t.move(e.id,e.version,`archived`),y(!1)},children:r(`detail.move.confirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>y(!1),children:r(`shared.cancel`)})]},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>{y(!0),p(!1)},children:r(`detail.move.to`,{status:r(K[n])})},n):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":n,onClick:()=>void t.move(e.id,e.version,n),children:r(`detail.move.to`,{status:r(K[n])})},n)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`blocked`,onClick:()=>void t.toggleBlocked(e),children:e.blocked?r(`detail.blocked.unmark`):r(`detail.blocked.mark`)}),T!==void 0&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-movebtn`,"data-to":`release`,title:r(`detail.release.title`,{id:T}),onClick:()=>void t.move(e.id,e.version,`todo`),children:r(`detail.release.button`)})]})}),(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.comments.title`),e.comments.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.comments.length})]}),e.comments.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:r(`detail.comments.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-commentlist`,children:e.comments.map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-bubble`,"data-from":e.threadId===void 0?`user`:`agent`,children:[(0,l.jsx)(`div`,{className:`dsh-atb-bubble-avatar`,children:e.threadId===void 0?`👤`:`🤖`}),(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-main`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-bubble-meta`,children:[(0,l.jsx)(`b`,{children:e.threadId===void 0?r(`detail.comments.user`):`agent ${Z(e.threadId)}`}),(0,l.jsx)(`span`,{children:J(e.createdAt)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-bubble-body`,children:(0,l.jsx)(Ie,{text:Pe(r,e)})})]})]},e.id))}),(0,l.jsxs)(`div`,{className:`dsh-atb-composer`,children:[(0,l.jsx)(`textarea`,{ref:u,className:`dsh-atb-composer-input`,value:i,placeholder:r(`detail.composer.placeholder`),disabled:o,onChange:e=>a(e.target.value),onKeyDown:e=>{(e.ctrlKey||e.metaKey)&&e.key===`Enter`&&i.trim().length>0&&!o&&F()},onPaste:e=>{let t=X(e.clipboardData.files);t.length>0&&(e.preventDefault(),I(t))},onDragOver:e=>{e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);t.length>0&&(e.preventDefault(),I(t))}}),(0,l.jsx)(`input`,{ref:d,type:`file`,accept:Oe,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&I(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o,title:r(`image.add`),onClick:()=>d.current?.click(),children:o?`…`:`🖼`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-composer-send`,disabled:i.trim().length===0||o,onClick:F,children:r(`detail.composer.send`)})]})]}),e.executions.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-section`,children:[(0,l.jsxs)(`h4`,{children:[r(`detail.exec.title`),(0,l.jsx)(`span`,{className:`dsh-atb-count2`,children:e.executions.length}),e.executionsPruned!==void 0&&e.executionsPruned>0&&(0,l.jsx)(`span`,{className:`dsh-atb-count2`,title:r(`detail.exec.prunedTitle`,{n:e.executionsPruned}),children:r(`detail.exec.pruned`,{n:e.executionsPruned})})]}),(0,l.jsx)(`div`,{className:`dsh-atb-execlist`,children:[...e.executions].reverse().map(e=>(0,l.jsxs)(`div`,{className:`dsh-atb-exec-row`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-exec-dot`,"data-outcome":e.outcome}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-trigger`,children:e.trigger===`manual`?r(`detail.exec.trigger.manual`):r(`detail.exec.trigger.scheduled`)}),(0,l.jsx)(`span`,{className:`dsh-atb-exec-outcome`,"data-outcome":e.outcome,children:r(_e[e.outcome]??e.outcome)}),(0,l.jsxs)(`span`,{className:`dsh-atb-exec-time`,children:[J(e.startedAt),e.endedAt!==void 0&&` · ${Fe(e.startedAt,e.endedAt)}`]}),e.sessionId!==void 0&&(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-exec-session`,title:r(`detail.exec.openTitle`,{id:e.sessionId}),onClick:()=>L(e.sessionId),children:[`🤖 `,Z(e.sessionId),` ↗`]}),e.error!==void 0&&(0,l.jsxs)(`span`,{className:`dsh-atb-exec-error`,title:e.error,children:[e.error.slice(0,80),e.error.length>80?`…`:``]})]},e.id))})]}),(0,l.jsx)(`div`,{className:`dsh-atb-dangerzone`,children:e.trashedAt===void 0?(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void t.remove(e.id,e.version,!1),children:r(`detail.danger.delete`)}):m?(0,l.jsxs)(`span`,{className:`dsh-atb-confirm`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-confirm-label`,children:r(`detail.danger.purgeConfirm`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{t.remove(e.id,e.version,!0),h(!1)},children:r(`detail.danger.purgeGo`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>h(!1),children:r(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>h(!0),children:r(`detail.danger.purge`)})}),ee]})}const Ge=e=>[{name:`goal`,kind:`command`,description:e(`slash.cmd.goal.desc`),hint:e(`slash.cmd.goal.hint`)},{name:`schedule`,kind:`command`,description:e(`slash.cmd.schedule.desc`),hint:e(`slash.cmd.schedule.hint`)},{name:`plan`,kind:`command`,description:e(`slash.cmd.plan.desc`)},{name:`browser`,kind:`command`,description:e(`slash.cmd.browser.desc`)},{name:`grill-me`,kind:`command`,description:e(`slash.cmd.grill-me.desc`)},{name:`teamwork-preview`,kind:`command`,description:e(`slash.cmd.teamwork-preview.desc`)},{name:`learn`,kind:`command`,description:e(`slash.cmd.learn.desc`)},{name:`review`,kind:`command`,description:e(`slash.cmd.review.desc`)},{name:`security`,kind:`command`,description:e(`slash.cmd.security.desc`)},{name:`permission`,kind:`command`,description:e(`slash.cmd.permission.desc`),hint:e(`slash.cmd.permission.hint`)}],Ke=e=>[{name:`frontend-ui-engineering`,kind:`skill`,description:e(`slash.skill.frontend-ui-engineering`)},{name:`api-and-interface-design`,kind:`skill`,description:e(`slash.skill.api-and-interface-design`)},{name:`test-driven-development`,kind:`skill`,description:e(`slash.skill.test-driven-development`)},{name:`debugging-and-error-recovery`,kind:`skill`,description:e(`slash.skill.debugging-and-error-recovery`)},{name:`performance-optimization`,kind:`skill`,description:e(`slash.skill.performance-optimization`)},{name:`ci-cd-and-automation`,kind:`skill`,description:e(`slash.skill.ci-cd-and-automation`)},{name:`code-review-and-quality`,kind:`skill`,description:e(`slash.skill.code-review-and-quality`)},{name:`code-simplification`,kind:`skill`,description:e(`slash.skill.code-simplification`)},{name:`context-engineering`,kind:`skill`,description:e(`slash.skill.context-engineering`)},{name:`doubt-driven-development`,kind:`skill`,description:e(`slash.skill.doubt-driven-development`)},{name:`git-workflow-and-versioning`,kind:`skill`,description:e(`slash.skill.git-workflow-and-versioning`)},{name:`idea-refine`,kind:`skill`,description:e(`slash.skill.idea-refine`)},{name:`incremental-implementation`,kind:`skill`,description:e(`slash.skill.incremental-implementation`)},{name:`interview-me`,kind:`skill`,description:e(`slash.skill.interview-me`)},{name:`memory-leak-debugging`,kind:`skill`,description:e(`slash.skill.memory-leak-debugging`)},{name:`observability-and-instrumentation`,kind:`skill`,description:e(`slash.skill.observability-and-instrumentation`)},{name:`planning-and-task-breakdown`,kind:`skill`,description:e(`slash.skill.planning-and-task-breakdown`)},{name:`security-and-hardening`,kind:`skill`,description:e(`slash.skill.security-and-hardening`)},{name:`shipping-and-launch`,kind:`skill`,description:e(`slash.skill.shipping-and-launch`)},{name:`source-driven-development`,kind:`skill`,description:e(`slash.skill.source-driven-development`)},{name:`spec-driven-development`,kind:`skill`,description:e(`slash.skill.spec-driven-development`)},{name:`using-agent-skills`,kind:`skill`,description:e(`slash.skill.using-agent-skills`)}];function qe({value:e,onChange:t,controller:n,placeholder:r,rows:i=4,maxLength:a=8e3,disabled:o=!1,autoFocus:s=!1,className:d,ariaLabel:f,allowImages:p=!1,onUploadingChange:m}){let h=W(),g=(0,c.useRef)(null),_=(0,c.useRef)(null),v=(0,c.useRef)(null),y=(0,c.useRef)(null),[b,x]=(0,c.useState)({}),[S,ee]=(0,c.useState)(void 0),te=(0,c.useMemo)(()=>{let e=(e,t)=>{let n=new Map;for(let t of e)n.set(t.name,t);for(let e of t??[])n.set(e.name,e);return Array.from(n.values())};return{commands:e(Ge(h),S?.commands),skills:e(Ke(h),S?.skills)}},[h,S]),[C,w]=(0,c.useState)(!1),[T,E]=(0,c.useState)(``),[D,O]=(0,c.useState)(-1),[k,A]=(0,c.useState)(0),[j,M]=(0,c.useState)(!1),ne=async r=>{let i=X(r);if(!p||n===void 0||i.length===0||j)return;M(!0),m?.(!0);let a=g.current,o=a?.value??e,s=a?.selectionStart??o.length,c=a?.selectionEnd??s,l=!1;try{for(let e of i){let t=await n.uploadImage(e);if(t===void 0)continue;let r=Me(o,s,c,je(t,Ae(e.name,h(`image.defaultAlt`))));o=r.value,s=r.cursor,c=s,l=!0}l&&(t(o),setTimeout(()=>{g.current?.focus(),g.current?.setSelectionRange(s,s)},0))}finally{M(!1),m?.(!1)}};(0,c.useEffect)(()=>{if(n===void 0)return;let e=!0;return n.fetchPromptCompletions().then(t=>{!e||t===void 0||ee({commands:t.commands.map(e=>({...e,kind:`command`})),skills:t.skills.map(e=>({...e,kind:`skill`}))})}),()=>{e=!1}},[n]);let N=(0,c.useMemo)(()=>{let e=T.toLowerCase().trim(),t=[...te.commands,...te.skills];return e.length===0?t:t.filter(t=>t.name.toLowerCase().includes(e)||t.description!==void 0&&t.description.toLowerCase().includes(e))},[te,T]);(0,c.useEffect)(()=>{k>=N.length&&A(Math.max(0,N.length-1))},[N.length,k]),(0,c.useLayoutEffect)(()=>{if(!C)return;let e=v.current,t=e?.children[k];if(e===null||!(t instanceof HTMLElement))return;let n=e.getBoundingClientRect(),r=t.getBoundingClientRect();r.top<n.top?e.scrollTop-=n.top-r.top:r.bottom>n.bottom&&(e.scrollTop+=r.bottom-n.bottom)},[C,k,N]);let P=()=>{let e=g.current;if(e===null)return;let t=e.selectionStart,n=e.value.slice(0,t),r=n.lastIndexOf(`/`);if(r>=0){let e=r>0?n[r-1]??` `:` -`,t=/\s/.test(e)||r===0,i=n.slice(r+1),a=!/\s/.test(i);if(t&&a){O(r),E(i),w(!0);return}}w(!1)},F=n=>{let r=g.current;if(r===null||D<0)return;let i=r.selectionStart,a=e.slice(0,D),o=e.slice(i),s=`/${n.name} `;t(a+s+o),w(!1),setTimeout(()=>{if(g.current!==null){let e=D+s.length;g.current.focus(),g.current.setSelectionRange(e,e)}},0)},I=e=>{if(C&&N.length>0){if(e.key===`ArrowDown`){e.preventDefault(),A(e=>(e+1)%N.length);return}if(e.key===`ArrowUp`){e.preventDefault(),A(e=>(e-1+N.length)%N.length);return}if(e.key===`Enter`||e.key===`Tab`){let t=N[k];if(t!==void 0){e.preventDefault(),F(t);return}}if(e.key===`Escape`){e.preventDefault(),w(!1);return}}},L=(0,c.useCallback)(()=>{let e=g.current;if(e===null)return;let t=e.getBoundingClientRect(),n=window.innerHeight,r=_.current?.offsetHeight??0,i=r>0?r:240,a=t.top-6-8,o=n-8-(t.bottom+6),s=o>a,c=Math.min(i,Math.max(s?o:a,120)),l=s?t.bottom+6:t.top-6-c;x(e=>e.left===t.left&&e.top===l&&e.width===t.width&&e.maxHeight===c?e:{position:`fixed`,left:t.left,top:l,width:t.width,maxHeight:c,zIndex:100})},[]);return(0,c.useLayoutEffect)(()=>{C&&L()}),(0,c.useEffect)(()=>{if(C)return window.addEventListener(`scroll`,L,!0),window.addEventListener(`resize`,L),()=>{window.removeEventListener(`scroll`,L,!0),window.removeEventListener(`resize`,L)}},[C,L]),(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-wrap ${d??``}`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-inner`,children:[(0,l.jsx)(`textarea`,{ref:g,className:`dsh-atb-prompt-input`,value:e,rows:i,maxLength:a,disabled:o||j,autoFocus:s,placeholder:r,"aria-label":f,onChange:e=>{t(e.target.value),P()},onKeyUp:P,onClick:P,onKeyDown:I,onPaste:e=>{let t=X(e.clipboardData.files);p&&t.length>0&&(e.preventDefault(),ne(t))},onDragOver:e=>{p&&e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);p&&t.length>0&&(e.preventDefault(),ne(t))}}),C&&N.length>0&&(0,u.createPortal)((0,l.jsxs)(`div`,{ref:_,className:`dsh-atb-slash-popup`,style:b,role:`listbox`,"aria-label":h(`slash.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-slash-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-title`,children:h(`slash.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-slash-hint`,children:h(`slash.hint`)})]}),(0,l.jsx)(`div`,{ref:v,className:`dsh-atb-slash-list`,children:N.map((e,t)=>(0,l.jsxs)(`div`,{role:`option`,"aria-selected":t===k,className:`dsh-atb-slash-item`,"data-active":t===k?`true`:void 0,"data-kind":e.kind,onClick:()=>F(e),onMouseEnter:()=>A(t),children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-badge`,"data-kind":e.kind,children:e.kind===`command`?h(`slash.badge.command`):h(`slash.badge.skill`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-slash-name`,children:[`/`,e.name]}),e.hint&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-param`,children:e.hint}),e.description&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-desc`,children:e.description})]},`${e.kind}-${e.name}`))})]}),document.body)]}),p&&(0,l.jsxs)(`div`,{className:`dsh-atb-image-actions`,children:[(0,l.jsx)(`input`,{ref:y,type:`file`,accept:`image/png,image/jpeg,image/gif,image/webp`,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&ne(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o||j,onClick:()=>y.current?.click(),children:h(j?`image.uploading`:`image.add`)}),(0,l.jsx)(`span`,{className:`dsh-atb-image-hint`,children:h(`image.hint`)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-prompt-foot`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-prompt-tip`,children:[h(`slash.tipA`),` `,(0,l.jsx)(`code`,{children:`/`}),` `,h(`slash.tipB`)]})})]})}const Je=`dsh-taskboard-last-model-v1`;function Ye(){try{let e=localStorage.getItem(Je);if(e===null)return;let t=JSON.parse(e);if(typeof t==`object`&&t){let{provider:e,model:n,reasoningEffort:r}=t;if(typeof e==`string`&&typeof n==`string`&&e.trim().length>0&&n.trim().length>0)return{provider:e.trim(),model:n.trim(),...typeof r==`string`&&r.trim().length>0?{reasoningEffort:r.trim()}:{}}}return}catch{return}}function Xe(e){try{e===void 0?localStorage.removeItem(Je):localStorage.setItem(Je,JSON.stringify(e))}catch{}}const Ze=e=>[{value:`urgent`,label:e(`form.urgency.urgent`)},{value:`normal`,label:e(`form.urgency.normal`)},{value:`relaxed`,label:e(`form.urgency.relaxed`)}],Qe=e=>[{label:e(`form.cron.daily`),cron:`0 9 * * *`},{label:e(`form.cron.hourly`),cron:`0 * * * *`},{label:e(`form.cron.every10min`),cron:`*/10 * * * *`},{label:e(`form.cron.weekly`),cron:`0 9 * * 1`}],$e=e=>[{value:`workspace-write`,label:e(`form.perm.write`),icon:`📁`},{value:`read-only`,label:e(`form.perm.readOnly`),icon:`🔒`},{value:`danger-full-access`,label:e(`form.perm.fullAccess`),icon:`⚡`}];function $({label:e,required:t=!1,full:n=!1,children:r}){return(0,l.jsxs)(`label`,{className:`dsh-atb-field`,"data-span":n?`full`:void 0,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-field-label`,children:[e,t&&(0,l.jsx)(`em`,{className:`dsh-atb-req`,children:`*`})]}),r]})}function et({rows:e,onChange:t,editing:n}){let r=W(),i=(n,r)=>{t(e.map((e,t)=>t===n?{...e,...r}:e))},a=e.filter(e=>e.checked).length;return(0,l.jsxs)(`div`,{className:`dsh-atb-cke`,children:[e.map((a,o)=>(0,l.jsxs)(`div`,{className:`dsh-atb-cke-row`,children:[n&&(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-cke-box`,checked:a.checked,title:r(`form.check.checkedTitle`,{who:a.checkedBy??r(`form.check.notCheckedYet`)}),onChange:e=>i(o,{checked:e.target.checked})}),(0,l.jsx)(`input`,{className:`dsh-atb-cke-text`,value:a.text,maxLength:200,placeholder:r(`form.check.itemPlaceholder`,{n:o+1}),spellCheck:!1,onChange:e=>i(o,{text:e.target.value})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-del`,title:r(`form.check.removeTitle`),onClick:()=>t(e.filter((e,t)=>t!==o)),children:`✕`})]},a.id??`new-${o}`)),e.length<30&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-add`,onClick:()=>t([...e,{text:``,checked:!1}]),children:r(`form.check.add`)}),e.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cke-hint`,children:n?r(`form.check.hintEdit`,{checked:a,total:e.length}):r(`form.check.hintCreate`,{n:e.length})})]})}function tt({controller:e,task:t}){let n=W(),r=e.getSnapshot(),i=r.templatePrefill,a=t!==void 0,[o,s]=(0,c.useState)(t?.title??i?.title??``),[u,d]=(0,c.useState)(t?.description??i?.description??``),[f,p]=(0,c.useState)(t?.prompt??i?.prompt??``),[m,h]=(0,c.useState)(t?.workspaceId??r.filters.workspaceId??r.workspaces[0]?.id??``),[g,_]=(0,c.useState)(t?.urgency??(i?.urgency===`urgent`||i?.urgency===`relaxed`?i.urgency:`normal`)),v=t?.execution??i?.execution,[y,b]=(0,c.useState)(v?.mode===`scheduled`?v.cron===void 0?`once`:`periodic`:`claim`),[x,te]=(0,c.useState)(t?.execution.cron??i?.execution?.cron??`0 9 * * *`),[w,T]=(0,c.useState)(t?.execution.periodicCompletion??i?.execution?.periodicCompletion??`spawn`),[E,D]=(0,c.useState)(t?.execution.reuseSessionId===void 0?t===void 0?i?.execution?.reuseSessionId===void 0?i?.execution?.sessionReuseMode??`fresh`:`selected:${i.execution.reuseSessionId}`:t.execution.sessionReuseMode??`reuse`:`selected:${t.execution.reuseSessionId}`),[k,j]=(0,c.useState)([]),[M,ne]=(0,c.useState)(!1),[N,P]=(0,c.useState)(!1),F=v?.mode===`scheduled`&&v.runAt!==void 0?v.runAt:void 0,[I,L]=(0,c.useState)(()=>{if(F===void 0)return``;let e=new Date(F);if(Number.isNaN(e.getTime()))return``;let t=e=>String(e).padStart(2,`0`);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`}),[R,re]=(0,c.useState)([]),z=a?t.model:i?.model??r.ledger.settings?.defaultModel??Ye(),[ie,B]=(0,c.useState)(z===void 0?``:JSON.stringify({provider:z.provider,model:z.model})),[ae,oe]=(0,c.useState)(z?.reasoningEffort??``),se=a?t.presetId??``:i?.presetId??r.ledger.settings?.defaultPresetId??``,[V,ce]=(0,c.useState)(se),[H,le]=(0,c.useState)([]),[U,ue]=(0,c.useState)(void 0),[G,de]=(0,c.useState)(t?.permission??(i?.permission?S(i.permission):C(r.ledger.settings))),[fe,pe]=(0,c.useState)(t?.isolation??(i?.isolation===`none`?`none`:i?.isolation===`worktree`?`worktree`:ee(r.ledger.settings))),[me,he]=(0,c.useState)(t?.checklist!==void 0&&t.checklist.length>0?t.checklist.map(e=>({...e})):(i?.checklist??[]).map(e=>({text:e,checked:!1}))),ge=(0,c.useRef)(null),[K,q]=(0,c.useState)(!1),[_e,ve]=(0,c.useState)(!1);(0,c.useEffect)(()=>{ge.current?.focus();let t=t=>{t.key===`Escape`&&e.closeForm()};return document.addEventListener(`keydown`,t),()=>document.removeEventListener(`keydown`,t)},[e]),(0,c.useEffect)(()=>{e.fetchModelCatalog().then(re).catch(()=>re([]))},[e]),(0,c.useEffect)(()=>{if(m===``)return;let t=!0;return ne(!0),P(!1),e.fetchProjectSessions(m).then(e=>{t&&j(e)}).catch(()=>{t&&(j([]),P(!0))}).finally(()=>{t&&ne(!1)}),()=>{t=!1}},[e,m]),(0,c.useEffect)(()=>{e.fetchPresetCatalog().then(e=>{le(e.presets),ue(e.defaultId),!a&&se===``&&e.defaultId!==void 0&&ce(e.defaultId)}).catch(()=>le([]))},[e,a,t?.presetId,se]);let ye=y===`periodic`?O(x.trim()):null,be=ye===null?null:A(ye,Date.now()),xe=y===`periodic`&&(ye===null||be===null),Se=y===`once`&&I!==``?new Date(I).getTime():NaN,Ce=y===`once`&&(I===``||Number.isNaN(Se)||Se<=Date.now()),we=o.trim().length>0&&m!==``&&!xe&&!Ce&&(y===`claim`||!E.startsWith(`selected:`)||!M&&k.some(e=>e.id===E.slice(9))),Te=()=>{let e=E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{sessionReuseMode:E===`reuse`?`reuse`:`fresh`};return y===`periodic`?{mode:`scheduled`,cron:x.trim(),periodicCompletion:w,...e}:y===`once`?{mode:`scheduled`,runAt:new Date(I).toISOString(),...E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{}}:{mode:`claim`}},Ee=a&&t.status===`in_progress`,Y=a&&((t.executions?.length??0)>0||t.status===`in_progress`),De=e.gitAvailable(m),Oe=e.repoCount(m),ke=Y||!De,Ae=()=>{if(De)return fe},je=()=>V.trim().length>0?V.trim():void 0,Me=()=>me.map(e=>({...e,text:e.text.trim()})).filter(e=>e.text.length>0),X=ie===``?void 0:JSON.parse(ie),Ne=X===void 0?void 0:R.find(e=>e.provider===X.provider&&e.model===X.model),Z=Ne?.reasoning,Pe=()=>{if(X===void 0)return;let e=ae.trim();return{provider:X.provider,model:X.model,...e.length>0?{reasoningEffort:e}:{}}},Fe=()=>{if(!we||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(a?e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null}):e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}})).catch(()=>void 0).finally(()=>q(!1))},Q=()=>{if(!we||Ee||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(async()=>{if(a)await e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null})&&await e.run(t.id);else{let t=await e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}});t!==void 0&&await e.run(t)}})().catch(()=>void 0).finally(()=>q(!1))},Ie=we?y===`periodic`&&be!==null?n(`form.hint.nextRun`,{time:J(be)}):y===`once`&&!Number.isNaN(Se)?n(`form.hint.onceAt`,{time:J(Se)}):a?n(`form.hint.saveVersion`,{v:t.version,next:t.version+1}):n(`form.hint.createClaim`):o.trim().length===0?n(`form.hint.needTitle`):n(m===``?`form.hint.needProject`:Ce?`form.hint.runAtBad`:`form.hint.cronBad`);return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-taskform-modal`,"data-mode":a?`edit`:`create`,role:`dialog`,"aria-modal":`true`,"aria-label":n(a?`form.title.edit`:`form.title.create`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:a?`✎`:`✚`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:n(a?`form.title.edit`:`form.title.create`)}),(0,l.jsx)(`p`,{children:n(a?`form.subtitle.edit`:`form.subtitle.create`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeForm(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body dsh-atb-taskform-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-left`,children:[(0,l.jsx)($,{label:n(`form.field.title`),required:!0,full:!0,children:(0,l.jsx)(`input`,{ref:ge,value:o,onChange:e=>s(e.target.value),placeholder:n(`form.field.titlePlaceholder`),maxLength:200})}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.project`),required:!0,children:(0,l.jsx)(`select`,{value:m,onChange:e=>{h(e.target.value),D(`fresh`),j([])},children:r.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))})}),(0,l.jsx)($,{label:n(`form.field.model`),children:(0,l.jsxs)(`select`,{value:ie,onChange:e=>{let t=e.target.value;if(B(t),t===``)oe(``);else{let e=JSON.parse(t),n=R.find(t=>t.provider===e.provider&&t.model===e.model);n?.reasoning?.defaultEffort===void 0?oe(``):oe(n.reasoning.defaultEffort)}},children:[(0,l.jsx)(`option`,{value:``,children:n(`form.field.modelDefault`)}),X!==void 0&&Ne===void 0&&(0,l.jsxs)(`option`,{value:ie,children:[X.model,` (`,X.provider,`)`]}),R.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:n(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})}),X!==void 0&&(0,l.jsx)($,{label:n(`form.field.effort`),children:(0,l.jsxs)(`select`,{value:ae,onChange:e=>oe(e.target.value),title:n(`form.field.effortTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.effort.follow`),Z?.defaultEffort===void 0?``:n(`shared.current`,{name:Z.efforts.find(e=>e.id===Z.defaultEffort)?.name??Z.defaultEffort})]}),Z!==void 0&&Z.efforts.length>0?Z.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id)):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`option`,{value:`low`,children:n(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:n(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:n(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:n(`form.effort.none`)})]})]})}),(H.length>0||V!==``)&&(0,l.jsx)($,{label:n(`form.field.preset`),children:(0,l.jsxs)(`select`,{value:V,onChange:e=>ce(e.target.value),title:n(`form.field.presetTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.preset.follow`),U===void 0?``:n(`shared.current`,{name:H.find(e=>e.id===U)?.name??U})]}),V!==``&&!H.some(e=>e.id===V)&&(0,l.jsx)(`option`,{value:V,children:V}),H.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===U?n(`form.preset.defaultTag`):``]},e.id))]})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.permission`),children:(0,l.jsx)(`select`,{className:`dsh-atb-permission-select`,value:G,onChange:e=>de(S(e.target.value)),children:$e(n).map(e=>(0,l.jsxs)(`option`,{value:e.value,children:[e.icon,` `,e.label,e.value===`workspace-write`?n(`form.perm.defaultTag`):``]},e.value))})}),(0,l.jsx)($,{label:n(`form.field.urgency`),children:(0,l.jsx)(`select`,{className:`dsh-atb-urgency-select`,value:g,onChange:e=>_(e.target.value),children:Ze(n).map(e=>(0,l.jsx)(`option`,{value:e.value,children:e.label},e.value))})})]}),(0,l.jsx)($,{label:n(`form.field.mode`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-exec":`true`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`claim`,onClick:()=>b(`claim`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.claim`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.claimHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`once`,onClick:()=>b(`once`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.once`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.onceHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`periodic`,onClick:()=>b(`periodic`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.periodic`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.periodicHint`)})]})]})}),y===`once`&&(0,l.jsx)($,{label:n(`form.field.runAt`),required:!0,full:!0,children:(0,l.jsx)(`input`,{type:`datetime-local`,className:Ce?`dsh-atb-input-bad`:void 0,value:I,onChange:e=>L(e.target.value),spellCheck:!1})}),y===`periodic`&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)($,{label:n(`form.field.cron`),required:!0,full:!0,children:[(0,l.jsx)(`input`,{className:xe?`dsh-atb-input-bad`:void 0,value:x,onChange:e=>te(e.target.value),placeholder:n(`form.cron.placeholder`),spellCheck:!1}),(0,l.jsxs)(`span`,{className:`dsh-atb-cron-presets`,children:[Qe(n).map(e=>(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cron-preset`,"data-on":x.trim()===e.cron,onClick:()=>te(e.cron),children:e.label},e.cron)),!xe&&be!==null&&(0,l.jsx)(`span`,{className:`dsh-atb-cron-next`,children:n(`form.cron.next`,{time:J(be)})})]})]}),(0,l.jsx)($,{label:n(`form.field.periodicCompletion`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`rearm`,onClick:()=>T(`rearm`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.rearm`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.rearmHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`spawn`,onClick:()=>T(`spawn`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.spawn`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.spawnHint`)})]})]})})]}),y===`periodic`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E,onChange:e=>D(e.target.value),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.fresh`)}),(0,l.jsx)(`option`,{value:`reuse`,children:n(`form.session.reuse`)}),k.map(e=>(0,l.jsx)(`option`,{value:`selected:${e.id}`,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),y===`once`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E.startsWith(`selected:`)?E:`fresh`,onChange:e=>D(e.target.value===`fresh`?`fresh`:`selected:${e.target.value}`),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.new`)}),k.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),(0,l.jsxs)($,{label:n(`form.field.isolation`),full:!0,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-disabled":ke?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`worktree`,disabled:ke,title:n(Y?`form.iso.locked`:De?`form.iso.worktreeTitle`:`form.iso.nonGit`),onClick:()=>pe(`worktree`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.worktree`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.worktreeHint`:`form.iso.nonGit`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`none`,disabled:ke,title:n(Y?`form.iso.locked`:`form.iso.noneTitle`),onClick:()=>pe(`none`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.none`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.noneHint`:`form.iso.noneHintNonGit`)})]})]}),!De&&!Y&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.iso.nonGitNote`)}),De&&!Y&&Oe>1&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,"data-mirror":`true`,children:n(`form.iso.mirrorNote`,{n:Oe})})]}),(0,l.jsx)($,{label:n(a?`form.field.checklist`:`form.field.checklistOptional`),full:!0,children:(0,l.jsx)(et,{rows:me,onChange:he,editing:a})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-right`,children:[(0,l.jsx)($,{label:n(a?`form.field.description`:`form.field.descriptionOptional`),full:!0,children:(0,l.jsx)(qe,{value:u,onChange:d,controller:e,rows:7,placeholder:n(`form.desc.placeholder`),allowImages:!0,onUploadingChange:ve})}),(0,l.jsx)($,{label:n(a?`form.field.prompt`:`form.field.promptOptional`),full:!0,children:(0,l.jsx)(qe,{value:f,onChange:p,controller:e,rows:7,placeholder:n(`form.prompt.placeholder`)})})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,"data-tone":we?void 0:`bad`,children:Ie}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeForm(),children:n(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!we||Ee||K||_e,title:n(Ee?`form.action.runBlockedTitle`:K||_e?`form.action.runBusyTitle`:`form.action.runTitle`),onClick:Q,children:n(`form.action.run`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!we||K||_e,onClick:Fe,children:n(a?`form.action.save`:`form.action.create`)})]})]})]})})}const nt=e=>[{value:`none`,name:e(`form.iso.none`),hint:e(`set.iso.noneHint`)},{value:`worktree`,name:e(`form.iso.worktree`),hint:e(`set.iso.worktreeHint`)}];function rt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.ledger.settings?.defaultIsolation??`none`,i=te(n.ledger.settings),a=C(n.ledger.settings),o=w(n.ledger.settings),s=T(n.ledger.settings),u=E(n.ledger.settings),d=D(n.ledger.settings),f=n.ledger.settings?.defaultModel,p=f===void 0?``:JSON.stringify({provider:f.provider,model:f.model}),m=f?.reasoningEffort??``,h=n.ledger.settings?.defaultPresetId??``,[g,_]=(0,c.useState)([]),[v,y]=(0,c.useState)([]),[b,x]=(0,c.useState)(),[S,ee]=(0,c.useState)(p),[O,k]=(0,c.useState)(m),[A,j]=(0,c.useState)(h),M=S===``?void 0:JSON.parse(S),ne=g.find(e=>e.provider===M?.provider&&e.model===M?.model),N=ne?.reasoning,[P,F]=(0,c.useState)(r),[I,L]=(0,c.useState)(i),[R,re]=(0,c.useState)(a),[z,ie]=(0,c.useState)(String(o)),[B,ae]=(0,c.useState)(String(s)),[oe,se]=(0,c.useState)(String(u)),[V,ce]=(0,c.useState)(String(d)),[H,le]=(0,c.useState)(n.storage?.currentDirectory??``),[U,ue]=(0,c.useState)(!1),[G,de]=(0,c.useState)(!1),fe=Number(z),pe=Number(B),me=Number(oe),he=Number(V),ge=Number.isSafeInteger(fe)&&fe>=1&&fe<=100&&Number.isSafeInteger(pe)&&pe>=1&&pe<=1440&&Number.isSafeInteger(me)&&me>=0&&me<=10080&&Number.isSafeInteger(he)&&he>=0&&he<=6e4,K=S!==p||O!==m||A!==h||P!==r||I!==i||R!==a||z!==String(o)||B!==String(s)||oe!==String(u)||V!==String(d),q=H.trim().length===0?n.storage?.defaultDirectory??``:H.trim(),_e=n.storage!==void 0&&q!==n.storage.currentDirectory;return(0,c.useEffect)(()=>{!U&&n.storage!==void 0&&le(n.storage.currentDirectory)},[n.storage,U]),(0,c.useEffect)(()=>{let t=!0;return e.fetchModelCatalog().then(e=>{t&&_(e)}).catch(()=>{}),e.fetchPresetCatalog().then(e=>{t&&(y(e.presets),x(e.defaultId))}).catch(()=>{}),()=>{t=!1}},[e]),(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeSettings()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-set`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`set.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`🛠`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`set.title`)}),(0,l.jsx)(`p`,{children:t(`set.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeSettings(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.iso.heading`)}),(0,l.jsx)(`div`,{className:`dsh-atb-mode-picker`,children:nt(t).map(e=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":P===e.value,title:e.hint,onClick:()=>F(e.value),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:e.name}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:e.hint})]},e.value))}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.iso.current`,{current:t(r===`worktree`?`form.iso.worktree`:`form.iso.none`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.sync.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":!I,title:t(`set.sync.off.title`),onClick:()=>L(!1),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.off.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.off.hint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":I,title:t(`set.sync.on.title`),onClick:()=>L(!0),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.on.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.on.hint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(i?`set.sync.stateOn`:`set.sync.stateOff`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.perm.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-perm-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`workspace-write`,onClick:()=>re(`workspace-write`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.writeName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.writeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`read-only`,onClick:()=>re(`read-only`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.readOnlyName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.readOnlyHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`danger-full-access`,onClick:()=>re(`danger-full-access`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.fullName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.fullHint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.perm.current`,{current:t(a===`read-only`?`set.perm.readOnlyName`:a===`danger-full-access`?`set.perm.fullName`:`set.perm.writeName`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.agent.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.agent.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.model`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:S,onChange:e=>{ee(e.target.value),k(``)},children:[(0,l.jsx)(`option`,{value:``,children:t(`form.field.modelDefault`)}),M!==void 0&&ne===void 0&&(0,l.jsxs)(`option`,{value:S,children:[M.model,` (`,M.provider,`)`]}),g.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:t(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.effort`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:O,disabled:M===void 0,onChange:e=>k(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.effort.follow`),N?.defaultEffort===void 0?``:t(`shared.current`,{name:N.efforts.find(e=>e.id===N.defaultEffort)?.name??N.defaultEffort})]}),O!==``&&N!==void 0&&!N.efforts.some(e=>e.id===O)&&(0,l.jsx)(`option`,{value:O,children:O}),N===void 0?(0,l.jsxs)(l.Fragment,{children:[![``,`low`,`medium`,`high`,`none`].includes(O)&&(0,l.jsx)(`option`,{value:O,children:O}),(0,l.jsx)(`option`,{value:`low`,children:t(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:t(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:t(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:t(`form.effort.none`)})]}):N.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.preset`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:A,onChange:e=>j(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.preset.follow`),b===void 0?``:t(`shared.current`,{name:v.find(e=>e.id===b)?.name??b})]}),A!==``&&!v.some(e=>e.id===A)&&(0,l.jsx)(`option`,{value:A,children:A}),v.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===b?t(`form.preset.defaultTag`):``]},e.id))]})]})]})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.schedule.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.schedule.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-settings-numbers`,children:[(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.concurrent`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`100`,step:`1`,value:z,onChange:e=>ie(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.missedAfter`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`1440`,step:`1`,value:B,onChange:e=>ae(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.queueMaxAge`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`10080`,step:`1`,value:oe,onChange:e=>se(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.dispatchInterval`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`60000`,step:`1`,value:V,onChange:e=>ce(e.target.value)})]})]}),!ge&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:t(`set.schedule.invalid`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.storage.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.storage.hint`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-storage-path`,value:H,disabled:n.storage===void 0||G,placeholder:n.storage?.defaultDirectory??t(`set.storage.loading`),onChange:t=>{le(t.target.value),ue(!0),e.dismissStorageNotice()}}),n.storageNotice!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-notice`,role:`status`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-storage-notice-ok`,children:[`✓ `,t(`set.storage.migrated`,{path:n.storageNotice.path})]}),n.storageNotice.warnings.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-notice-warn`,children:t(`set.storage.warnings`,{warnings:n.storageNotice.warnings.join(`; `)})})]}),n.storage!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-meta`,children:[(0,l.jsx)(`span`,{children:t(`set.storage.current`,{path:n.storage.currentDirectory})}),(0,l.jsx)(`span`,{children:t(`set.storage.assets`,{count:n.storage.assetCount,size:(n.storage.assetBytes/1024/1024).toFixed(1)})}),n.storage.error!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:n.storage.error})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-storage-actions`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G,onClick:()=>{le(n.storage?.defaultDirectory??``),ue(!0)},children:t(`set.storage.default`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G||q.length===0,onClick:()=>{de(!0),e.checkStorage(q).finally(()=>de(!1))},children:t(`set.storage.check`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!_e||G,onClick:()=>{window.confirm(t(`set.storage.confirm`,{from:n.storage?.currentDirectory??``,to:q}))&&(de(!0),e.migrateStorage(q).then(e=>{e&&(ue(!1),le(q))}).finally(()=>de(!1)))},children:t(G?`set.storage.migrating`:`set.storage.migrate`)})]})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(K?`set.foot.dirty`:`set.foot.clean`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeSettings(),children:t(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!K||!ge,onClick:()=>{e.updateSettings({defaultIsolation:P,syncExternalSessions:I,defaultPermission:R,...M===void 0?{}:{defaultModel:{...M,...O===``?{}:{reasoningEffort:O}}},...A===``?{}:{defaultPresetId:A},maxConcurrent:fe,scheduleMissedAfterMinutes:pe,queueMaxAgeMinutes:me,dispatchIntervalMs:he}).then(t=>{t&&e.closeSettings()})},children:t(`set.action.save`)})]})]})]})})}function it({row:e}){return(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,title:e.id,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.status})]})}function at({controller:e}){let t=W(),[n,r]=(0,c.useState)(``),[i,a]=(0,c.useState)(null),[o,s]=(0,c.useState)(void 0),[u,d]=(0,c.useState)(void 0),[f,p]=(0,c.useState)(`merge`),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(void 0),[v,y]=(0,c.useState)(!1),b=(0,c.useRef)(null),{alert:x,el:S}=Y(),ee=n=>{d(void 0),s(void 0),_(void 0),y(!1),r(``),a(null),n!==void 0&&n.text().then(i=>{try{let t=JSON.parse(i);a(t),r(n.name),e.importPreview(t).then(e=>{e!==void 0&&d(e)})}catch{s(t(`imp.parseError`))}})},te=()=>{if(!(i===null||u===void 0||m)){if(f===`replace`&&!v){y(!0);return}h(!0),e.importCommit(f,i).then(e=>{h(!1),y(!1),e!==void 0&&_(e.mode===`replace`?t(`imp.result.replace`,{n:e.created+e.overwritten,total:e.replacedTotal??0}):t(`imp.result.merge`,{n:e.created,m:e.overwritten}))})}},C=()=>e.closeImport();return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&C()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-imp`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`imp.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⬆`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`imp.title`)}),(0,l.jsx)(`p`,{children:t(`imp.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:C,children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-picker`,children:[(0,l.jsx)(`input`,{ref:b,type:`file`,accept:`.json,application/json`,onChange:e=>ee(e.target.files?.[0])}),n.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-imp-filename`,children:n})]}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-note`,children:t(`imp.note`)}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-error`,children:o}),u===void 0&&o===void 0&&n.length>0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`imp.previewing`)}),u!==void 0&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stats`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`ok`,children:[(0,l.jsx)(`b`,{children:u.create.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.create`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`warn`,children:[(0,l.jsx)(`b`,{children:u.overwrite.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.overwrite`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":u.invalid.length>0?`bad`:void 0,children:[(0,l.jsx)(`b`,{children:u.invalid.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.invalid`)})]})]}),u.create.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.create`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.create.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.overwrite.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.overwrite`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.overwrite.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.invalid.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.invalid`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.invalid.map((e,n)=>(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,"data-tone":`bad`,title:e.id??``,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.id??t(`imp.noId`)}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.reason})]},e.id??`invalid-${n}`))})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`merge`,onClick:()=>{p(`merge`),y(!1)},children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.merge`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.mergeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`replace`,onClick:()=>p(`replace`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.replace`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.replaceHint`)})]})]}),g!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-result`,children:g})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(f===`replace`?v?`imp.foot.replaceConfirm`:`imp.foot.replaceNeedConfirm`:`imp.foot.mergeHint`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:C,children:t(g===void 0?`shared.cancel`:`shared.close`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,"data-danger":f===`replace`&&v?`true`:void 0,disabled:u===void 0||m,onClick:te,children:t(f===`replace`&&v?`imp.action.confirmReplace`:`imp.action.run`)})]})]})]}),S]})}function ot({controller:e}){let t=W(),n=e.getSnapshot(),[r,i]=(0,c.useState)({}),[a,o]=(0,c.useState)(void 0),{alert:s,el:u}=Y(),d=()=>e.closeTemplateManager(),f=e=>r[e.id]??xe(e),p=e=>{if(e===void 0)return``;let n=q[e];return n===void 0?` · ${e}`:` · ${t(n)}`},m=(r,a)=>{let o=n.templates.find(e=>e.id===r);o===void 0||a===xe(o)||e.upsertTemplate({id:r,name:a,task:o.task}).then(e=>{e&&(i(e=>{let t={...e};return delete t[r],t}),s(t(`tpl.renamed`)))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&d()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-tplm`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`tpl.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⌗`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`tpl.title`)}),(0,l.jsx)(`p`,{children:t(`tpl.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:d,children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:n.templates.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`tpl.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-tplm-list`,children:n.templates.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-tplm-row`,children:[(0,l.jsx)(`input`,{className:`dsh-atb-tplm-name`,value:f(n),maxLength:60,spellCheck:!1,"aria-label":t(`tpl.name.aria`,{name:xe(n)}),onChange:e=>i(t=>({...t,[n.id]:e.target.value})),onKeyDown:e=>{e.key===`Enter`&&m(n.id,f(n))}}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-meta`,title:`${n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`)}${n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``}${p(n.task.urgency)}${n.task.permission===void 0?``:` · ${t(`shared.permission`)}: ${n.task.permission}`}`,children:[n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`),n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``,p(n.task.urgency),n.task.permission!==void 0&&n.task.permission!==`workspace-write`?` · ${n.task.permission===`read-only`?t(`tpl.meta.permReadOnly`):t(`tpl.meta.permFull`)}`:``]}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-btns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:f(n)===xe(n)||f(n).trim().length===0,title:t(`tpl.rename.title`),onClick:()=>m(n.id,f(n)),children:t(`tpl.rename.button`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`tpl.use.title`),onClick:()=>{d(),e.newFromTemplate(Se(n))},children:t(`tpl.use.button`)}),a===n.id?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{e.deleteTemplate(n.id),o(void 0)},children:t(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(void 0),children:t(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,title:t(`tpl.delete.title`),onClick:()=>o(n.id),children:`🗑`})]})]},n.id))})}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(`tpl.foot.hint`)}),(0,l.jsx)(`span`,{className:`dsh-atb-modal-footbtns`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:d,children:t(`shared.close`)})})]})]}),u]})}const st={urgent:0,normal:1,relaxed:2};function ct(e,t){let n=e.search.trim().toLowerCase(),r=t.filter(t=>(e.filters.workspaceId===void 0||t.workspaceId===e.filters.workspaceId)&&(e.filters.urgencies.length===0||e.filters.urgencies.includes(t.urgency))&&(n.length===0||t.title.toLowerCase().includes(n)||t.id.toLowerCase().includes(n)));if(e.sortBy==="default")return r;let i=[...r];return e.sortBy===`updated`?i.sort((e,t)=>t.updatedAt-e.updatedAt):e.sortBy===`created`?i.sort((e,t)=>t.createdAt-e.createdAt):e.sortBy===`urgency`?i.sort((e,t)=>st[e.urgency]-st[t.urgency]||t.updatedAt-e.updatedAt):e.sortBy===`title`&&i.sort((e,t)=>e.title.localeCompare(t.title,void 0,{numeric:!0})),i}function lt({controller:e}){let t=W(),n=(0,c.useSyncExternalStore)(t=>e.subscribe(t),()=>e.getSnapshot()),[r,i]=(0,c.useState)(()=>Date.now());(0,c.useEffect)(()=>{let e=setInterval(()=>i(Date.now()),6e4);return()=>clearInterval(e)},[]);let a=ct(n,n.ledger.tasks.filter(e=>e.trashedAt===void 0)),o=n.selectedId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.selectedId),{alert:s,el:u}=Y(),[d,f]=(0,c.useState)(!1),p=()=>f(!1),[m,h]=(0,c.useState)(!1),g=()=>h(!1);return(0,l.jsxs)(`div`,{className:`dsh-atb-board`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-toolbar`,children:[(0,l.jsx)(`h2`,{className:`dsh-atb-title`,children:t(`board.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-count`,children:t(`board.count.tasks`,{n:a.length,rev:n.ledger.revision})}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{let t=!d;f(t),t&&e.prepareTemplateMenu()},children:t(`board.action.newTask`)}),d&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:p}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.setComposer(!0)},children:t(`board.action.blankTask`)}),n.templates.map(t=>{let n=xe(t),r=Se(t);return(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:r.description!==void 0&&r.description.length>0?r.description.slice(0,120):n,onClick:()=>{p(),e.newFromTemplate(r)},children:n},t.id)}),(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-sep`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.openTemplateManager()},children:t(`board.action.manageTemplates`)})]})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-spacer`}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-search`,value:n.search,placeholder:t(`board.search.placeholder`),spellCheck:!1,onChange:t=>e.setSearch(t.target.value)}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.filters.workspaceId??``,onChange:t=>e.setWorkspaceFilter(t.target.value===``?void 0:t.target.value),children:[(0,l.jsx)(`option`,{value:``,children:t(`board.filter.allProjects`)}),n.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))]}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.sortBy,title:t(`board.sort.title`),onChange:t=>e.setSortBy(t.target.value),children:[(0,l.jsx)(`option`,{value:`default`,children:t(`board.sort.default`)}),(0,l.jsx)(`option`,{value:`updated`,children:t(`board.sort.updated`)}),(0,l.jsx)(`option`,{value:`urgency`,children:t(`board.sort.urgency`)}),(0,l.jsx)(`option`,{value:`created`,children:t(`board.sort.created`)}),(0,l.jsx)(`option`,{value:`title`,children:t(`board.sort.byTitle`)})]}),[`urgent`,`normal`,`relaxed`].map(r=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip`,"data-urgency":r,"data-on":n.filters.urgencies.includes(r),onClick:()=>e.toggleUrgency(r),children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-urgency":r}),t(q[r])]},r)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.toggleSecondary(),children:n.secondaryOpen?t(`board.action.backToBoard`):t(`board.action.otherTasks`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.settingsTitle`),onClick:()=>e.openSettings(),children:t(`board.action.settings`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.diagTitle`),onClick:()=>e.openDiagnostics(),children:t(`board.action.diag`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.importTitle`),onClick:()=>e.openImport(),children:t(`board.action.import`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.exportTitle`),onClick:()=>h(!m),children:t(`board.action.export`)}),m&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:g}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.jsonTitle`),onClick:()=>{g(),e.exportJson()},children:t(`board.export.json`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.csvTitle`),onClick:()=>{g(),e.exportCsv()},children:t(`board.export.csv`)})]})]})]}),(0,l.jsxs)(`a`,{className:`dsh-atb-ver`,href:`https://github.com/cloader/dsh-taskboard`,target:`_blank`,rel:`noopener noreferrer`,children:[`V`,`0.8.6`]})]}),n.error!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-error`,children:n.error}),n.secondaryOpen?(0,l.jsx)(ft,{controller:e,tasks:ct(n,n.ledger.tasks)}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(i=>{let o=a.filter(e=>e.status===i);return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,onDragOver:e=>{e.dataTransfer.types.includes(`application/x-dsh-atb-task`)&&(e.preventDefault(),e.dataTransfer.dropEffect=`move`,e.currentTarget.dataset.dragover=`true`)},onDragLeave:e=>{delete e.currentTarget.dataset.dragover},onDrop:r=>{r.preventDefault(),delete r.currentTarget.dataset.dragover;let a=r.dataTransfer.getData(we);if(a.length===0)return;let o=n.ledger.tasks.find(e=>e.id===a);if(!(o===void 0||o.status===i)){if(!x(o.status,i)){s(t(`board.drag.forbidden`,{from:t(he[o.status]),to:t(he[i])}));return}e.move(a,o.version,i)}},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":i}),t(he[i]),(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:o.length}),i===`in_progress`&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-queuechip`,title:t(`queue.panel.title`),"data-bad":(n.queue?.depth??0)>0?`true`:void 0,onClick:()=>e.openQueuePanel(),children:t(`board.queue.chip`,{n:n.queue?.depth??0})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[o.map(t=>(0,l.jsx)(Ee,{task:t,controller:e,draggable:!0,now:r,onAlert:s},t.id)),o.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:t(`board.empty`)})]})]},i)})}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-detailpanel`,children:(0,l.jsx)(We,{task:o,controller:e,now:r},o.id)}),n.composerOpen&&(0,l.jsx)(tt,{controller:e,task:n.editingId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.editingId)}),n.diagOpen&&(0,l.jsx)(dt,{controller:e}),n.queueOpen&&(0,l.jsx)(ut,{controller:e,onAlert:s}),n.settingsOpen&&(0,l.jsx)(rt,{controller:e}),n.importOpen&&(0,l.jsx)(at,{controller:e}),n.tplManagerOpen&&(0,l.jsx)(ot,{controller:e}),u]})}function ut({controller:e,onAlert:t}){let n=W(),r=e.getSnapshot().queue,[i,a]=(0,c.useState)(!1),[o,s]=(0,c.useState)(!1),u=r?.depth??0,d=r?.oldestWaitMinutes,f=async()=>{if(!i){a(!0);return}s(!0);let r=await e.clearQueue();s(!1),a(!1),r!==void 0&&(t(n(`queue.cleared`,{n:r.cleared})),e.closeQueuePanel())};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeQueuePanel()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-queuepanel`,role:`dialog`,"aria-modal":`true`,"aria-label":n(`queue.panel.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`📋`}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-headtext`,children:(0,l.jsx)(`h3`,{children:n(`queue.panel.title`)})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeQueuePanel(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":u>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:u}),(0,l.jsx)(`span`,{children:n(`queue.depth`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":d!==void 0&&d>5?`true`:void 0,children:[(0,l.jsxs)(`b`,{children:[d??0,`m`]}),(0,l.jsx)(`span`,{children:n(`queue.wait`)})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-queuepanel-actions`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:u===0||o,"data-armed":i?`true`:void 0,onClick:()=>{f()},children:n(i?`queue.confirm`:`queue.clear`)})})]})]})})}function dt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.diagnostics,i=e=>{let t=n.workspaces.find(t=>t.id===e);return t?.title??t?.path??e.slice(0,8)};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeDiagnostics()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-diag`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`diag.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⚙`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`diag.title`)}),(0,l.jsx)(`p`,{children:t(`diag.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeDiagnostics(),children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:r===void 0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`shared.loading`)}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.revision}),(0,l.jsx)(`span`,{children:t(`diag.revision`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.tasks}),(0,l.jsx)(`span`,{children:t(`diag.tasks`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.staleRunning>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.staleRunning}),(0,l.jsx)(`span`,{children:t(`diag.running`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.orphanWorktrees.length>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.orphanWorktrees.length}),(0,l.jsx)(`span`,{children:t(`diag.orphans`)})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.orphans.heading`)}),r.orphanWorktrees.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.orphanWorktrees.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-diag-orphan`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:n.path,children:[i(n.workspaceId),` · `,n.taskId]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void e.cleanupOrphan(n.workspaceId,n.taskId),children:t(`diag.orphans.cleanup`)})]},n.path))}),(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.hint`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.gitignore.heading`)}),(r.gitIgnoreSuggestions??[]).length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.gitignore.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.gitIgnoreSuggestions.map(e=>(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphan`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:e.workspacePath,children:[i(e.workspaceId),` · `,t(`diag.gitignore.suggestA`),` `,(0,l.jsx)(`code`,{children:`.dsh-worktrees/`}),t(`diag.gitignore.suggestB`)]})},e.workspaceId))})]})]})})]})})}function ft({controller:e,tasks:t}){let n=W(),[r,i]=(0,c.useState)(`none`),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(()=>new Set),d=t.filter(e=>e.trashedAt!==void 0),f=t.filter(e=>e.trashedAt===void 0&&e.status===`archived`),p=t.filter(e=>e.trashedAt===void 0&&e.status===`canceled`),m=e=>{u(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},h=e=>{u(t=>{let n=e.length>0&&e.every(e=>t.has(e.id)),r=new Set(t);for(let t of e)n?r.delete(t.id):r.add(t.id);return r})},g=async t=>{let n=(t===`trashArchived`?f:d).filter(e=>s.has(e.id));o(!0);try{for(let r of n){try{await e.remove(r.id,r.version,t===`purgeTrashed`)}catch{}u(e=>{let t=new Set(e);return t.delete(r.id),t})}}finally{o(!1),i(`none`),u(new Set)}},_=()=>{i(`none`),u(new Set)},v=[{label:n(`status.column.canceled`),dot:`canceled`,rows:p},{label:n(`status.column.archived`),dot:`archived`,rows:f,action:`trashArchived`},{label:n(`board.group.trashed`),dot:`trashed`,rows:d,action:`purgeTrashed`}];return d.length+f.length+p.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-secondary`,children:(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.secondary.empty`)})}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(t=>{let o=r===t.action,c=o?t.rows.filter(e=>s.has(e.id)).length:0,d=t.rows.length>0&&c===t.rows.length;return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":t.dot}),t.label,(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:t.rows.length}),t.action&&t.rows.length>0&&(o?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:n(`board.batch.confirmCancelTip`),onClick:_,children:n(`board.batch.cancel`)}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:a||c===0,title:n(c===0?`board.batch.noneSelected`:`board.batch.confirmTip`),onClick:()=>{g(t.action)},children:[n(`board.batch.confirm`),`(`,c,`)`]}),(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selectall`,checked:d,disabled:a,title:n(`board.batch.selectAll`),onClick:e=>e.stopPropagation(),onChange:()=>h(t.rows)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:t.action===`trashArchived`?n(`board.batch.trashArchivedTip`):n(`board.batch.purgeTrashedTip`),onClick:()=>{i(t.action),u(new Set)},children:t.action===`trashArchived`?n(`board.batch.trashArchived`):n(`board.batch.purgeTrashed`)}))]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[t.rows.map(n=>t.action&&o?(0,l.jsxs)(`div`,{className:`dsh-atb-selrow`,children:[(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selbox`,checked:s.has(n.id),disabled:a,onClick:e=>e.stopPropagation(),onChange:()=>m(n.id)}),(0,l.jsx)(Ee,{task:n,controller:e})]},n.id):(0,l.jsx)(Ee,{task:n,controller:e},n.id)),t.rows.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.empty`)})]})]},t.label)})})}function pt(e){let t=()=>{if((document.body.getAttribute(`data-dsh-desktop-platform`)??new URLSearchParams(window.location.search).get(`dsh-desktop-platform`))!==`win32`){delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`);return}e.dataset.dshAtbWindows=``,e.style.setProperty(`--dsh-atb-viewport-top`,`${e.getBoundingClientRect().top}px`)},n,r=()=>{n===void 0&&(n=requestAnimationFrame(()=>{n=void 0,t()}))},i=new MutationObserver(r);i.observe(document.body,{attributes:!0,childList:!0,subtree:!0,attributeFilter:[`class`,`data-dsh-desktop-platform`,`data-dsh-desktop-mode`,`data-details-collapsed`]}),i.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-dsh-atb-active`]});let a=typeof ResizeObserver>`u`?void 0:new ResizeObserver(r);return a?.observe(e),e.parentElement!==null&&a?.observe(e.parentElement),window.addEventListener(`resize`,r),r(),()=>{i.disconnect(),a?.disconnect(),window.removeEventListener(`resize`,r),n!==void 0&&cancelAnimationFrame(n),delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`)}}function mt(){let e=document.querySelector(`[data-pane="sidebar"], [class*="sidebarCol"], .dshDesktopUpstreamSidebar, .dshDesktopSidebarSurface`);if(e!==null)return e.querySelector(`[class*="logoRow"]`)?.parentElement??e.firstElementChild}function ht(e){let t=e.querySelector(`button[class*="newSession"]`);if(t!==null)return t;for(let t of e.children)if(t instanceof HTMLButtonElement&&!t.matches(`[data-dsh-atb-entry]`))return t;let n=e.querySelector(`button[aria-label="新建会话"], button[aria-label="New Session"], button[aria-label*="新会话"], button[aria-label*="new session" i]`);return n===null?Array.from(e.querySelectorAll(`button`)).find(e=>!e.matches(`[data-dsh-atb-entry]`)&&/新会话|新建会话|new session/i.test(e.textContent??``)):n}function gt(e){let t=document.createElement(`button`);return t.type=`button`,t.dataset.dshAtbEntry=``,t.className=`dsh-atb-entry`,t.setAttribute(`aria-label`,U(`shared.entry.aria`)),t.innerHTML=`<span class="dsh-atb-entry-icon"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M6 2v12M10 2v12"/></svg></span><span class="dsh-atb-entry-label">${U(`shared.entry.label`)}</span><span class="dsh-atb-entry-stats"></span>`,t.addEventListener(`click`,()=>{e.toggleBoard()}),t}function _t(e){let t=0,n=0,r=0;for(let i of e.getSnapshot().ledger.tasks)i.trashedAt===void 0&&(i.status===`todo`?t++:i.status===`in_progress`?n++:i.status===`in_review`&&r++);return[t,n,r]}function vt(e,t){let n=String(t);if(e.dataset.value===n)return;let r=e.dataset.value;if(e.dataset.value=n,e.style.minWidth=`${n.length}ch`,r===void 0){e.textContent=n;return}e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``);let i=document.createElement(`span`);i.className=`dsh-atb-rn`,i.textContent=r;let a=document.createElement(`span`);a.className=`dsh-atb-rn dsh-atb-rn-next`,a.textContent=n,e.replaceChildren(i,a),e.dataset.dir=t>Number(r)?`up`:`down`,e.dataset.busy=`1`,requestAnimationFrame(()=>{e.dataset.anim=`1`});let o=()=>{e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``,e.textContent=e.dataset.value??``)};e.addEventListener(`transitionend`,o,{once:!0}),setTimeout(o,400)}function yt(e,t){let n=e.querySelector(`.dsh-atb-entry-stats`);if(n===null)return()=>{};let r=[`todo`,`in_progress`,`in_review`],i=[];for(let e=0;e<3;e++){if(e>0){let e=document.createElement(`span`);e.className=`dsh-atb-entry-sep`,e.textContent=`|`,n.append(e)}let t=document.createElement(`span`);t.className=`dsh-atb-roll`,t.dataset.stat=r[e],n.append(t),i.push(t)}return()=>{let[e,r,a]=_t(t);vt(i[0],e),vt(i[1],r),vt(i[2],a),n.title=U(`shared.stats.title`,{todo:e,doing:r,review:a})}}function bt(e,t){let n=ht(e);if(n===void 0)return!1;if(t.parentElement!==e){let r=n.closest(`[class*="logoRow"]`),i=r!==null&&r.parentElement===e?r:n,a=Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.matches(`[data-dsh-atb-entry], [data-dsh-taskboard-entry], [data-dsh-ssh-entry]`)),o=a.length>0?a[0]??null:i.nextElementSibling??null;e.insertBefore(t,o)}return!0}function xt(e){let t=gt(e),n={attempts:0,found:!1,placed:!1},r=globalThis.location?.hostname;(r===`localhost`||r===`127.0.0.1`)&&(window.__atbDebug=n);let i,a=!1,o=()=>{if(n.attempts++,i!==void 0&&!i.isConnected&&(c.disconnect(),i=void 0,a=!1),a){if(document.body.contains(t))return;c.disconnect(),i=void 0,a=!1}i??=mt(),i!==void 0&&(n.found=ht(i)!==void 0,a=bt(i,t),n.placed=a,a&&c.observe(i,{childList:!0,subtree:!0}))},s=new MutationObserver(()=>{o()});s.observe(document.body,{childList:!0,subtree:!0});let c=new MutationObserver(()=>{if(i===void 0||!i.isConnected){a=!1,o();return}i.contains(t)||(a=bt(i,t))}),l=setInterval(()=>{o()},2e3),u=yt(t,e),d=()=>{e.getSnapshot().boardOpen?t.dataset.active=`true`:delete t.dataset.active,u()},f=e.subscribe(d),p=H.subscribe(()=>{t.setAttribute(`aria-label`,U(`shared.entry.aria`));let e=t.querySelector(`.dsh-atb-entry-label`);e!==null&&(e.textContent=U(`shared.entry.label`)),u()});return d(),o(),()=>{clearInterval(l),s.disconnect(),c.disconnect(),p(),f(),t.remove()}}const St=`data-dsh-atb-active`,Ct=[`data-dsh-taskboard-active`,`data-dsh-ssh-active`],wt=`dsh-panel-activate`,Tt=`dsh-taskboard`;function Et(){return document.querySelector(`[data-pane="conversation"], [class*="centerCol"], .dshDesktopConversationSurface`)??void 0}function Dt(e){let t,n,r,i=()=>{if(n!==void 0)return;let i=Et();i!==void 0&&(n=document.createElement(`div`),n.dataset.dshAtbView=``,n.className=`dsh-atb-view`,i.appendChild(n),r=pt(n),t=(0,d.createRoot)(n),t.render((0,l.jsx)(lt,{controller:e})))},a=new MutationObserver(()=>{i()});a.observe(document.body,{childList:!0,subtree:!0});let o=()=>{if(e.getSnapshot().boardOpen){for(let e of Ct)document.documentElement.removeAttribute(e);document.documentElement.setAttribute(St,``),document.dispatchEvent(new CustomEvent(wt,{detail:Tt}))}else document.documentElement.removeAttribute(St)},s=t=>{t.detail!==Tt&&e.getSnapshot().boardOpen&&e.closeBoard()},c=t=>{if(!e.getSnapshot().boardOpen)return;let n=t.target;n!==null&&n.closest(`[data-dsh-atb-entry]`)===null&&n.closest(`[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]`)!==null&&e.closeBoard()};document.addEventListener(`click`,c,!0),document.addEventListener(wt,s);let u=e.subscribe(o);return o(),i(),()=>{document.removeEventListener(`click`,c,!0),document.removeEventListener(wt,s),a.disconnect(),u(),r?.(),document.documentElement.removeAttribute(St),t?.unmount(),n?.remove()}}const Ot=`dsh-taskboard`;function kt(e){let t=e.get?.(`slots`)??e.slots;if(!(t===void 0||typeof t.register!=`function`||typeof t.inject!=`function`||typeof t.spec!=`function`)){try{if(t.spec(`main`)===void 0||t.spec(`sidebar.panellist`)===void 0)return}catch{return}return t}}function At(e){return function(){let t=c.useCallback(e=>{if(e===null)return;let t=`__dshAtbInsetDispose`,n=e;n[t]===void 0&&(n[t]=pt(e))},[]);return c.createElement(`div`,{"data-dsh-atb-panel-root":``,ref:t,className:`dsh-atb-panel-root`},c.createElement(lt,{controller:e}))}}function jt(e){return function(t){let n=typeof t.size==`number`?t.size:16,r=c.useCallback(t=>e.subscribe(t),[e]),i=c.useCallback(()=>e.getSnapshot(),[e]),a=c.useSyncExternalStore(r,i,i),o=c.useMemo(()=>{let e=0,t=0,n=0;for(let r of a.ledger.tasks)r.trashedAt===void 0&&(r.status===`todo`?e++:r.status===`in_progress`?t++:r.status===`in_review`&&n++);return{todo:e,doing:t,review:n}},[a.ledger]),[s,l]=c.useState(!0),u=c.useCallback(e=>{if(e===null||typeof ResizeObserver>`u`)return;let t=e.closest(`button`);t!==null&&new ResizeObserver(()=>{l(t.clientWidth>44)}).observe(t)},[]),d=U(`shared.stats.title`,{todo:o.todo,doing:o.doing,review:o.review});return c.createElement(`span`,{className:`dsh-atb-pglyph`,ref:u},c.createElement(`span`,{className:`dsh-atb-picon`},c.createElement(`svg`,{width:n,height:n,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":1.3,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":!0},c.createElement(`rect`,{x:2,y:2,width:12,height:12,rx:2}),c.createElement(`path`,{d:`M6 2v12M10 2v12`})),!s&&o.todo>0?c.createElement(`span`,{className:`dsh-atb-pbadge`,title:d,"aria-hidden":!0},o.todo):null),s&&(o.todo>0||o.doing>0||o.review>0)?c.createElement(`span`,{className:`dsh-atb-pstats`,title:d,"aria-hidden":!0},c.createElement(`span`,{"data-stat":`todo`},o.todo),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_progress`},o.doing),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_review`},o.review)):null)}}function Mt(e,t,n){let r=[];try{let i=t.get?.(`layout`)??t.layout;n.installCloseRequester(()=>{i?.selectPanel(null)}),r.push(()=>{n.installCloseRequester(void 0)});let a=At(n);r.push(e.inject(`main`,()=>e.register({name:`main`,key:Ot},a)));let o=jt(n);r.push(e.inject(`sidebar.panellist`,()=>e.register({name:`sidebar.panellist`,id:Ot,order:10,label:()=>U(`shared.entry.label`)},o))),console.info(`[dsh-taskboard] official panel mode: registered`,Ot)}catch(e){console.error(`[dsh-taskboard] official panel registration failed — falling back to legacy mounting:`,e);for(let e of r.splice(0))e();return}return()=>{for(let e of r.splice(0))e()}}function Nt(e,t){let n=kt(e);if(n!==void 0){let r=Mt(n,e,t);if(r!==void 0)return r}let r=[];r=[xt(t),Dt(t)];let i=0,a,o=setInterval(()=>{i++;let n=kt(e);if(n!==void 0){let i=Mt(n,e,t);if(i!==void 0){a=i,clearInterval(o);for(let e of r.splice(0))e();return}}i>=3&&clearInterval(o)},350);return()=>{clearInterval(o);for(let e of r.splice(0))e();a?.()}}function Pt(e){let t=(e,t,n)=>e.list.getSnapshot().byId[n]===void 0?`absent`:t?.list.getSnapshot().archivedSessionIds.includes(n)??!1?`archived`:`openable`,n=(t,n)=>{let r=e.getUiWorkspace?.();return typeof r?.openSession==`function`?(r.openSession(n),!0):typeof t.open==`function`?(t.open(n),!0):!1};return async r=>{let i=e.getSessions();if(i===void 0)return`unavailable`;try{let a=t(i,e.getWorkspaces(),r);if(a===`absent`){try{await i.refresh()}catch{}a=t(i,e.getWorkspaces(),r)}return a===`archived`?`archived`:a===`absent`?`missing`:n(i,r)?`opened`:`unavailable`}catch{return`unavailable`}}}const Ft=[`connection`];function It(e){try{me(),V(e.get?.(`locale`),()=>e.get?.(`locale`));let t=_(),n=new fe(t);n.installModelCatalog(async()=>{try{let t=e.get?.(`modelDirectories`)??e.modelDirectories;if(t?.catalog?.load!==void 0){let e=await t.catalog.load();if(e?.groups!==void 0&&e.groups.length>0){let t=[];for(let n of e.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`remote`)??e.remote;if(t?.session?.modelCatalog!==void 0){let e=await t.session.modelCatalog();if(e.ok&&e.value?.groups!==void 0&&e.value.groups.length>0){let t=[];for(let n of e.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}if(t?.llm?.models!==void 0){let e=await t.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.llm?.models!==void 0){let e=await t.api.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let e=await t.modelCatalog();if(e.models!==void 0&&e.models.length>0)return e.models}catch{}return[]}),n.installPresetRoster(async()=>{try{let t=e.get?.(`remote`)??e.remote;if(t?.agentPresets?.list!==void 0){let e=await t.agentPresets.list(),n=e.ok===!0?e.value.presets:e.result?.ok===!0?e.result.value.presets:void 0;if(n!==void 0&&n.length>0){let e=n.map(e=>({id:e.id,name:e.name})),t=n.find(e=>e.isDefault);return{presets:e,...t===void 0?{}:{defaultId:t.id}}}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.agentPresets?.list!==void 0){let e=await t.api.agentPresets.list({});if(e.result.ok&&e.result.value?.presets!==void 0&&e.result.value.presets.length>0){let t=e.result.value.presets.map(e=>({id:e.id,name:e.name})),n=e.result.value.presets.find(e=>e.isDefault);return{presets:t,...n===void 0?{}:{defaultId:n.id}}}}}catch{}try{let e=await t.modelCatalog();if(e.presets!==void 0&&e.presets.length>0)return{presets:e.presets,...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{}return{presets:[]}}),n.installSessionJumper(Pt({getSessions:()=>e.get?.(`sessions`),getWorkspaces:()=>e.get?.(`workspaces`),getUiWorkspace:()=>e.get?.(`uiWorkspace`)})),n.installSessionTitles(()=>(e.get?.(`sessions`))?.list.getSnapshot().byId??{}),n.start();let r=[];try{r.push(Nt(e,n))}catch(e){console.error(`[dsh-taskboard] mount failed:`,e)}e.effect?.(()=>()=>{for(let e of r.splice(0))e();n.dispose(),ce()},`dsh-taskboard: client mount`)}catch(e){console.error(`[dsh-taskboard] client half failed to start:`,e)}}exports.apply=It,exports.inject=Ft,exports.name=`dsh-taskboard/client`; +`,t=/\s/.test(e)||r===0,i=n.slice(r+1),a=!/\s/.test(i);if(t&&a){O(r),E(i),w(!0);return}}w(!1)},F=n=>{let r=g.current;if(r===null||D<0)return;let i=r.selectionStart,a=e.slice(0,D),o=e.slice(i),s=`/${n.name} `;t(a+s+o),w(!1),setTimeout(()=>{if(g.current!==null){let e=D+s.length;g.current.focus(),g.current.setSelectionRange(e,e)}},0)},I=e=>{if(C&&N.length>0){if(e.key===`ArrowDown`){e.preventDefault(),A(e=>(e+1)%N.length);return}if(e.key===`ArrowUp`){e.preventDefault(),A(e=>(e-1+N.length)%N.length);return}if(e.key===`Enter`||e.key===`Tab`){let t=N[k];if(t!==void 0){e.preventDefault(),F(t);return}}if(e.key===`Escape`){e.preventDefault(),w(!1);return}}},L=(0,c.useCallback)(()=>{let e=g.current;if(e===null)return;let t=e.getBoundingClientRect(),n=window.innerHeight,r=_.current?.offsetHeight??0,i=r>0?r:240,a=t.top-6-8,o=n-8-(t.bottom+6),s=o>a,c=Math.min(i,Math.max(s?o:a,120)),l=s?t.bottom+6:t.top-6-c;x(e=>e.left===t.left&&e.top===l&&e.width===t.width&&e.maxHeight===c?e:{position:`fixed`,left:t.left,top:l,width:t.width,maxHeight:c,zIndex:100})},[]);return(0,c.useLayoutEffect)(()=>{C&&L()}),(0,c.useEffect)(()=>{if(C)return window.addEventListener(`scroll`,L,!0),window.addEventListener(`resize`,L),()=>{window.removeEventListener(`scroll`,L,!0),window.removeEventListener(`resize`,L)}},[C,L]),(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-wrap ${d??``}`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-prompt-inner`,children:[(0,l.jsx)(`textarea`,{ref:g,className:`dsh-atb-prompt-input`,value:e,rows:i,maxLength:a,disabled:o||j,autoFocus:s,placeholder:r,"aria-label":f,onChange:e=>{t(e.target.value),P()},onKeyUp:P,onClick:P,onKeyDown:I,onPaste:e=>{let t=X(e.clipboardData.files);p&&t.length>0&&(e.preventDefault(),ne(t))},onDragOver:e=>{p&&e.dataTransfer.types.includes(`Files`)&&e.preventDefault()},onDrop:e=>{let t=X(e.dataTransfer.files);p&&t.length>0&&(e.preventDefault(),ne(t))}}),C&&N.length>0&&(0,u.createPortal)((0,l.jsxs)(`div`,{ref:_,className:`dsh-atb-slash-popup`,style:b,role:`listbox`,"aria-label":h(`slash.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-slash-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-title`,children:h(`slash.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-slash-hint`,children:h(`slash.hint`)})]}),(0,l.jsx)(`div`,{ref:v,className:`dsh-atb-slash-list`,children:N.map((e,t)=>(0,l.jsxs)(`div`,{role:`option`,"aria-selected":t===k,className:`dsh-atb-slash-item`,"data-active":t===k?`true`:void 0,"data-kind":e.kind,onClick:()=>F(e),onMouseEnter:()=>A(t),children:[(0,l.jsx)(`span`,{className:`dsh-atb-slash-badge`,"data-kind":e.kind,children:e.kind===`command`?h(`slash.badge.command`):h(`slash.badge.skill`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-slash-name`,children:[`/`,e.name]}),e.hint&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-param`,children:e.hint}),e.description&&(0,l.jsx)(`span`,{className:`dsh-atb-slash-desc`,children:e.description})]},`${e.kind}-${e.name}`))})]}),document.body)]}),p&&(0,l.jsxs)(`div`,{className:`dsh-atb-image-actions`,children:[(0,l.jsx)(`input`,{ref:y,type:`file`,accept:`image/png,image/jpeg,image/gif,image/webp`,multiple:!0,hidden:!0,onChange:e=>{e.target.files!==null&&ne(e.target.files),e.target.value=``}}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-image-add`,disabled:o||j,onClick:()=>y.current?.click(),children:h(j?`image.uploading`:`image.add`)}),(0,l.jsx)(`span`,{className:`dsh-atb-image-hint`,children:h(`image.hint`)})]}),(0,l.jsx)(`div`,{className:`dsh-atb-prompt-foot`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-prompt-tip`,children:[h(`slash.tipA`),` `,(0,l.jsx)(`code`,{children:`/`}),` `,h(`slash.tipB`)]})})]})}const Je=`dsh-taskboard-last-model-v1`;function Ye(){try{let e=localStorage.getItem(Je);if(e===null)return;let t=JSON.parse(e);if(typeof t==`object`&&t){let{provider:e,model:n,reasoningEffort:r}=t;if(typeof e==`string`&&typeof n==`string`&&e.trim().length>0&&n.trim().length>0)return{provider:e.trim(),model:n.trim(),...typeof r==`string`&&r.trim().length>0?{reasoningEffort:r.trim()}:{}}}return}catch{return}}function Xe(e){try{e===void 0?localStorage.removeItem(Je):localStorage.setItem(Je,JSON.stringify(e))}catch{}}const Ze=e=>[{value:`urgent`,label:e(`form.urgency.urgent`)},{value:`normal`,label:e(`form.urgency.normal`)},{value:`relaxed`,label:e(`form.urgency.relaxed`)}],Qe=e=>[{label:e(`form.cron.daily`),cron:`0 9 * * *`},{label:e(`form.cron.hourly`),cron:`0 * * * *`},{label:e(`form.cron.every10min`),cron:`*/10 * * * *`},{label:e(`form.cron.weekly`),cron:`0 9 * * 1`}],$e=e=>[{value:`workspace-write`,label:e(`form.perm.write`),icon:`📁`},{value:`read-only`,label:e(`form.perm.readOnly`),icon:`🔒`},{value:`danger-full-access`,label:e(`form.perm.fullAccess`),icon:`⚡`}];function $({label:e,required:t=!1,full:n=!1,children:r}){return(0,l.jsxs)(`label`,{className:`dsh-atb-field`,"data-span":n?`full`:void 0,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-field-label`,children:[e,t&&(0,l.jsx)(`em`,{className:`dsh-atb-req`,children:`*`})]}),r]})}function et({rows:e,onChange:t,editing:n}){let r=W(),i=(n,r)=>{t(e.map((e,t)=>t===n?{...e,...r}:e))},a=e.filter(e=>e.checked).length;return(0,l.jsxs)(`div`,{className:`dsh-atb-cke`,children:[e.map((a,o)=>(0,l.jsxs)(`div`,{className:`dsh-atb-cke-row`,children:[n&&(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-cke-box`,checked:a.checked,title:r(`form.check.checkedTitle`,{who:a.checkedBy??r(`form.check.notCheckedYet`)}),onChange:e=>i(o,{checked:e.target.checked})}),(0,l.jsx)(`input`,{className:`dsh-atb-cke-text`,value:a.text,maxLength:200,placeholder:r(`form.check.itemPlaceholder`,{n:o+1}),spellCheck:!1,onChange:e=>i(o,{text:e.target.value})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-del`,title:r(`form.check.removeTitle`),onClick:()=>t(e.filter((e,t)=>t!==o)),children:`✕`})]},a.id??`new-${o}`)),e.length<30&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cke-add`,onClick:()=>t([...e,{text:``,checked:!1}]),children:r(`form.check.add`)}),e.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-cke-hint`,children:n?r(`form.check.hintEdit`,{checked:a,total:e.length}):r(`form.check.hintCreate`,{n:e.length})})]})}function tt({controller:e,task:t}){let n=W(),r=e.getSnapshot(),i=r.templatePrefill,a=t!==void 0,[o,s]=(0,c.useState)(t?.title??i?.title??``),[u,d]=(0,c.useState)(t?.description??i?.description??``),[f,p]=(0,c.useState)(t?.prompt??i?.prompt??``),[m,h]=(0,c.useState)(t?.workspaceId??r.filters.workspaceId??r.workspaces[0]?.id??``),[g,_]=(0,c.useState)(t?.urgency??(i?.urgency===`urgent`||i?.urgency===`relaxed`?i.urgency:`normal`)),v=t?.execution??i?.execution,[y,b]=(0,c.useState)(v?.mode===`scheduled`?v.cron===void 0?`once`:`periodic`:`claim`),[x,te]=(0,c.useState)(t?.execution.cron??i?.execution?.cron??`0 9 * * *`),[w,T]=(0,c.useState)(t?.execution.periodicCompletion??i?.execution?.periodicCompletion??`spawn`),[E,D]=(0,c.useState)(t?.execution.reuseSessionId===void 0?t===void 0?i?.execution?.reuseSessionId===void 0?i?.execution?.sessionReuseMode??`fresh`:`selected:${i.execution.reuseSessionId}`:t.execution.sessionReuseMode??`reuse`:`selected:${t.execution.reuseSessionId}`),[k,j]=(0,c.useState)([]),[M,ne]=(0,c.useState)(!1),[N,P]=(0,c.useState)(!1),F=v?.mode===`scheduled`&&v.runAt!==void 0?v.runAt:void 0,[I,L]=(0,c.useState)(()=>{if(F===void 0)return``;let e=new Date(F);if(Number.isNaN(e.getTime()))return``;let t=e=>String(e).padStart(2,`0`);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`}),[R,re]=(0,c.useState)([]),z=a?t.model:i?.model??r.ledger.settings?.defaultModel??Ye(),[ie,ae]=(0,c.useState)(z===void 0?``:JSON.stringify({provider:z.provider,model:z.model})),[B,oe]=(0,c.useState)(z?.reasoningEffort??``),se=a?t.presetId??``:i?.presetId??r.ledger.settings?.defaultPresetId??``,[V,ce]=(0,c.useState)(se),[H,le]=(0,c.useState)([]),[U,ue]=(0,c.useState)(void 0),[G,de]=(0,c.useState)(t?.permission??(i?.permission?S(i.permission):C(r.ledger.settings))),[fe,pe]=(0,c.useState)(t?.isolation??(i?.isolation===`none`?`none`:i?.isolation===`worktree`?`worktree`:ee(r.ledger.settings))),[me,he]=(0,c.useState)(t?.checklist!==void 0&&t.checklist.length>0?t.checklist.map(e=>({...e})):(i?.checklist??[]).map(e=>({text:e,checked:!1}))),ge=(0,c.useRef)(null),[K,q]=(0,c.useState)(!1),[_e,ve]=(0,c.useState)(!1);(0,c.useEffect)(()=>{ge.current?.focus();let t=t=>{t.key===`Escape`&&e.closeForm()};return document.addEventListener(`keydown`,t),()=>document.removeEventListener(`keydown`,t)},[e]),(0,c.useEffect)(()=>{e.fetchModelCatalog().then(re).catch(()=>re([]))},[e]),(0,c.useEffect)(()=>{if(m===``)return;let t=!0;return ne(!0),P(!1),e.fetchProjectSessions(m).then(e=>{t&&j(e)}).catch(()=>{t&&(j([]),P(!0))}).finally(()=>{t&&ne(!1)}),()=>{t=!1}},[e,m]),(0,c.useEffect)(()=>{e.fetchPresetCatalog().then(e=>{le(e.presets),ue(e.defaultId),!a&&se===``&&e.defaultId!==void 0&&ce(e.defaultId)}).catch(()=>le([]))},[e,a,t?.presetId,se]);let ye=y===`periodic`?O(x.trim()):null,be=ye===null?null:A(ye,Date.now()),xe=y===`periodic`&&(ye===null||be===null),Se=y===`once`&&I!==``?new Date(I).getTime():NaN,Ce=y===`once`&&(I===``||Number.isNaN(Se)||Se<=Date.now()),we=o.trim().length>0&&m!==``&&!xe&&!Ce&&(y===`claim`||!E.startsWith(`selected:`)||!M&&k.some(e=>e.id===E.slice(9))),Te=()=>{let e=E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{sessionReuseMode:E===`reuse`?`reuse`:`fresh`};return y===`periodic`?{mode:`scheduled`,cron:x.trim(),periodicCompletion:w,...e}:y===`once`?{mode:`scheduled`,runAt:new Date(I).toISOString(),...E.startsWith(`selected:`)?{reuseSessionId:E.slice(9)}:{}}:{mode:`claim`}},Ee=a&&t.status===`in_progress`,Y=a&&((t.executions?.length??0)>0||t.status===`in_progress`),De=e.gitAvailable(m),Oe=e.repoCount(m),ke=Y||!De,Ae=()=>{if(De)return fe},je=()=>V.trim().length>0?V.trim():void 0,Me=()=>me.map(e=>({...e,text:e.text.trim()})).filter(e=>e.text.length>0),X=ie===``?void 0:JSON.parse(ie),Ne=X===void 0?void 0:R.find(e=>e.provider===X.provider&&e.model===X.model),Z=Ne?.reasoning,Pe=()=>{if(X===void 0)return;let e=B.trim();return{provider:X.provider,model:X.model,...e.length>0?{reasoningEffort:e}:{}}},Fe=()=>{if(!we||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(a?e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null}):e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}})).catch(()=>void 0).finally(()=>q(!1))},Q=()=>{if(!we||Ee||K||_e)return;let n=Pe();a||Xe(n);let r=Ae(),i=je(),s=Me();q(!0),(async()=>{if(a)await e.update(t.id,t.version,{title:o,description:u,prompt:f,urgency:g,workspaceId:m,execution:Te(),model:n??null,...r!==void 0&&!Y?{isolation:r}:{},presetId:i??null,permission:G,checklist:s.length>0?s:null})&&await e.run(t.id);else{let t=await e.create({title:o,workspaceId:m,urgency:g,description:u.length>0?u:void 0,prompt:f.length>0?f:void 0,execution:Te(),model:n??null,...r===void 0?{}:{isolation:r},presetId:i??null,permission:G,...s.length>0?{checklist:s.map(e=>e.text)}:{}});t!==void 0&&await e.run(t)}})().catch(()=>void 0).finally(()=>q(!1))},Ie=we?y===`periodic`&&be!==null?n(`form.hint.nextRun`,{time:J(be)}):y===`once`&&!Number.isNaN(Se)?n(`form.hint.onceAt`,{time:J(Se)}):a?n(`form.hint.saveVersion`,{v:t.version,next:t.version+1}):n(`form.hint.createClaim`):o.trim().length===0?n(`form.hint.needTitle`):n(m===``?`form.hint.needProject`:Ce?`form.hint.runAtBad`:`form.hint.cronBad`);return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-taskform-modal`,"data-mode":a?`edit`:`create`,role:`dialog`,"aria-modal":`true`,"aria-label":n(a?`form.title.edit`:`form.title.create`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:a?`✎`:`✚`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:n(a?`form.title.edit`:`form.title.create`)}),(0,l.jsx)(`p`,{children:n(a?`form.subtitle.edit`:`form.subtitle.create`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeForm(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body dsh-atb-taskform-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-left`,children:[(0,l.jsx)($,{label:n(`form.field.title`),required:!0,full:!0,children:(0,l.jsx)(`input`,{ref:ge,value:o,onChange:e=>s(e.target.value),placeholder:n(`form.field.titlePlaceholder`),maxLength:200})}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.project`),required:!0,children:(0,l.jsx)(`select`,{value:m,onChange:e=>{h(e.target.value),D(`fresh`),j([])},children:r.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))})}),(0,l.jsx)($,{label:n(`form.field.model`),children:(0,l.jsxs)(`select`,{value:ie,onChange:e=>{let t=e.target.value;if(ae(t),t===``)oe(``);else{let e=JSON.parse(t),n=R.find(t=>t.provider===e.provider&&t.model===e.model);n?.reasoning?.defaultEffort===void 0?oe(``):oe(n.reasoning.defaultEffort)}},children:[(0,l.jsx)(`option`,{value:``,children:n(`form.field.modelDefault`)}),X!==void 0&&Ne===void 0&&(0,l.jsxs)(`option`,{value:ie,children:[X.model,` (`,X.provider,`)`]}),R.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:n(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})}),X!==void 0&&(0,l.jsx)($,{label:n(`form.field.effort`),children:(0,l.jsxs)(`select`,{value:B,onChange:e=>oe(e.target.value),title:n(`form.field.effortTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.effort.follow`),Z?.defaultEffort===void 0?``:n(`shared.current`,{name:Z.efforts.find(e=>e.id===Z.defaultEffort)?.name??Z.defaultEffort})]}),B!==``&&!(Z!==void 0&&Z.efforts.length>0?Z.efforts.some(e=>e.id===B):[`low`,`medium`,`high`,`none`].includes(B))&&(0,l.jsx)(`option`,{value:B,children:B}),Z!==void 0&&Z.efforts.length>0?Z.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id)):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`option`,{value:`low`,children:n(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:n(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:n(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:n(`form.effort.none`)})]})]})}),(H.length>0||V!==``)&&(0,l.jsx)($,{label:n(`form.field.preset`),children:(0,l.jsxs)(`select`,{value:V,onChange:e=>ce(e.target.value),title:n(`form.field.presetTitle`),children:[(0,l.jsxs)(`option`,{value:``,children:[n(`form.preset.follow`),U===void 0?``:n(`shared.current`,{name:H.find(e=>e.id===U)?.name??U})]}),V!==``&&!H.some(e=>e.id===V)&&(0,l.jsx)(`option`,{value:V,children:V}),H.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===U?n(`form.preset.defaultTag`):``]},e.id))]})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsx)($,{label:n(`form.field.permission`),children:(0,l.jsx)(`select`,{className:`dsh-atb-permission-select`,value:G,onChange:e=>de(S(e.target.value)),children:$e(n).map(e=>(0,l.jsxs)(`option`,{value:e.value,children:[e.icon,` `,e.label,e.value===`workspace-write`?n(`form.perm.defaultTag`):``]},e.value))})}),(0,l.jsx)($,{label:n(`form.field.urgency`),children:(0,l.jsx)(`select`,{className:`dsh-atb-urgency-select`,value:g,onChange:e=>_(e.target.value),children:Ze(n).map(e=>(0,l.jsx)(`option`,{value:e.value,children:e.label},e.value))})})]}),(0,l.jsx)($,{label:n(`form.field.mode`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-exec":`true`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`claim`,onClick:()=>b(`claim`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.claim`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.claimHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`once`,onClick:()=>b(`once`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.once`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.onceHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":y===`periodic`,onClick:()=>b(`periodic`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.mode.periodic`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.mode.periodicHint`)})]})]})}),y===`once`&&(0,l.jsx)($,{label:n(`form.field.runAt`),required:!0,full:!0,children:(0,l.jsx)(`input`,{type:`datetime-local`,className:Ce?`dsh-atb-input-bad`:void 0,value:I,onChange:e=>L(e.target.value),spellCheck:!1})}),y===`periodic`&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)($,{label:n(`form.field.cron`),required:!0,full:!0,children:[(0,l.jsx)(`input`,{className:xe?`dsh-atb-input-bad`:void 0,value:x,onChange:e=>te(e.target.value),placeholder:n(`form.cron.placeholder`),spellCheck:!1}),(0,l.jsxs)(`span`,{className:`dsh-atb-cron-presets`,children:[Qe(n).map(e=>(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-cron-preset`,"data-on":x.trim()===e.cron,onClick:()=>te(e.cron),children:e.label},e.cron)),!xe&&be!==null&&(0,l.jsx)(`span`,{className:`dsh-atb-cron-next`,children:n(`form.cron.next`,{time:J(be)})})]})]}),(0,l.jsx)($,{label:n(`form.field.periodicCompletion`),full:!0,children:(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`rearm`,onClick:()=>T(`rearm`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.rearm`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.rearmHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":w===`spawn`,onClick:()=>T(`spawn`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.periodicCompletion.spawn`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(`form.periodicCompletion.spawnHint`)})]})]})})]}),y===`periodic`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E,onChange:e=>D(e.target.value),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.fresh`)}),(0,l.jsx)(`option`,{value:`reuse`,children:n(`form.session.reuse`)}),k.map(e=>(0,l.jsx)(`option`,{value:`selected:${e.id}`,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),y===`once`&&(0,l.jsxs)($,{label:n(`form.field.reuseSession`),full:!0,children:[(0,l.jsxs)(`select`,{value:E.startsWith(`selected:`)?E:`fresh`,onChange:e=>D(e.target.value===`fresh`?`fresh`:`selected:${e.target.value}`),children:[(0,l.jsx)(`option`,{value:`fresh`,children:n(`form.session.new`)}),k.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title??e.id},e.id))]}),M&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`shared.loading`)}),N&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.unavailable`)}),!M&&!N&&E.startsWith(`selected:`)&&!k.some(e=>e.id===E.slice(9))&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.session.missing`)})]}),(0,l.jsxs)($,{label:n(`form.field.isolation`),full:!0,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,"data-disabled":ke?`true`:void 0,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`worktree`,disabled:ke,title:n(Y?`form.iso.locked`:De?`form.iso.worktreeTitle`:`form.iso.nonGit`),onClick:()=>pe(`worktree`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.worktree`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.worktreeHint`:`form.iso.nonGit`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":fe===`none`,disabled:ke,title:n(Y?`form.iso.locked`:`form.iso.noneTitle`),onClick:()=>pe(`none`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:n(`form.iso.none`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:n(Y?`form.iso.lockedShort`:De?`form.iso.noneHint`:`form.iso.noneHintNonGit`)})]})]}),!De&&!Y&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:n(`form.iso.nonGitNote`)}),De&&!Y&&Oe>1&&(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,"data-mirror":`true`,children:n(`form.iso.mirrorNote`,{n:Oe})})]}),(0,l.jsx)($,{label:n(a?`form.field.checklist`:`form.field.checklistOptional`),full:!0,children:(0,l.jsx)(et,{rows:me,onChange:he,editing:a})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-col dsh-atb-form-right`,children:[(0,l.jsx)($,{label:n(a?`form.field.description`:`form.field.descriptionOptional`),full:!0,children:(0,l.jsx)(qe,{value:u,onChange:d,controller:e,rows:7,placeholder:n(`form.desc.placeholder`),allowImages:!0,onUploadingChange:ve})}),(0,l.jsx)($,{label:n(a?`form.field.prompt`:`form.field.promptOptional`),full:!0,children:(0,l.jsx)(qe,{value:f,onChange:p,controller:e,rows:7,placeholder:n(`form.prompt.placeholder`)})})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,"data-tone":we?void 0:`bad`,children:Ie}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeForm(),children:n(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:!we||Ee||K||_e,title:n(Ee?`form.action.runBlockedTitle`:K||_e?`form.action.runBusyTitle`:`form.action.runTitle`),onClick:Q,children:n(`form.action.run`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!we||K||_e,onClick:Fe,children:n(a?`form.action.save`:`form.action.create`)})]})]})]})})}const nt=e=>[{value:`none`,name:e(`form.iso.none`),hint:e(`set.iso.noneHint`)},{value:`worktree`,name:e(`form.iso.worktree`),hint:e(`set.iso.worktreeHint`)}];function rt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.ledger.settings?.defaultIsolation??`none`,i=te(n.ledger.settings),a=C(n.ledger.settings),o=w(n.ledger.settings),s=T(n.ledger.settings),u=E(n.ledger.settings),d=D(n.ledger.settings),f=n.ledger.settings?.defaultModel,p=f===void 0?``:JSON.stringify({provider:f.provider,model:f.model}),m=f?.reasoningEffort??``,h=n.ledger.settings?.defaultPresetId??``,[g,_]=(0,c.useState)([]),[v,y]=(0,c.useState)([]),[b,x]=(0,c.useState)(),[S,ee]=(0,c.useState)(p),[O,k]=(0,c.useState)(m),[A,j]=(0,c.useState)(h),M=S===``?void 0:JSON.parse(S),ne=g.find(e=>e.provider===M?.provider&&e.model===M?.model),N=ne?.reasoning,[P,F]=(0,c.useState)(r),[I,L]=(0,c.useState)(i),[R,re]=(0,c.useState)(a),[z,ie]=(0,c.useState)(String(o)),[ae,B]=(0,c.useState)(String(s)),[oe,se]=(0,c.useState)(String(u)),[V,ce]=(0,c.useState)(String(d)),[H,le]=(0,c.useState)(n.storage?.currentDirectory??``),[U,ue]=(0,c.useState)(!1),[G,de]=(0,c.useState)(!1),fe=Number(z),pe=Number(ae),me=Number(oe),he=Number(V),ge=Number.isSafeInteger(fe)&&fe>=1&&fe<=100&&Number.isSafeInteger(pe)&&pe>=1&&pe<=1440&&Number.isSafeInteger(me)&&me>=0&&me<=10080&&Number.isSafeInteger(he)&&he>=0&&he<=6e4,K=S!==p||O!==m||A!==h||P!==r||I!==i||R!==a||z!==String(o)||ae!==String(s)||oe!==String(u)||V!==String(d),q=H.trim().length===0?n.storage?.defaultDirectory??``:H.trim(),_e=n.storage!==void 0&&q!==n.storage.currentDirectory;return(0,c.useEffect)(()=>{!U&&n.storage!==void 0&&le(n.storage.currentDirectory)},[n.storage,U]),(0,c.useEffect)(()=>{let t=!0;return e.fetchModelCatalog().then(e=>{t&&_(e)}).catch(()=>{}),e.fetchPresetCatalog().then(e=>{t&&(y(e.presets),x(e.defaultId))}).catch(()=>{}),()=>{t=!1}},[e]),(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeSettings()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-set`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`set.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`🛠`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`set.title`)}),(0,l.jsx)(`p`,{children:t(`set.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeSettings(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.iso.heading`)}),(0,l.jsx)(`div`,{className:`dsh-atb-mode-picker`,children:nt(t).map(e=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":P===e.value,title:e.hint,onClick:()=>F(e.value),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:e.name}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:e.hint})]},e.value))}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.iso.current`,{current:t(r===`worktree`?`form.iso.worktree`:`form.iso.none`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.sync.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":!I,title:t(`set.sync.off.title`),onClick:()=>L(!1),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.off.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.off.hint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":I,title:t(`set.sync.on.title`),onClick:()=>L(!0),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`set.sync.on.name`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`set.sync.on.hint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(i?`set.sync.stateOn`:`set.sync.stateOff`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.perm.heading`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-perm-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`workspace-write`,onClick:()=>re(`workspace-write`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.writeName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.writeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`read-only`,onClick:()=>re(`read-only`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.readOnlyName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.readOnlyHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-perm-opt`,"data-on":R===`danger-full-access`,onClick:()=>re(`danger-full-access`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-perm-name`,children:t(`set.perm.fullName`)}),(0,l.jsx)(`span`,{className:`dsh-atb-perm-hint`,children:t(`set.perm.fullHint`)})]})]}),(0,l.jsx)(`span`,{className:`dsh-atb-isolation-note`,children:t(`set.perm.current`,{current:t(a===`read-only`?`set.perm.readOnlyName`:a===`danger-full-access`?`set.perm.fullName`:`set.perm.writeName`)})})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.agent.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.agent.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-form-subgrid`,children:[(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.model`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:S,onChange:e=>{ee(e.target.value),k(``)},children:[(0,l.jsx)(`option`,{value:``,children:t(`form.field.modelDefault`)}),M!==void 0&&ne===void 0&&(0,l.jsxs)(`option`,{value:S,children:[M.model,` (`,M.provider,`)`]}),g.map(e=>(0,l.jsx)(`option`,{value:JSON.stringify({provider:e.provider,model:e.model}),children:t(`form.model.option`,{name:e.name??e.model,provider:e.provider})},`${e.provider}/${e.model}`))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.effort`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:O,disabled:M===void 0,onChange:e=>k(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.effort.follow`),N?.defaultEffort===void 0?``:t(`shared.current`,{name:N.efforts.find(e=>e.id===N.defaultEffort)?.name??N.defaultEffort})]}),O!==``&&N!==void 0&&!N.efforts.some(e=>e.id===O)&&(0,l.jsx)(`option`,{value:O,children:O}),N===void 0?(0,l.jsxs)(l.Fragment,{children:[![``,`low`,`medium`,`high`,`none`].includes(O)&&(0,l.jsx)(`option`,{value:O,children:O}),(0,l.jsx)(`option`,{value:`low`,children:t(`form.effort.low`)}),(0,l.jsx)(`option`,{value:`medium`,children:t(`form.effort.medium`)}),(0,l.jsx)(`option`,{value:`high`,children:t(`form.effort.high`)}),(0,l.jsx)(`option`,{value:`none`,children:t(`form.effort.none`)})]}):N.efforts.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name,e.description?` (${e.description})`:``]},e.id))]})]}),(0,l.jsxs)(`label`,{className:`dsh-atb-field`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-field-label`,children:t(`set.agent.preset`)}),(0,l.jsxs)(`select`,{className:`dsh-atb-input`,value:A,onChange:e=>j(e.target.value),children:[(0,l.jsxs)(`option`,{value:``,children:[t(`form.preset.follow`),b===void 0?``:t(`shared.current`,{name:v.find(e=>e.id===b)?.name??b})]}),A!==``&&!v.some(e=>e.id===A)&&(0,l.jsx)(`option`,{value:A,children:A}),v.map(e=>(0,l.jsxs)(`option`,{value:e.id,children:[e.name??e.id,e.id===b?t(`form.preset.defaultTag`):``]},e.id))]})]})]})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.schedule.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.schedule.hint`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-settings-numbers`,children:[(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.concurrent`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`100`,step:`1`,value:z,onChange:e=>ie(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.missedAfter`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`1`,max:`1440`,step:`1`,value:ae,onChange:e=>B(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.queueMaxAge`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`10080`,step:`1`,value:oe,onChange:e=>se(e.target.value)})]}),(0,l.jsxs)(`label`,{children:[(0,l.jsx)(`span`,{children:t(`set.schedule.dispatchInterval`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input`,type:`number`,min:`0`,max:`60000`,step:`1`,value:V,onChange:e=>ce(e.target.value)})]})]}),!ge&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:t(`set.schedule.invalid`)})]}),(0,l.jsxs)(`section`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`set.storage.heading`)}),(0,l.jsx)(`p`,{className:`dsh-atb-isolation-note`,children:t(`set.storage.hint`)}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-storage-path`,value:H,disabled:n.storage===void 0||G,placeholder:n.storage?.defaultDirectory??t(`set.storage.loading`),onChange:t=>{le(t.target.value),ue(!0),e.dismissStorageNotice()}}),n.storageNotice!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-notice`,role:`status`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-storage-notice-ok`,children:[`✓ `,t(`set.storage.migrated`,{path:n.storageNotice.path})]}),n.storageNotice.warnings.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-notice-warn`,children:t(`set.storage.warnings`,{warnings:n.storageNotice.warnings.join(`; `)})})]}),n.storage!==void 0&&(0,l.jsxs)(`div`,{className:`dsh-atb-storage-meta`,children:[(0,l.jsx)(`span`,{children:t(`set.storage.current`,{path:n.storage.currentDirectory})}),(0,l.jsx)(`span`,{children:t(`set.storage.assets`,{count:n.storage.assetCount,size:(n.storage.assetBytes/1024/1024).toFixed(1)})}),n.storage.error!==void 0&&(0,l.jsx)(`span`,{className:`dsh-atb-storage-error`,children:n.storage.error})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-storage-actions`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G,onClick:()=>{le(n.storage?.defaultDirectory??``),ue(!0)},children:t(`set.storage.default`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:n.storage===void 0||G||q.length===0,onClick:()=>{de(!0),e.checkStorage(q).finally(()=>de(!1))},children:t(`set.storage.check`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!_e||G,onClick:()=>{window.confirm(t(`set.storage.confirm`,{from:n.storage?.currentDirectory??``,to:q}))&&(de(!0),e.migrateStorage(q).then(e=>{e&&(ue(!1),le(q))}).finally(()=>de(!1)))},children:t(G?`set.storage.migrating`:`set.storage.migrate`)})]})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(K?`set.foot.dirty`:`set.foot.clean`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.closeSettings(),children:t(`shared.cancel`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,disabled:!K||!ge,onClick:()=>{e.updateSettings({defaultIsolation:P,syncExternalSessions:I,defaultPermission:R,...M===void 0?{}:{defaultModel:{...M,...O===``?{}:{reasoningEffort:O}}},...A===``?{}:{defaultPresetId:A},maxConcurrent:fe,scheduleMissedAfterMinutes:pe,queueMaxAgeMinutes:me,dispatchIntervalMs:he}).then(t=>{t&&e.closeSettings()})},children:t(`set.action.save`)})]})]})]})})}function it({row:e}){return(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,title:e.id,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.title}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.status})]})}function at({controller:e}){let t=W(),[n,r]=(0,c.useState)(``),[i,a]=(0,c.useState)(null),[o,s]=(0,c.useState)(void 0),[u,d]=(0,c.useState)(void 0),[f,p]=(0,c.useState)(`merge`),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(void 0),[v,y]=(0,c.useState)(!1),b=(0,c.useRef)(null),{alert:x,el:S}=Y(),ee=n=>{d(void 0),s(void 0),_(void 0),y(!1),r(``),a(null),n!==void 0&&n.text().then(i=>{try{let t=JSON.parse(i);a(t),r(n.name),e.importPreview(t).then(e=>{e!==void 0&&d(e)})}catch{s(t(`imp.parseError`))}})},te=()=>{if(!(i===null||u===void 0||m)){if(f===`replace`&&!v){y(!0);return}h(!0),e.importCommit(f,i).then(e=>{h(!1),y(!1),e!==void 0&&_(e.mode===`replace`?t(`imp.result.replace`,{n:e.created+e.overwritten,total:e.replacedTotal??0}):t(`imp.result.merge`,{n:e.created,m:e.overwritten}))})}},C=()=>e.closeImport();return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&C()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-imp`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`imp.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⬆`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`imp.title`)}),(0,l.jsx)(`p`,{children:t(`imp.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:C,children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-picker`,children:[(0,l.jsx)(`input`,{ref:b,type:`file`,accept:`.json,application/json`,onChange:e=>ee(e.target.files?.[0])}),n.length>0&&(0,l.jsx)(`span`,{className:`dsh-atb-imp-filename`,children:n})]}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-note`,children:t(`imp.note`)}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-error`,children:o}),u===void 0&&o===void 0&&n.length>0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`imp.previewing`)}),u!==void 0&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stats`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`ok`,children:[(0,l.jsx)(`b`,{children:u.create.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.create`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":`warn`,children:[(0,l.jsx)(`b`,{children:u.overwrite.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.overwrite`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-imp-stat`,"data-tone":u.invalid.length>0?`bad`:void 0,children:[(0,l.jsx)(`b`,{children:u.invalid.length}),(0,l.jsx)(`span`,{children:t(`imp.stat.invalid`)})]})]}),u.create.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.create`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.create.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.overwrite.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.overwrite`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.overwrite.map(e=>(0,l.jsx)(it,{row:e},e.id))})]}),u.invalid.length>0&&(0,l.jsxs)(`div`,{className:`dsh-atb-imp-sec`,children:[(0,l.jsx)(`h4`,{children:t(`imp.sec.invalid`)}),(0,l.jsx)(`div`,{className:`dsh-atb-imp-list`,children:u.invalid.map((e,n)=>(0,l.jsxs)(`div`,{className:`dsh-atb-imp-row`,"data-tone":`bad`,title:e.id??``,children:[(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-title`,children:e.id??t(`imp.noId`)}),(0,l.jsx)(`span`,{className:`dsh-atb-imp-row-status`,children:e.reason})]},e.id??`invalid-${n}`))})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-mode-picker`,children:[(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`merge`,onClick:()=>{p(`merge`),y(!1)},children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.merge`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.mergeHint`)})]}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-mode-opt`,"data-on":f===`replace`,onClick:()=>p(`replace`),children:[(0,l.jsx)(`span`,{className:`dsh-atb-mode-name`,children:t(`imp.mode.replace`)}),(0,l.jsx)(`span`,{className:`dsh-atb-mode-hint`,children:t(`imp.mode.replaceHint`)})]})]}),g!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-imp-result`,children:g})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(f===`replace`?v?`imp.foot.replaceConfirm`:`imp.foot.replaceNeedConfirm`:`imp.foot.mergeHint`)}),(0,l.jsxs)(`span`,{className:`dsh-atb-modal-footbtns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:C,children:t(g===void 0?`shared.cancel`:`shared.close`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,"data-danger":f===`replace`&&v?`true`:void 0,disabled:u===void 0||m,onClick:te,children:t(f===`replace`&&v?`imp.action.confirmReplace`:`imp.action.run`)})]})]})]}),S]})}function ot({controller:e}){let t=W(),n=e.getSnapshot(),[r,i]=(0,c.useState)({}),[a,o]=(0,c.useState)(void 0),{alert:s,el:u}=Y(),d=()=>e.closeTemplateManager(),f=e=>r[e.id]??xe(e),p=e=>{if(e===void 0)return``;let n=q[e];return n===void 0?` · ${e}`:` · ${t(n)}`},m=(r,a)=>{let o=n.templates.find(e=>e.id===r);o===void 0||a===xe(o)||e.upsertTemplate({id:r,name:a,task:o.task}).then(e=>{e&&(i(e=>{let t={...e};return delete t[r],t}),s(t(`tpl.renamed`)))})};return(0,l.jsxs)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:e=>{e.target===e.currentTarget&&d()},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-tplm`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`tpl.aria`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⌗`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`tpl.title`)}),(0,l.jsx)(`p`,{children:t(`tpl.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:d,children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:n.templates.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`tpl.empty`)}):(0,l.jsx)(`div`,{className:`dsh-atb-tplm-list`,children:n.templates.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-tplm-row`,children:[(0,l.jsx)(`input`,{className:`dsh-atb-tplm-name`,value:f(n),maxLength:60,spellCheck:!1,"aria-label":t(`tpl.name.aria`,{name:xe(n)}),onChange:e=>i(t=>({...t,[n.id]:e.target.value})),onKeyDown:e=>{e.key===`Enter`&&m(n.id,f(n))}}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-meta`,title:`${n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`)}${n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``}${p(n.task.urgency)}${n.task.permission===void 0?``:` · ${t(`shared.permission`)}: ${n.task.permission}`}`,children:[n.builtin===!0?t(`tpl.builtin`):t(`tpl.custom`),n.task.checklist!==void 0&&n.task.checklist.length>0?t(`tpl.meta.checklist`,{n:n.task.checklist.length}):``,p(n.task.urgency),n.task.permission!==void 0&&n.task.permission!==`workspace-write`?` · ${n.task.permission===`read-only`?t(`tpl.meta.permReadOnly`):t(`tpl.meta.permFull`)}`:``]}),(0,l.jsxs)(`span`,{className:`dsh-atb-tplm-btns`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:f(n)===xe(n)||f(n).trim().length===0,title:t(`tpl.rename.title`),onClick:()=>m(n.id,f(n)),children:t(`tpl.rename.button`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`tpl.use.title`),onClick:()=>{d(),e.newFromTemplate(Se(n))},children:t(`tpl.use.button`)}),a===n.id?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>{e.deleteTemplate(n.id),o(void 0)},children:t(`shared.confirmDelete`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>o(void 0),children:t(`shared.cancel`)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,title:t(`tpl.delete.title`),onClick:()=>o(n.id),children:`🗑`})]})]},n.id))})}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-foot`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-hint`,children:t(`tpl.foot.hint`)}),(0,l.jsx)(`span`,{className:`dsh-atb-modal-footbtns`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:d,children:t(`shared.close`)})})]})]}),u]})}const st={urgent:0,normal:1,relaxed:2};function ct(e,t){let n=e.search.trim().toLowerCase(),r=t.filter(t=>(e.filters.workspaceId===void 0||t.workspaceId===e.filters.workspaceId)&&(e.filters.urgencies.length===0||e.filters.urgencies.includes(t.urgency))&&(n.length===0||t.title.toLowerCase().includes(n)||t.id.toLowerCase().includes(n)));if(e.sortBy==="default")return r;let i=[...r];return e.sortBy===`updated`?i.sort((e,t)=>t.updatedAt-e.updatedAt):e.sortBy===`created`?i.sort((e,t)=>t.createdAt-e.createdAt):e.sortBy===`urgency`?i.sort((e,t)=>st[e.urgency]-st[t.urgency]||t.updatedAt-e.updatedAt):e.sortBy===`title`&&i.sort((e,t)=>e.title.localeCompare(t.title,void 0,{numeric:!0})),i}function lt({controller:e}){let t=W(),n=(0,c.useSyncExternalStore)(t=>e.subscribe(t),()=>e.getSnapshot()),[r,i]=(0,c.useState)(()=>Date.now());(0,c.useEffect)(()=>{let e=setInterval(()=>i(Date.now()),6e4);return()=>clearInterval(e)},[]);let a=ct(n,n.ledger.tasks.filter(e=>e.trashedAt===void 0)),o=n.selectedId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.selectedId),{alert:s,el:u}=Y(),[d,f]=(0,c.useState)(!1),p=()=>f(!1),[m,h]=(0,c.useState)(!1),g=()=>h(!1);return(0,l.jsxs)(`div`,{className:`dsh-atb-board`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-toolbar`,children:[(0,l.jsx)(`h2`,{className:`dsh-atb-title`,children:t(`board.title`)}),(0,l.jsx)(`span`,{className:`dsh-atb-count`,children:t(`board.count.tasks`,{n:a.length,rev:n.ledger.revision})}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-primary":`true`,onClick:()=>{let t=!d;f(t),t&&e.prepareTemplateMenu()},children:t(`board.action.newTask`)}),d&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:p}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.setComposer(!0)},children:t(`board.action.blankTask`)}),n.templates.map(t=>{let n=xe(t),r=Se(t);return(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:r.description!==void 0&&r.description.length>0?r.description.slice(0,120):n,onClick:()=>{p(),e.newFromTemplate(r)},children:n},t.id)}),(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-sep`}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,onClick:()=>{p(),e.openTemplateManager()},children:t(`board.action.manageTemplates`)})]})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-spacer`}),(0,l.jsx)(`input`,{className:`dsh-atb-input dsh-atb-search`,value:n.search,placeholder:t(`board.search.placeholder`),spellCheck:!1,onChange:t=>e.setSearch(t.target.value)}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.filters.workspaceId??``,onChange:t=>e.setWorkspaceFilter(t.target.value===``?void 0:t.target.value),children:[(0,l.jsx)(`option`,{value:``,children:t(`board.filter.allProjects`)}),n.workspaces.map(e=>(0,l.jsx)(`option`,{value:e.id,children:e.title||e.path},e.id))]}),(0,l.jsxs)(`select`,{className:`dsh-atb-select`,value:n.sortBy,title:t(`board.sort.title`),onChange:t=>e.setSortBy(t.target.value),children:[(0,l.jsx)(`option`,{value:`default`,children:t(`board.sort.default`)}),(0,l.jsx)(`option`,{value:`updated`,children:t(`board.sort.updated`)}),(0,l.jsx)(`option`,{value:`urgency`,children:t(`board.sort.urgency`)}),(0,l.jsx)(`option`,{value:`created`,children:t(`board.sort.created`)}),(0,l.jsx)(`option`,{value:`title`,children:t(`board.sort.byTitle`)})]}),[`urgent`,`normal`,`relaxed`].map(r=>(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-chip`,"data-urgency":r,"data-on":n.filters.urgencies.includes(r),onClick:()=>e.toggleUrgency(r),children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-urgency":r}),t(q[r])]},r)),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,onClick:()=>e.toggleSecondary(),children:n.secondaryOpen?t(`board.action.backToBoard`):t(`board.action.otherTasks`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.settingsTitle`),onClick:()=>e.openSettings(),children:t(`board.action.settings`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.diagTitle`),onClick:()=>e.openDiagnostics(),children:t(`board.action.diag`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.importTitle`),onClick:()=>e.openImport(),children:t(`board.action.import`)}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,title:t(`board.action.exportTitle`),onClick:()=>h(!m),children:t(`board.action.export`)}),m&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`dsh-atb-newmenu-backdrop`,onClick:g}),(0,l.jsxs)(`div`,{className:`dsh-atb-newmenu-list`,children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.jsonTitle`),onClick:()=>{g(),e.exportJson()},children:t(`board.export.json`)}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-newmenu-opt`,title:t(`board.export.csvTitle`),onClick:()=>{g(),e.exportCsv()},children:t(`board.export.csv`)})]})]})]}),(0,l.jsxs)(`a`,{className:`dsh-atb-ver`,href:`https://github.com/cloader/dsh-taskboard`,target:`_blank`,rel:`noopener noreferrer`,children:[`V`,`0.8.6`]})]}),n.error!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-error`,children:n.error}),n.secondaryOpen?(0,l.jsx)(ft,{controller:e,tasks:ct(n,n.ledger.tasks)}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(i=>{let o=a.filter(e=>e.status===i);return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,onDragOver:e=>{e.dataTransfer.types.includes(`application/x-dsh-atb-task`)&&(e.preventDefault(),e.dataTransfer.dropEffect=`move`,e.currentTarget.dataset.dragover=`true`)},onDragLeave:e=>{delete e.currentTarget.dataset.dragover},onDrop:r=>{r.preventDefault(),delete r.currentTarget.dataset.dragover;let a=r.dataTransfer.getData(we);if(a.length===0)return;let o=n.ledger.tasks.find(e=>e.id===a);if(!(o===void 0||o.status===i)){if(!x(o.status,i)){s(t(`board.drag.forbidden`,{from:t(he[o.status]),to:t(he[i])}));return}e.move(a,o.version,i)}},children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":i}),t(he[i]),(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:o.length}),i===`in_progress`&&(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-queuechip`,title:t(`queue.panel.title`),"data-bad":(n.queue?.depth??0)>0?`true`:void 0,onClick:()=>e.openQueuePanel(),children:t(`board.queue.chip`,{n:n.queue?.depth??0})})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[o.map(t=>(0,l.jsx)(Ee,{task:t,controller:e,draggable:!0,now:r,onAlert:s},t.id)),o.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:t(`board.empty`)})]})]},i)})}),o!==void 0&&(0,l.jsx)(`div`,{className:`dsh-atb-detailpanel`,children:(0,l.jsx)(We,{task:o,controller:e,now:r},o.id)}),n.composerOpen&&(0,l.jsx)(tt,{controller:e,task:n.editingId===void 0?void 0:n.ledger.tasks.find(e=>e.id===n.editingId)}),n.diagOpen&&(0,l.jsx)(dt,{controller:e}),n.queueOpen&&(0,l.jsx)(ut,{controller:e,onAlert:s}),n.settingsOpen&&(0,l.jsx)(rt,{controller:e}),n.importOpen&&(0,l.jsx)(at,{controller:e}),n.tplManagerOpen&&(0,l.jsx)(ot,{controller:e}),u]})}function ut({controller:e,onAlert:t}){let n=W(),r=e.getSnapshot().queue,[i,a]=(0,c.useState)(!1),[o,s]=(0,c.useState)(!1),u=r?.depth??0,d=r?.oldestWaitMinutes,f=async()=>{if(!i){a(!0);return}s(!0);let r=await e.clearQueue();s(!1),a(!1),r!==void 0&&(t(n(`queue.cleared`,{n:r.cleared})),e.closeQueuePanel())};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeQueuePanel()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-queuepanel`,role:`dialog`,"aria-modal":`true`,"aria-label":n(`queue.panel.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`📋`}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-headtext`,children:(0,l.jsx)(`h3`,{children:n(`queue.panel.title`)})}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":n(`shared.close`),onClick:()=>e.closeQueuePanel(),children:`✕`})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-body`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":u>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:u}),(0,l.jsx)(`span`,{children:n(`queue.depth`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":d!==void 0&&d>5?`true`:void 0,children:[(0,l.jsxs)(`b`,{children:[d??0,`m`]}),(0,l.jsx)(`span`,{children:n(`queue.wait`)})]})]}),(0,l.jsx)(`div`,{className:`dsh-atb-queuepanel-actions`,children:(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:u===0||o,"data-armed":i?`true`:void 0,onClick:()=>{f()},children:n(i?`queue.confirm`:`queue.clear`)})})]})]})})}function dt({controller:e}){let t=W(),n=e.getSnapshot(),r=n.diagnostics,i=e=>{let t=n.workspaces.find(t=>t.id===e);return t?.title??t?.path??e.slice(0,8)};return(0,l.jsx)(`div`,{className:`dsh-atb-modal-backdrop`,onClick:t=>{t.target===t.currentTarget&&e.closeDiagnostics()},children:(0,l.jsxs)(`div`,{className:`dsh-atb-modal dsh-atb-diag`,role:`dialog`,"aria-modal":`true`,"aria-label":t(`diag.title`),children:[(0,l.jsxs)(`div`,{className:`dsh-atb-modal-head`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-modal-headicon`,children:`⚙`}),(0,l.jsxs)(`div`,{className:`dsh-atb-modal-headtext`,children:[(0,l.jsx)(`h3`,{children:t(`diag.title`)}),(0,l.jsx)(`p`,{children:t(`diag.subtitle`)})]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-modal-close`,"aria-label":t(`shared.close`),onClick:()=>e.closeDiagnostics(),children:`✕`})]}),(0,l.jsx)(`div`,{className:`dsh-atb-modal-body`,children:r===void 0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`shared.loading`)}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-grid`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.revision}),(0,l.jsx)(`span`,{children:t(`diag.revision`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,children:[(0,l.jsx)(`b`,{children:r.tasks}),(0,l.jsx)(`span`,{children:t(`diag.tasks`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.staleRunning>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.staleRunning}),(0,l.jsx)(`span`,{children:t(`diag.running`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-item`,"data-bad":r.orphanWorktrees.length>0?`true`:void 0,children:[(0,l.jsx)(`b`,{children:r.orphanWorktrees.length}),(0,l.jsx)(`span`,{children:t(`diag.orphans`)})]})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.orphans.heading`)}),r.orphanWorktrees.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.orphanWorktrees.map(n=>(0,l.jsxs)(`div`,{className:`dsh-atb-diag-orphan`,children:[(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:n.path,children:[i(n.workspaceId),` · `,n.taskId]}),(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,onClick:()=>void e.cleanupOrphan(n.workspaceId,n.taskId),children:t(`diag.orphans.cleanup`)})]},n.path))}),(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.orphans.hint`)})]}),(0,l.jsxs)(`div`,{className:`dsh-atb-diag-sec`,children:[(0,l.jsx)(`h4`,{children:t(`diag.gitignore.heading`)}),(r.gitIgnoreSuggestions??[]).length===0?(0,l.jsx)(`div`,{className:`dsh-atb-empty2`,children:t(`diag.gitignore.none`)}):(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphans`,children:r.gitIgnoreSuggestions.map(e=>(0,l.jsx)(`div`,{className:`dsh-atb-diag-orphan`,children:(0,l.jsxs)(`span`,{className:`dsh-atb-diag-orphan-path`,title:e.workspacePath,children:[i(e.workspaceId),` · `,t(`diag.gitignore.suggestA`),` `,(0,l.jsx)(`code`,{children:`.dsh-worktrees/`}),t(`diag.gitignore.suggestB`)]})},e.workspaceId))})]})]})})]})})}function ft({controller:e,tasks:t}){let n=W(),[r,i]=(0,c.useState)(`none`),[a,o]=(0,c.useState)(!1),[s,u]=(0,c.useState)(()=>new Set),d=t.filter(e=>e.trashedAt!==void 0),f=t.filter(e=>e.trashedAt===void 0&&e.status===`archived`),p=t.filter(e=>e.trashedAt===void 0&&e.status===`canceled`),m=e=>{u(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},h=e=>{u(t=>{let n=e.length>0&&e.every(e=>t.has(e.id)),r=new Set(t);for(let t of e)n?r.delete(t.id):r.add(t.id);return r})},g=async t=>{let n=(t===`trashArchived`?f:d).filter(e=>s.has(e.id));o(!0);try{for(let r of n){try{await e.remove(r.id,r.version,t===`purgeTrashed`)}catch{}u(e=>{let t=new Set(e);return t.delete(r.id),t})}}finally{o(!1),i(`none`),u(new Set)}},_=()=>{i(`none`),u(new Set)},v=[{label:n(`status.column.canceled`),dot:`canceled`,rows:p},{label:n(`status.column.archived`),dot:`archived`,rows:f,action:`trashArchived`},{label:n(`board.group.trashed`),dot:`trashed`,rows:d,action:`purgeTrashed`}];return d.length+f.length+p.length===0?(0,l.jsx)(`div`,{className:`dsh-atb-secondary`,children:(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.secondary.empty`)})}):(0,l.jsx)(`div`,{className:`dsh-atb-columns`,children:v.map(t=>{let o=r===t.action,c=o?t.rows.filter(e=>s.has(e.id)).length:0,d=t.rows.length>0&&c===t.rows.length;return(0,l.jsxs)(`div`,{className:`dsh-atb-column`,children:[(0,l.jsxs)(`div`,{className:`dsh-atb-colhead`,children:[(0,l.jsx)(`span`,{className:`dsh-atb-dot`,"data-status":t.dot}),t.label,(0,l.jsx)(`span`,{className:`dsh-atb-colcount`,children:t.rows.length}),t.action&&t.rows.length>0&&(o?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:n(`board.batch.confirmCancelTip`),onClick:_,children:n(`board.batch.cancel`)}),(0,l.jsxs)(`button`,{type:`button`,className:`dsh-atb-btn`,"data-danger":`true`,disabled:a||c===0,title:n(c===0?`board.batch.noneSelected`:`board.batch.confirmTip`),onClick:()=>{g(t.action)},children:[n(`board.batch.confirm`),`(`,c,`)`]}),(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selectall`,checked:d,disabled:a,title:n(`board.batch.selectAll`),onClick:e=>e.stopPropagation(),onChange:()=>h(t.rows)})]}):(0,l.jsx)(`button`,{type:`button`,className:`dsh-atb-btn`,disabled:a,title:t.action===`trashArchived`?n(`board.batch.trashArchivedTip`):n(`board.batch.purgeTrashedTip`),onClick:()=>{i(t.action),u(new Set)},children:t.action===`trashArchived`?n(`board.batch.trashArchived`):n(`board.batch.purgeTrashed`)}))]}),(0,l.jsxs)(`div`,{className:`dsh-atb-cards`,children:[t.rows.map(n=>t.action&&o?(0,l.jsxs)(`div`,{className:`dsh-atb-selrow`,children:[(0,l.jsx)(`input`,{type:`checkbox`,className:`dsh-atb-selbox`,checked:s.has(n.id),disabled:a,onClick:e=>e.stopPropagation(),onChange:()=>m(n.id)}),(0,l.jsx)(Ee,{task:n,controller:e})]},n.id):(0,l.jsx)(Ee,{task:n,controller:e},n.id)),t.rows.length===0&&(0,l.jsx)(`div`,{className:`dsh-atb-empty`,children:n(`board.empty`)})]})]},t.label)})})}function pt(e){let t=()=>{if((document.body.getAttribute(`data-dsh-desktop-platform`)??new URLSearchParams(window.location.search).get(`dsh-desktop-platform`))!==`win32`){delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`);return}e.dataset.dshAtbWindows=``,e.style.setProperty(`--dsh-atb-viewport-top`,`${e.getBoundingClientRect().top}px`)},n,r=()=>{n===void 0&&(n=requestAnimationFrame(()=>{n=void 0,t()}))},i=new MutationObserver(r);i.observe(document.body,{attributes:!0,childList:!0,subtree:!0,attributeFilter:[`class`,`data-dsh-desktop-platform`,`data-dsh-desktop-mode`,`data-details-collapsed`]}),i.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-dsh-atb-active`]});let a=typeof ResizeObserver>`u`?void 0:new ResizeObserver(r);return a?.observe(e),e.parentElement!==null&&a?.observe(e.parentElement),window.addEventListener(`resize`,r),r(),()=>{i.disconnect(),a?.disconnect(),window.removeEventListener(`resize`,r),n!==void 0&&cancelAnimationFrame(n),delete e.dataset.dshAtbWindows,e.style.removeProperty(`--dsh-atb-viewport-top`)}}function mt(){let e=document.querySelector(`[data-pane="sidebar"], [class*="sidebarCol"], .dshDesktopUpstreamSidebar, .dshDesktopSidebarSurface`);if(e!==null)return e.querySelector(`[class*="logoRow"]`)?.parentElement??e.firstElementChild}function ht(e){let t=e.querySelector(`button[class*="newSession"]`);if(t!==null)return t;for(let t of e.children)if(t instanceof HTMLButtonElement&&!t.matches(`[data-dsh-atb-entry]`))return t;let n=e.querySelector(`button[aria-label="新建会话"], button[aria-label="New Session"], button[aria-label*="新会话"], button[aria-label*="new session" i]`);return n===null?Array.from(e.querySelectorAll(`button`)).find(e=>!e.matches(`[data-dsh-atb-entry]`)&&/新会话|新建会话|new session/i.test(e.textContent??``)):n}function gt(e){let t=document.createElement(`button`);return t.type=`button`,t.dataset.dshAtbEntry=``,t.className=`dsh-atb-entry`,t.setAttribute(`aria-label`,U(`shared.entry.aria`)),t.innerHTML=`<span class="dsh-atb-entry-icon"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M6 2v12M10 2v12"/></svg></span><span class="dsh-atb-entry-label">${U(`shared.entry.label`)}</span><span class="dsh-atb-entry-stats"></span>`,t.addEventListener(`click`,()=>{e.toggleBoard()}),t}function _t(e){let t=0,n=0,r=0;for(let i of e.getSnapshot().ledger.tasks)i.trashedAt===void 0&&(i.status===`todo`?t++:i.status===`in_progress`?n++:i.status===`in_review`&&r++);return[t,n,r]}function vt(e,t){let n=String(t);if(e.dataset.value===n)return;let r=e.dataset.value;if(e.dataset.value=n,e.style.minWidth=`${n.length}ch`,r===void 0){e.textContent=n;return}e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``);let i=document.createElement(`span`);i.className=`dsh-atb-rn`,i.textContent=r;let a=document.createElement(`span`);a.className=`dsh-atb-rn dsh-atb-rn-next`,a.textContent=n,e.replaceChildren(i,a),e.dataset.dir=t>Number(r)?`up`:`down`,e.dataset.busy=`1`,requestAnimationFrame(()=>{e.dataset.anim=`1`});let o=()=>{e.dataset.busy===`1`&&(e.dataset.busy=``,e.dataset.anim=``,e.textContent=e.dataset.value??``)};e.addEventListener(`transitionend`,o,{once:!0}),setTimeout(o,400)}function yt(e,t){let n=e.querySelector(`.dsh-atb-entry-stats`);if(n===null)return()=>{};let r=[`todo`,`in_progress`,`in_review`],i=[];for(let e=0;e<3;e++){if(e>0){let e=document.createElement(`span`);e.className=`dsh-atb-entry-sep`,e.textContent=`|`,n.append(e)}let t=document.createElement(`span`);t.className=`dsh-atb-roll`,t.dataset.stat=r[e],n.append(t),i.push(t)}return()=>{let[e,r,a]=_t(t);vt(i[0],e),vt(i[1],r),vt(i[2],a),n.title=U(`shared.stats.title`,{todo:e,doing:r,review:a})}}function bt(e,t){let n=ht(e);if(n===void 0)return!1;if(t.parentElement!==e){let r=n.closest(`[class*="logoRow"]`),i=r!==null&&r.parentElement===e?r:n,a=Array.from(e.children).filter(e=>e instanceof HTMLElement&&e.matches(`[data-dsh-atb-entry], [data-dsh-taskboard-entry], [data-dsh-ssh-entry]`)),o=a.length>0?a[0]??null:i.nextElementSibling??null;e.insertBefore(t,o)}return!0}function xt(e){let t=gt(e),n={attempts:0,found:!1,placed:!1},r=globalThis.location?.hostname;(r===`localhost`||r===`127.0.0.1`)&&(window.__atbDebug=n);let i,a=!1,o=()=>{if(n.attempts++,i!==void 0&&!i.isConnected&&(c.disconnect(),i=void 0,a=!1),a){if(document.body.contains(t))return;c.disconnect(),i=void 0,a=!1}i??=mt(),i!==void 0&&(n.found=ht(i)!==void 0,a=bt(i,t),n.placed=a,a&&c.observe(i,{childList:!0,subtree:!0}))},s=new MutationObserver(()=>{o()});s.observe(document.body,{childList:!0,subtree:!0});let c=new MutationObserver(()=>{if(i===void 0||!i.isConnected){a=!1,o();return}i.contains(t)||(a=bt(i,t))}),l=setInterval(()=>{o()},2e3),u=yt(t,e),d=()=>{e.getSnapshot().boardOpen?t.dataset.active=`true`:delete t.dataset.active,u()},f=e.subscribe(d),p=H.subscribe(()=>{t.setAttribute(`aria-label`,U(`shared.entry.aria`));let e=t.querySelector(`.dsh-atb-entry-label`);e!==null&&(e.textContent=U(`shared.entry.label`)),u()});return d(),o(),()=>{clearInterval(l),s.disconnect(),c.disconnect(),p(),f(),t.remove()}}const St=`data-dsh-atb-active`,Ct=[`data-dsh-taskboard-active`,`data-dsh-ssh-active`],wt=`dsh-panel-activate`,Tt=`dsh-taskboard`;function Et(){return document.querySelector(`[data-pane="conversation"], [class*="centerCol"], .dshDesktopConversationSurface`)??void 0}function Dt(e){let t,n,r,i=()=>{if(n!==void 0)return;let i=Et();i!==void 0&&(n=document.createElement(`div`),n.dataset.dshAtbView=``,n.className=`dsh-atb-view`,i.appendChild(n),r=pt(n),t=(0,d.createRoot)(n),t.render((0,l.jsx)(lt,{controller:e})))},a=new MutationObserver(()=>{i()});a.observe(document.body,{childList:!0,subtree:!0});let o=()=>{if(e.getSnapshot().boardOpen){for(let e of Ct)document.documentElement.removeAttribute(e);document.documentElement.setAttribute(St,``),document.dispatchEvent(new CustomEvent(wt,{detail:Tt}))}else document.documentElement.removeAttribute(St)},s=t=>{t.detail!==Tt&&e.getSnapshot().boardOpen&&e.closeBoard()},c=t=>{if(!e.getSnapshot().boardOpen)return;let n=t.target;n!==null&&n.closest(`[data-dsh-atb-entry]`)===null&&n.closest(`[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]`)!==null&&e.closeBoard()};document.addEventListener(`click`,c,!0),document.addEventListener(wt,s);let u=e.subscribe(o);return o(),i(),()=>{document.removeEventListener(`click`,c,!0),document.removeEventListener(wt,s),a.disconnect(),u(),r?.(),document.documentElement.removeAttribute(St),t?.unmount(),n?.remove()}}const Ot=`dsh-taskboard`;function kt(e){let t=e.get?.(`slots`)??e.slots;if(!(t===void 0||typeof t.register!=`function`||typeof t.inject!=`function`||typeof t.spec!=`function`)){try{if(t.spec(`main`)===void 0||t.spec(`sidebar.panellist`)===void 0)return}catch{return}return t}}function At(e){return function(){let t=c.useCallback(e=>{if(e===null)return;let t=`__dshAtbInsetDispose`,n=e;n[t]===void 0&&(n[t]=pt(e))},[]);return c.createElement(`div`,{"data-dsh-atb-panel-root":``,ref:t,className:`dsh-atb-panel-root`},c.createElement(lt,{controller:e}))}}function jt(e){return function(t){let n=typeof t.size==`number`?t.size:16,r=c.useCallback(t=>e.subscribe(t),[e]),i=c.useCallback(()=>e.getSnapshot(),[e]),a=c.useSyncExternalStore(r,i,i),o=c.useMemo(()=>{let e=0,t=0,n=0;for(let r of a.ledger.tasks)r.trashedAt===void 0&&(r.status===`todo`?e++:r.status===`in_progress`?t++:r.status===`in_review`&&n++);return{todo:e,doing:t,review:n}},[a.ledger]),[s,l]=c.useState(!0),u=c.useCallback(e=>{if(e===null||typeof ResizeObserver>`u`)return;let t=e.closest(`button`);t!==null&&new ResizeObserver(()=>{l(t.clientWidth>44)}).observe(t)},[]),d=U(`shared.stats.title`,{todo:o.todo,doing:o.doing,review:o.review});return c.createElement(`span`,{className:`dsh-atb-pglyph`,ref:u},c.createElement(`span`,{className:`dsh-atb-picon`},c.createElement(`svg`,{width:n,height:n,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":1.3,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":!0},c.createElement(`rect`,{x:2,y:2,width:12,height:12,rx:2}),c.createElement(`path`,{d:`M6 2v12M10 2v12`})),!s&&o.todo>0?c.createElement(`span`,{className:`dsh-atb-pbadge`,title:d,"aria-hidden":!0},o.todo):null),s&&(o.todo>0||o.doing>0||o.review>0)?c.createElement(`span`,{className:`dsh-atb-pstats`,title:d,"aria-hidden":!0},c.createElement(`span`,{"data-stat":`todo`},o.todo),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_progress`},o.doing),c.createElement(`span`,{className:`dsh-atb-psep`},`|`),c.createElement(`span`,{"data-stat":`in_review`},o.review)):null)}}function Mt(e,t,n){let r=[];try{let i=t.get?.(`layout`)??t.layout;n.installCloseRequester(()=>{i?.selectPanel(null)}),r.push(()=>{n.installCloseRequester(void 0)});let a=At(n);r.push(e.inject(`main`,()=>e.register({name:`main`,key:Ot},a)));let o=jt(n);r.push(e.inject(`sidebar.panellist`,()=>e.register({name:`sidebar.panellist`,id:Ot,order:10,label:()=>U(`shared.entry.label`)},o))),console.info(`[dsh-taskboard] official panel mode: registered`,Ot)}catch(e){console.error(`[dsh-taskboard] official panel registration failed — falling back to legacy mounting:`,e);for(let e of r.splice(0))e();return}return()=>{for(let e of r.splice(0))e()}}function Nt(e,t){let n=kt(e);if(n!==void 0){let r=Mt(n,e,t);if(r!==void 0)return r}let r=[];r=[xt(t),Dt(t)];let i=0,a,o=setInterval(()=>{i++;let n=kt(e);if(n!==void 0){let i=Mt(n,e,t);if(i!==void 0){a=i,clearInterval(o);for(let e of r.splice(0))e();return}}i>=3&&clearInterval(o)},350);return()=>{clearInterval(o);for(let e of r.splice(0))e();a?.()}}function Pt(e){let t=(e,t,n)=>e.list.getSnapshot().byId[n]===void 0?`absent`:t?.list.getSnapshot().archivedSessionIds.includes(n)??!1?`archived`:`openable`,n=(t,n)=>{let r=e.getUiWorkspace?.();return typeof r?.openSession==`function`?(r.openSession(n),!0):typeof t.open==`function`?(t.open(n),!0):!1};return async r=>{let i=e.getSessions();if(i===void 0)return`unavailable`;try{let a=t(i,e.getWorkspaces(),r);if(a===`absent`){try{await i.refresh()}catch{}a=t(i,e.getWorkspaces(),r)}return a===`archived`?`archived`:a===`absent`?`missing`:n(i,r)?`opened`:`unavailable`}catch{return`unavailable`}}}const Ft=[`connection`];function It(e){try{me(),V(e.get?.(`locale`),()=>e.get?.(`locale`));let t=_(),n=new fe(t);n.installModelCatalog(async()=>{try{let t=e.get?.(`modelDirectories`)??e.modelDirectories;if(t?.catalog?.load!==void 0){let e=await t.catalog.load();if(e?.groups!==void 0&&e.groups.length>0){let t=[];for(let n of e.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`remote`)??e.remote;if(t?.session?.modelCatalog!==void 0){let e=await t.session.modelCatalog();if(e.ok&&e.value?.groups!==void 0&&e.value.groups.length>0){let t=[];for(let n of e.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}if(t?.llm?.models!==void 0){let e=await t.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.llm?.models!==void 0){let e=await t.api.llm.models({});if(e.result.ok&&e.result.value?.groups!==void 0&&e.result.value.groups.length>0){let t=[];for(let n of e.result.value.groups)for(let e of n.models)t.push({provider:n.id,model:e.id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.reasoning===void 0?{}:{reasoning:e.reasoning}});if(t.length>0)return t}}}catch{}try{let e=await t.modelCatalog();if(e.models!==void 0&&e.models.length>0)return e.models}catch{}return[]}),n.installPresetRoster(async()=>{try{let t=e.get?.(`remote`)??e.remote;if(t?.agentPresets?.list!==void 0){let e=await t.agentPresets.list(),n=e.ok===!0?e.value.presets:e.result?.ok===!0?e.result.value.presets:void 0;if(n!==void 0&&n.length>0){let e=n.map(e=>({id:e.id,name:e.name})),t=n.find(e=>e.isDefault);return{presets:e,...t===void 0?{}:{defaultId:t.id}}}}}catch{}try{let t=e.get?.(`connection`)??e.connection;if(t?.api?.agentPresets?.list!==void 0){let e=await t.api.agentPresets.list({});if(e.result.ok&&e.result.value?.presets!==void 0&&e.result.value.presets.length>0){let t=e.result.value.presets.map(e=>({id:e.id,name:e.name})),n=e.result.value.presets.find(e=>e.isDefault);return{presets:t,...n===void 0?{}:{defaultId:n.id}}}}}catch{}try{let e=await t.modelCatalog();if(e.presets!==void 0&&e.presets.length>0)return{presets:e.presets,...e.defaultPresetId===void 0?{}:{defaultId:e.defaultPresetId}}}catch{}return{presets:[]}}),n.installSessionJumper(Pt({getSessions:()=>e.get?.(`sessions`),getWorkspaces:()=>e.get?.(`workspaces`),getUiWorkspace:()=>e.get?.(`uiWorkspace`)})),n.installSessionTitles(()=>(e.get?.(`sessions`))?.list.getSnapshot().byId??{}),n.start();let r=[];try{r.push(Nt(e,n))}catch(e){console.error(`[dsh-taskboard] mount failed:`,e)}e.effect?.(()=>()=>{for(let e of r.splice(0))e();n.dispose(),ce()},`dsh-taskboard: client mount`)}catch(e){console.error(`[dsh-taskboard] client half failed to start:`,e)}}exports.apply=It,exports.inject=Ft,exports.name=`dsh-taskboard/client`; return module.exports; } }); diff --git a/src/client/board/TaskFormModal.tsx b/src/client/board/TaskFormModal.tsx index c3d3df3..b48de5f 100644 --- a/src/client/board/TaskFormModal.tsx +++ b/src/client/board/TaskFormModal.tsx @@ -511,6 +511,11 @@ export function TaskFormModal({ controller, task }: { controller: BoardControlle title={t('form.field.effortTitle')} > <option value="">{t('form.effort.follow')}{modelReasoning?.defaultEffort !== undefined ? t('shared.current', { name: modelReasoning.efforts.find(ef => ef.id === modelReasoning.defaultEffort)?.name ?? modelReasoning.defaultEffort }) : ''}</option> + {reasoningEffort !== '' && !(modelReasoning !== undefined && modelReasoning.efforts.length > 0 + ? modelReasoning.efforts.some(eff => eff.id === reasoningEffort) + : ['low', 'medium', 'high', 'none'].includes(reasoningEffort)) && ( + <option value={reasoningEffort}>{reasoningEffort}</option> + )} {modelReasoning !== undefined && modelReasoning.efforts.length > 0 ? ( modelReasoning.efforts.map(eff => ( <option key={eff.id} value={eff.id}> diff --git a/tests/default-settings.spec.ts b/tests/default-settings.spec.ts index 03472af..300d5bd 100644 --- a/tests/default-settings.spec.ts +++ b/tests/default-settings.spec.ts @@ -57,6 +57,22 @@ async function setup(initial: BoardSettings = {}) { } describe('board model, effort and preset defaults', () => { + it.each(['unavailable', 'missing-effort'] as const)('shows and submits the saved effort when the catalog is %s', async catalogState => { + const model = { ...boardModel, reasoningEffort: 'xhigh' } + const view = await setup({ defaultModel: model, defaultPresetId: 'custom' }) + if (catalogState === 'unavailable') view.controller.installModelCatalog(async () => []) + view.root.render(React.createElement(TaskFormModal, { controller: view.controller })) + await waitFor(() => view.field('执行模式(preset)').options.length === 3) + const effort = view.field('思考强度(Reasoning Effort)') + expect(effort.value).toBe('xhigh') + expect(effort.selectedOptions[0]?.textContent).toBe('xhigh') + view.title() + await waitFor(() => !view.button('创建任务').disabled) + view.button('创建任务').click() + await waitFor(() => view.created.length === 1) + expect(view.created[0]?.model).toEqual(model) + }) + it('stages and saves defaults, resets effort on model change, and can clear back to deployment defaults', async () => { const view = await setup({ defaultModel: boardModel, defaultPresetId: 'custom' }) const { root, controller, field, change, button, saved } = view