dsh-taskboard 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -130,13 +130,15 @@ agent:
130
130
  - 任务挂项目:认领校验会话归属,跨项目不可抢
131
131
  - 紧急度三色(紧急/一般/不急)筛选与色条;搜索(标题/ID)与列内排序,筛选排序持久化
132
132
  - 列头状态色圆点:待规划灰 / 待办蓝 / 进行中橙 / 待验收紫 / 已完成绿 / 已删除红
133
- - 新建/编辑弹窗:项目、模型、紧急度、执行方式、cron 实时校验与下次运行预览、执行隔离开关、验收清单编辑
133
+ - 新建/编辑弹窗:项目、模型(含思考强度)、紧急度、执行方式、cron 实时校验与下次运行预览、执行隔离开关、验收清单编辑
134
134
  - 详情面板:状态流转(done 仅限人工;清单未全勾时完成需二次确认并显示未勾数)、agent/用户评论流、执行记录(倒序,最新在最上;会话 ID 点击跳转打开该执行会话;已删除/已归档分开提示)、停止执行、Worktree 隔离块(分支 / 提交 / 改动统计 / 合并与清理)、执行报告块、验收清单块
135
135
  - 待验收列卡片快捷操作:「✓ 完成」一键验收、「✗ 退回」退回待办并可附退回原因(agent 开工前会读)
136
+ - **一键跳转执行会话(0.5.4)**:任务卡片新增「🤖 会话ID ↗」按钮、详情页顶部新增「🤖 跳转会话 ↗」按钮,持有者 Chip 同样可点——进行中优先、其次最近一次执行对应的会话一键直达(看板自动收起);已归档 / 已删除 / 会话服务不可用分别精准提示
137
+ - **记住上次模型(0.5.4)**:新建任务自动带出上次选用的模型与思考强度(模板预填与编辑不受影响)
136
138
  - **验收清单 DoD(0.4.0)**:建卡时定验收条件(≤30 项);agent 用 `taskboard_checklist` 增补/勾选(附证据 note);用户在详情页直接勾选;待验收时未完成项红色高亮 + 卡片「☑ n/m」角标(未全勾显红);清单编辑在表单中整组管理(勾选状态与证据保留)
137
139
  - **结构化执行报告(0.4.0)**:agent 收尾用 `taskboard_execution_report` 提交(摘要 / 改动文件 / 自验 / 产物 / 剩余风险),自动挂到当前执行记录;待验收详情页分栏渲染;开场框架行明示提交时序(报告 → 评论 → 移待验收)
138
140
  - **JSON 导入(0.4.0)**:顶栏「⬆ 导入」选择备份文件 → 干跑预览(新增 / 覆盖 / 无效分类明细)→ 合并(按 id upsert)或整册替换(自动先备份当前台账 + 二次确认);⬇ JSON 导出的文件即同格式可直接恢复
139
- - **任务模板(0.4.0)**:「+ 新建任务 ▼」下拉(空白 / 内置 Bug 修复·发布检查·例行巡检 / 管理模板)一键预填表单(标题/描述/Prompt/紧急度/定时/隔离/preset/清单);任务详情「⌗ 存为模板」沉淀常用配置;模板存 DSH 主目录 side file,改名/删除在管理弹窗
141
+ - **任务模板(0.4.0)**:「+ 新建任务 ▼」下拉(空白 / 内置 新增功能·Bug 修复·发布检查·例行巡检 / 管理模板)一键预填表单(标题/描述/Prompt/紧急度/定时/隔离/preset/清单);任务详情「⌗ 存为模板」沉淀常用配置;模板存 DSH 主目录 side file,改名/删除在管理弹窗
140
142
  - **Diff 查看器(0.4.0)**:隔离块提交行、未提交修改文件行点击即在看板内展开 diff(提交 `git show` / 文件 `git diff`,128KB·2000 行封顶标注截断);worktree 已删时回落主仓(仅限提交与带基线的范围 diff)
141
143
 
142
144
  **Agent 工具(taskboard_\*)**
@@ -144,7 +146,7 @@ agent:
144
146
  - 代码级协议闸:agent 永远移不到 done(清单全勾也不行);任务被持有时不可抢占;model/execution 对 agent 只读
145
147
 
146
148
  **执行**
147
- - 手动执行或 cron 定时:每次执行在任务项目内新建全新会话(干净上下文、可指定模型、可指定 preset);开场两条消息同一回合送达——插件上下文行携带任务框架与交接协议(含失败回退路径),卡片内容(提示词或标题+描述)以正常用户消息呈现
149
+ - 手动执行或 cron 定时:每次执行在任务项目内新建全新会话(干净上下文、可指定模型、可指定 preset);开场两条消息同一回合送达——插件上下文行携带任务框架与交接协议(含失败回退路径),卡片内容(标题+描述+提示词)以正常用户消息呈现
148
150
  - **任务级 preset(0.3.3)**:新建/编辑表单「执行模式(preset)」下拉——执行会话按该 preset 组合(工具集与人设由此而来,对齐 GUI 新会话的组合方式);默认预选部署默认 preset,也可选「跟随部署默认」;preset 损坏时执行直接失败并把原因写进执行记录(不产出半组合会话);随时可改,下轮执行生效
149
151
  - **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/分支上继续执行(保留上次改动与提交)
150
152
  - **看板设置(0.5.0)**:顶栏「🛠 设置」——选择新建任务默认怎么执行(🌿 Worktree 隔离 / 📁 原目录执行,出厂默认后者)。保存后新建的任务都按它来;之后改设置,已建好的任务不受影响
@@ -159,7 +161,7 @@ agent:
159
161
  - **Worktree 隔离是协作约定而非沙箱**:执行会话拥有完整工具权限,隔离依赖分支约定,不适用于运行不可信代码的场景。
160
162
  - **数据本地**:台账、模板全部存本机 DSH 主目录;不外发任何数据,无需 Token / API Key。
161
163
 
162
- ## 配置与数据
164
+ ## 配置与数据
163
165
 
164
166
  开箱即用,全部配置项如下(均为可选):
165
167
 
@@ -216,6 +218,20 @@ node scripts/screenshot.mjs # 重新生成 img/ 截图(需本机 Edge)
216
218
 
217
219
  ## 升级日志
218
220
 
221
+ ### 0.5.4
222
+
223
+
224
+ - **看板卡片与任务详情一键跳转执行会话: [@jw5555555555](https://github.com/jw5555555555)([#11](https://github.com/cloader/dsh-taskboard/pull/11))**:卡片元数据行新增「🤖 会话ID ↗」按钮、详情页顶部新增「🤖 跳转会话 ↗」按钮、持有者 Chip 可点击——进行中优先、其次最近一次执行对应的会话一键直达(看板自动收起);已归档 / 已删除 / 会话服务不可用分别给出明确提示
225
+ - **新建任务记住上次选用的模型,支持设置思考强度: [@jw5555555555](https://github.com/jw5555555555)([#11](https://github.com/cloader/dsh-taskboard/pull/11))**:create 模式自动带出上次的模型与思考强度(模板预填与编辑不受影响);模型可固定思考强度(reasoningEffort,如 low/medium/high),随执行会话下发;支持思考强度的模型从 DSH 模型目录读取可用档位
226
+ - **列内排序新增「按标题」: [@Amoss-1](https://github.com/Amoss-1)([#4](https://github.com/cloader/dsh-taskboard/pull/4))**:数字感知比较,数字前缀按真实数值排序(`01 < 02 < 10 < 90`,字符串比较会把 `10` 排到 `02` 前);排序选择随视图状态持久化
227
+ - 界面细节:下拉 / 输入框适配明暗主题(DSH 主题变量 + color-scheme);模板管理弹窗布局优化
228
+
229
+ ### 0.5.3
230
+
231
+ - **执行 Prompt 改为追加式**:自定义 Prompt 不再整体替换开场指令——实际发给执行会话的是「标题+任务描述+Prompt」,写在描述里的背景不再被漏掉;新建/编辑表单文案与 `taskboard_create` 工具描述同步更新
232
+ - **新增内置「新增功能」模板**:排在「Bug 修复」之前——标题前缀「新增:」,按「明确需求要点 → 实现 → 补测试 → 跑套件」四步走,自带三项验收清单
233
+ - **「Bug 修复」模板首行改为「修复以上问题并按序交接」**:描述现在先于 Prompt 送达,「以上」才指得住
234
+
219
235
  ### 0.5.2
220
236
 
221
237
  - **支持 DSH Desktop 非兼容模式下侧栏没有「任务看板」入口、看板也打不开的问题([#6](https://github.com/cloader/dsh-taskboard/issues/6))**:
package/lib/client.js CHANGED
@@ -275,7 +275,7 @@ window.__ModuleLoader__.load({
275
275
  sortBy: "default"
276
276
  };
277
277
  const parsed = JSON.parse(raw);
278
- const sortBy = parsed.sortBy === "updated" || parsed.sortBy === "urgency" || parsed.sortBy === "created" ? parsed.sortBy : "default";
278
+ const sortBy = parsed.sortBy === "updated" || parsed.sortBy === "urgency" || parsed.sortBy === "created" || parsed.sortBy === "title" ? parsed.sortBy : "default";
279
279
  return {
280
280
  workspaceId: typeof parsed.workspaceId === "string" ? parsed.workspaceId : void 0,
281
281
  urgencies: Array.isArray(parsed.urgencies) ? parsed.urgencies.filter((u) => u === "urgent" || u === "normal" || u === "relaxed") : [],
@@ -1053,8 +1053,43 @@ window.__ModuleLoader__.load({
1053
1053
  .dsh-atb-spacer { flex: 1; }
1054
1054
  .dsh-atb-select, .dsh-atb-input {
1055
1055
  font: inherit; font-size: 12.5px; padding: 5px 8px; border-radius: 7px;
1056
- border: 1px solid var(--dsw-border, rgba(128,128,128,.35));
1057
- background: var(--dsw-bg, transparent); color: inherit;
1056
+ border: 1px solid var(--dsw-border, var(--dsw-alias-border-l2, rgba(128,128,128,.35)));
1057
+ background: var(--dsw-alias-bg-module-platform, var(--dsw-bg-elevated, var(--dsw-bg, transparent)));
1058
+ color: var(--dsw-alias-label-primary, var(--dsw-text-primary, inherit));
1059
+ color-scheme: light dark;
1060
+ }
1061
+ .dsh-atb-select option,
1062
+ .dsh-atb-modal-body select option {
1063
+ background: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-module-platform, #252830));
1064
+ color: var(--dsw-alias-label-primary, var(--dsw-text-primary, #e6e8eb));
1065
+ }
1066
+ body[data-ds-dark-theme] .dsh-atb-board,
1067
+ body[data-ds-dark-theme] .dsh-atb-modal,
1068
+ body[data-ds-dark-theme] .dsh-atb-select,
1069
+ body[data-ds-dark-theme] .dsh-atb-input,
1070
+ body[data-ds-dark-theme] .dsh-atb-modal-body select,
1071
+ body[data-ds-dark-theme] .dsh-atb-modal-body input,
1072
+ body[data-ds-dark-theme] .dsh-atb-modal-body textarea {
1073
+ color-scheme: dark;
1074
+ }
1075
+ body[data-ds-dark-theme] .dsh-atb-select option,
1076
+ body[data-ds-dark-theme] .dsh-atb-modal-body select option {
1077
+ background: #252830;
1078
+ color: #e6e8eb;
1079
+ }
1080
+ @media (prefers-color-scheme: dark) {
1081
+ .dsh-atb-select,
1082
+ .dsh-atb-input,
1083
+ .dsh-atb-modal-body select,
1084
+ .dsh-atb-modal-body input,
1085
+ .dsh-atb-modal-body textarea {
1086
+ color-scheme: dark;
1087
+ }
1088
+ .dsh-atb-select option,
1089
+ .dsh-atb-modal-body select option {
1090
+ background: #252830;
1091
+ color: #e6e8eb;
1092
+ }
1058
1093
  }
1059
1094
  .dsh-atb-chip {
1060
1095
  display: inline-flex; align-items: center; gap: 5px;
@@ -1130,6 +1165,19 @@ window.__ModuleLoader__.load({
1130
1165
  .dsh-atb-badge[data-kind="trashed"] { background: rgba(229,72,77,.14); color: #e5484d; text-decoration: line-through; }
1131
1166
  .dsh-atb-badge[data-kind="done"] { background: rgba(46,160,67,.16); color: #2ea043; }
1132
1167
  .dsh-atb-badge[data-kind="running"] { background: rgba(229,152,42,.16); color: #e69842; }
1168
+ .dsh-atb-card-session {
1169
+ font: inherit; font-size: 10.5px; line-height: 1; padding: 2px 6px; border-radius: 5px;
1170
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.3));
1171
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.14));
1172
+ color: var(--dsw-text-secondary, inherit);
1173
+ cursor: pointer; display: inline-flex; align-items: center; gap: 3px;
1174
+ transition: all .15s ease;
1175
+ }
1176
+ .dsh-atb-card-session:hover {
1177
+ border-color: var(--dsw-alias-brand-primary, #1f2328);
1178
+ background: var(--dsw-hover, rgba(128,128,128,.24));
1179
+ color: var(--dsw-alias-label-primary, inherit);
1180
+ }
1133
1181
 
1134
1182
  /* ---------- card quick review (in_review column) ---------- */
1135
1183
  .dsh-atb-quick { display: flex; gap: 6px; margin-top: 7px; }
@@ -1177,6 +1225,17 @@ window.__ModuleLoader__.load({
1177
1225
  background: var(--dsw-bg-elevated, rgba(128,128,128,.07)); color: var(--dsw-text-secondary, inherit);
1178
1226
  }
1179
1227
  .dsh-atb-detail-edit:hover { border-color: var(--dsw-alias-brand-primary, #1f2328); color: var(--dsw-alias-label-primary, inherit); }
1228
+ .dsh-atb-detail-session {
1229
+ font: inherit; font-size: 12px; padding: 4px 10px; border-radius: 7px; cursor: pointer;
1230
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.32));
1231
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.1));
1232
+ color: var(--dsw-alias-label-primary, inherit);
1233
+ display: inline-flex; align-items: center; gap: 4px;
1234
+ }
1235
+ .dsh-atb-detail-session:hover {
1236
+ border-color: var(--dsw-alias-brand-primary, #1f2328);
1237
+ background: var(--dsw-hover, rgba(128,128,128,.22));
1238
+ }
1180
1239
 
1181
1240
  .dsh-atb-statuspill {
1182
1241
  flex: none; font-size: 11px; font-weight: 600; padding: 2px 9px; border-radius: 999px; letter-spacing: .02em;
@@ -1194,6 +1253,14 @@ window.__ModuleLoader__.load({
1194
1253
  padding: 3px 8px; border-radius: 6px;
1195
1254
  background: var(--dsw-bg-inset, rgba(128,128,128,.09)); color: var(--dsw-text-secondary, #999);
1196
1255
  }
1256
+ button.dsh-atb-chip2.dsh-atb-chip-btn {
1257
+ font: inherit; border: 1px solid var(--dsw-border, rgba(128,128,128,.25)); cursor: pointer;
1258
+ }
1259
+ button.dsh-atb-chip2.dsh-atb-chip-btn:hover {
1260
+ border-color: var(--dsw-alias-brand-primary, #1f2328);
1261
+ color: var(--dsw-alias-label-primary, inherit);
1262
+ background: var(--dsw-hover, rgba(128,128,128,.2));
1263
+ }
1197
1264
  .dsh-atb-chip2-icon { font-size: 10.5px; opacity: .85; }
1198
1265
  .dsh-atb-chip2[data-tone="urgent"] { background: rgba(229,72,77,.15); color: #e5484d; }
1199
1266
  .dsh-atb-chip2[data-tone="normal"] { background: rgba(142,78,198,.14); color: #a06ce0; }
@@ -1607,19 +1674,30 @@ window.__ModuleLoader__.load({
1607
1674
  .dsh-atb-newmenu-opt:hover { background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.1)); }
1608
1675
  .dsh-atb-newmenu-sep { height: 1px; margin: 4px 6px; background: var(--dsw-alias-border-l2, rgba(128,128,128,.25)); }
1609
1676
 
1610
- .dsh-atb-tplm { max-width: 560px; width: min(560px, 92vw); }
1611
- .dsh-atb-tplm-list { display: flex; flex-direction: column; gap: 8px; }
1677
+ .dsh-atb-tplm { max-width: 600px; width: min(600px, 92vw); }
1678
+ .dsh-atb-tplm .dsh-atb-modal-body,
1679
+ .dsh-atb-set .dsh-atb-modal-body,
1680
+ .dsh-atb-diag .dsh-atb-modal-body,
1681
+ .dsh-atb-imp .dsh-atb-modal-body {
1682
+ display: flex; flex-direction: column; gap: 10px;
1683
+ }
1684
+ .dsh-atb-tplm-list { display: flex; flex-direction: column; gap: 8px; width: 100%; }
1612
1685
  .dsh-atb-tplm-row {
1613
- display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
1686
+ display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 12px; border-radius: 8px;
1614
1687
  border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
1688
+ background: var(--dsw-alias-bg-layer-1, rgba(128,128,128,.02));
1615
1689
  }
1616
1690
  .dsh-atb-tplm-name {
1617
- flex: 0 0 160px; font-size: 12.5px; padding: 5px 8px; border-radius: 7px;
1691
+ flex: 0 0 150px; width: 150px; max-width: 150px; font-size: 12.5px; padding: 5px 8px; border-radius: 7px;
1618
1692
  border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.3));
1619
1693
  background: var(--dsw-alias-bg-layer-1, transparent); color: inherit;
1694
+ box-sizing: border-box;
1620
1695
  }
1621
- .dsh-atb-tplm-meta { flex: 1; min-width: 0; font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
1622
- .dsh-atb-tplm-btns { display: flex; gap: 6px; flex-shrink: 0; }
1696
+ .dsh-atb-tplm-meta {
1697
+ flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, gray);
1698
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1699
+ }
1700
+ .dsh-atb-tplm-btns { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
1623
1701
 
1624
1702
  .dsh-atb-imp { max-width: 600px; width: min(600px, 92vw); }
1625
1703
  .dsh-atb-imp-picker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
@@ -1939,7 +2017,7 @@ window.__ModuleLoader__.load({
1939
2017
  * @module dsh-taskboard/shared/version
1940
2018
  */
1941
2019
  /** The package version (must equal package.json "version"). */
1942
- const PLUGIN_VERSION = "0.5.2";
2020
+ const PLUGIN_VERSION = "0.5.4";
1943
2021
 
1944
2022
  //#endregion
1945
2023
  //#region src/client/board/labels.ts
@@ -2021,6 +2099,11 @@ window.__ModuleLoader__.load({
2021
2099
  */
2022
2100
  /** dataTransfer type carrying the dragged task id. */
2023
2101
  const DRAG_TYPE = "application/x-dsh-atb-task";
2102
+ /** Compact session-id display (execution sessions carry the taskboard infix). */
2103
+ function shortId$1(id) {
2104
+ if (id === void 0) return "";
2105
+ return id.replace(/^session-(taskboard-)?/, "").slice(0, 8);
2106
+ }
2024
2107
  /**
2025
2108
  * The card view.
2026
2109
  * @param task - the task record.
@@ -2036,6 +2119,8 @@ window.__ModuleLoader__.load({
2036
2119
  const running = task.executions.find((ex) => ex.outcome === "running");
2037
2120
  const stale = now !== void 0 && isStaleClaim(task, now);
2038
2121
  const reviewing = task.status === "in_review" && task.trashedAt === void 0;
2122
+ const sessionExecution = [...task.executions].reverse().find((ex) => ex.sessionId !== void 0);
2123
+ const targetSessionId = running?.sessionId ?? sessionExecution?.sessionId ?? (task.claimedBy?.startsWith("session-") ? task.claimedBy : void 0);
2039
2124
  /** Submit the quick-reject: one atomic route (move + optional note). */
2040
2125
  const submitReject = () => {
2041
2126
  controller.reject(task.id, task.version, note).then((ok) => {
@@ -2101,9 +2186,10 @@ window.__ModuleLoader__.load({
2101
2186
  "data-kind": "scheduled",
2102
2187
  children: ["⏰ ", fmtTime(task.execution.nextRunAt)]
2103
2188
  }),
2104
- task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2189
+ task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2105
2190
  className: "dsh-atb-badge",
2106
- children: task.model.model
2191
+ title: `固定模型: ${task.model.provider}/${task.model.model}${task.model.reasoningEffort !== void 0 ? ` · 思考强度: ${task.model.reasoningEffort}` : ""}`,
2192
+ children: [task.model.model, task.model.reasoningEffort !== void 0 ? ` (${task.model.reasoningEffort})` : ""]
2107
2193
  }),
2108
2194
  task.checklist !== void 0 && task.checklist.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2109
2195
  className: "dsh-atb-badge",
@@ -2126,6 +2212,34 @@ window.__ModuleLoader__.load({
2126
2212
  "data-kind": last.outcome === "running" ? "running" : last.outcome,
2127
2213
  children: OUTCOME_LABEL[last.outcome] ?? last.outcome
2128
2214
  }),
2215
+ targetSessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2216
+ type: "button",
2217
+ className: "dsh-atb-card-session",
2218
+ title: `点击一键跳转到会话:${targetSessionId}`,
2219
+ onClick: (e) => {
2220
+ e.stopPropagation();
2221
+ controller.openSession(targetSessionId).then((result) => {
2222
+ if (result === "missing") {
2223
+ const msg = `该会话已被删除(${shortId$1(targetSessionId)}),无法打开`;
2224
+ if (onAlert !== void 0) onAlert(msg);
2225
+ else alert(msg);
2226
+ } else if (result === "archived") {
2227
+ const msg = `该会话已归档(${shortId$1(targetSessionId)}),已从会话列表隐藏`;
2228
+ if (onAlert !== void 0) onAlert(msg);
2229
+ else alert(msg);
2230
+ } else if (result === "unavailable") {
2231
+ const msg = `会话导航不可用,会话 ID:${targetSessionId}`;
2232
+ if (onAlert !== void 0) onAlert(msg);
2233
+ else alert(msg);
2234
+ }
2235
+ });
2236
+ },
2237
+ children: [
2238
+ "🤖 ",
2239
+ shortId$1(targetSessionId),
2240
+ " ↗"
2241
+ ]
2242
+ }),
2129
2243
  task.comments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["💬 ", task.comments.length] }),
2130
2244
  task.trashedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2131
2245
  className: "dsh-atb-badge",
@@ -2291,10 +2405,11 @@ window.__ModuleLoader__.load({
2291
2405
  return `${Math.floor(s / 3600)}h${Math.floor(s % 3600 / 60)}m`;
2292
2406
  }
2293
2407
  /** Small labelled meta chip. */
2294
- function Chip({ icon, children, tone }) {
2408
+ function Chip({ icon, children, tone, title }) {
2295
2409
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2296
2410
  className: "dsh-atb-chip2",
2297
2411
  "data-tone": tone,
2412
+ title,
2298
2413
  children: [icon !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2299
2414
  className: "dsh-atb-chip2-icon",
2300
2415
  children: icon
@@ -2753,6 +2868,8 @@ window.__ModuleLoader__.load({
2753
2868
  const holder = task.status === "in_progress" ? task.claimedBy : void 0;
2754
2869
  const stale = now !== void 0 && isStaleClaim(task, now);
2755
2870
  const unchecked = (task.checklist ?? []).filter((i) => !i.checked).length;
2871
+ const sessionExecution = [...task.executions].reverse().find((e) => e.sessionId !== void 0);
2872
+ const targetSessionId = runningExecution?.sessionId ?? sessionExecution?.sessionId ?? (task.claimedBy?.startsWith("session-") ? task.claimedBy : void 0);
2756
2873
  /** Fire one top action under the shared busy guard; re-enable on settle. */
2757
2874
  const runAction = (action) => {
2758
2875
  if (actionBusy) return;
@@ -2795,9 +2912,10 @@ window.__ModuleLoader__.load({
2795
2912
  icon: "📁",
2796
2913
  children: ws?.title ?? shortId(task.workspaceId)
2797
2914
  }),
2798
- task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2915
+ task.model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
2799
2916
  icon: "✦",
2800
- children: task.model.model
2917
+ title: `固定模型: ${task.model.provider}/${task.model.model}${task.model.reasoningEffort !== void 0 ? ` · 思考强度: ${task.model.reasoningEffort}` : ""}`,
2918
+ children: [task.model.model, task.model.reasoningEffort !== void 0 ? ` · ${task.model.reasoningEffort}` : ""]
2801
2919
  }),
2802
2920
  task.presetId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2803
2921
  icon: "🎛",
@@ -2835,13 +2953,20 @@ window.__ModuleLoader__.load({
2835
2953
  icon: "🌿",
2836
2954
  children: "Worktree 隔离"
2837
2955
  }),
2838
- holder !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
2839
- icon: stale ? "" : "🔑",
2840
- tone: stale ? "urgent" : void 0,
2956
+ holder !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2957
+ type: "button",
2958
+ className: "dsh-atb-chip2 dsh-atb-chip-btn",
2959
+ "data-tone": stale ? "urgent" : void 0,
2960
+ title: `点击跳转至该会话:${holder}`,
2961
+ onClick: () => jumpToSession(holder),
2841
2962
  children: [
2963
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2964
+ className: "dsh-atb-chip2-icon",
2965
+ children: stale ? "⏱" : "🤖"
2966
+ }),
2842
2967
  stale ? "认领超时 · " : "由 ",
2843
2968
  shortId(holder),
2844
- " 持有"
2969
+ " 持有"
2845
2970
  ]
2846
2971
  }),
2847
2972
  task.trashedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
@@ -2865,6 +2990,13 @@ window.__ModuleLoader__.load({
2865
2990
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2866
2991
  className: "dsh-atb-detail-topbtns",
2867
2992
  children: [
2993
+ targetSessionId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2994
+ type: "button",
2995
+ className: "dsh-atb-detail-session",
2996
+ title: `一键跳转到对应会话:${targetSessionId}`,
2997
+ onClick: () => jumpToSession(targetSessionId),
2998
+ children: "🤖 跳转会话 ↗"
2999
+ }),
2868
3000
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2869
3001
  type: "button",
2870
3002
  className: "dsh-atb-detail-edit",
@@ -3210,6 +3342,34 @@ window.__ModuleLoader__.load({
3210
3342
  *
3211
3343
  * @module dsh-taskboard/client/board/TaskFormModal
3212
3344
  */
3345
+ /** Local storage key for remembering the last selected model in create mode. */
3346
+ const LAST_MODEL_KEY = "dsh-taskboard-last-model-v1";
3347
+ /** Read the remembered model from localStorage. */
3348
+ function loadLastModel() {
3349
+ try {
3350
+ const raw = localStorage.getItem(LAST_MODEL_KEY);
3351
+ if (raw === null) return void 0;
3352
+ const parsed = JSON.parse(raw);
3353
+ if (typeof parsed === "object" && parsed !== null) {
3354
+ const { provider, model, reasoningEffort } = parsed;
3355
+ if (typeof provider === "string" && typeof model === "string" && provider.trim().length > 0 && model.trim().length > 0) return {
3356
+ provider: provider.trim(),
3357
+ model: model.trim(),
3358
+ ...typeof reasoningEffort === "string" && reasoningEffort.trim().length > 0 ? { reasoningEffort: reasoningEffort.trim() } : {}
3359
+ };
3360
+ }
3361
+ return;
3362
+ } catch {
3363
+ return;
3364
+ }
3365
+ }
3366
+ /** Save the remembered model to localStorage. */
3367
+ function saveLastModel(model) {
3368
+ try {
3369
+ if (model === void 0) localStorage.removeItem(LAST_MODEL_KEY);
3370
+ else localStorage.setItem(LAST_MODEL_KEY, JSON.stringify(model));
3371
+ } catch {}
3372
+ }
3213
3373
  /** Urgency segmented options with a one-line hint each. */
3214
3374
  const URGENCY_OPTIONS = [
3215
3375
  {
@@ -3340,7 +3500,12 @@ window.__ModuleLoader__.load({
3340
3500
  const [mode, setMode] = (0, react.useState)(task?.execution.mode === "scheduled" || prefill?.execution?.mode === "scheduled" ? "scheduled" : "claim");
3341
3501
  const [cron, setCron] = (0, react.useState)(task?.execution.cron ?? prefill?.execution?.cron ?? "0 9 * * *");
3342
3502
  const [catalog, setCatalog] = (0, react.useState)([]);
3343
- const [model, setModel] = (0, react.useState)(task?.model !== void 0 || prefill?.model !== void 0 ? JSON.stringify(task?.model ?? prefill?.model) : "");
3503
+ const initialModel = task?.model ?? prefill?.model ?? (!editing ? loadLastModel() : void 0);
3504
+ const [model, setModel] = (0, react.useState)(initialModel !== void 0 ? JSON.stringify({
3505
+ provider: initialModel.provider,
3506
+ model: initialModel.model
3507
+ }) : "");
3508
+ const [reasoningEffort, setReasoningEffort] = (0, react.useState)(initialModel?.reasoningEffort ?? "");
3344
3509
  const initialPreset = task?.presetId ?? prefill?.presetId ?? "";
3345
3510
  const [presetId, setPresetId] = (0, react.useState)(initialPreset);
3346
3511
  const [presets, setPresets] = (0, react.useState)([]);
@@ -3402,9 +3567,21 @@ window.__ModuleLoader__.load({
3402
3567
  ...r,
3403
3568
  text: r.text.trim()
3404
3569
  })).filter((r) => r.text.length > 0);
3570
+ const parsedModel = model !== "" ? JSON.parse(model) : void 0;
3571
+ const modelReasoning = (parsedModel !== void 0 ? catalog.find((m) => m.provider === parsedModel.provider && m.model === parsedModel.model) : void 0)?.reasoning;
3572
+ const buildPickedModel = () => {
3573
+ if (parsedModel === void 0) return void 0;
3574
+ const eff = reasoningEffort.trim();
3575
+ return {
3576
+ provider: parsedModel.provider,
3577
+ model: parsedModel.model,
3578
+ ...eff.length > 0 ? { reasoningEffort: eff } : {}
3579
+ };
3580
+ };
3405
3581
  const submit = () => {
3406
3582
  if (!valid || busy) return;
3407
- const picked = model !== "" ? JSON.parse(model) : void 0;
3583
+ const picked = buildPickedModel();
3584
+ if (!editing) saveLastModel(picked);
3408
3585
  const isolationOut = isolationPayload();
3409
3586
  const presetOut = presetPayload();
3410
3587
  const rows = filledRows();
@@ -3442,7 +3619,8 @@ window.__ModuleLoader__.load({
3442
3619
  /** Save the form, then immediately trigger a manual run of the task. */
3443
3620
  const submitAndRun = () => {
3444
3621
  if (!valid || runBlocked || busy) return;
3445
- const picked = model !== "" ? JSON.parse(model) : void 0;
3622
+ const picked = buildPickedModel();
3623
+ if (!editing) saveLastModel(picked);
3446
3624
  const isolationOut = isolationPayload();
3447
3625
  const presetOut = presetPayload();
3448
3626
  const rows = filledRows();
@@ -3548,7 +3726,17 @@ window.__ModuleLoader__.load({
3548
3726
  label: "模型(默认 = 会话默认模型)",
3549
3727
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3550
3728
  value: model,
3551
- onChange: (e) => setModel(e.target.value),
3729
+ onChange: (e) => {
3730
+ const val = e.target.value;
3731
+ setModel(val);
3732
+ if (val === "") setReasoningEffort("");
3733
+ else {
3734
+ const pm = JSON.parse(val);
3735
+ const cm = catalog.find((m) => m.provider === pm.provider && m.model === pm.model);
3736
+ if (cm?.reasoning?.defaultEffort !== void 0) setReasoningEffort(cm.reasoning.defaultEffort);
3737
+ else setReasoningEffort("");
3738
+ }
3739
+ },
3552
3740
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3553
3741
  value: "",
3554
3742
  children: "默认模型"
@@ -3566,6 +3754,38 @@ window.__ModuleLoader__.load({
3566
3754
  }, `${m.provider}/${m.model}`))]
3567
3755
  })
3568
3756
  }),
3757
+ parsedModel !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3758
+ label: "思考强度(Reasoning Effort)",
3759
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3760
+ value: reasoningEffort,
3761
+ onChange: (e) => setReasoningEffort(e.target.value),
3762
+ title: "设置模型的思考强度(如 low/medium/high);默认 = 跟随模型/提供商默认",
3763
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
3764
+ value: "",
3765
+ children: ["跟随模型默认", modelReasoning?.defaultEffort !== void 0 ? `(当前:${modelReasoning.efforts.find((ef) => ef.id === modelReasoning.defaultEffort)?.name ?? modelReasoning.defaultEffort})` : ""]
3766
+ }), modelReasoning !== void 0 && modelReasoning.efforts.length > 0 ? modelReasoning.efforts.map((eff) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
3767
+ value: eff.id,
3768
+ children: [eff.name, eff.description ? ` (${eff.description})` : ""]
3769
+ }, eff.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3770
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3771
+ value: "low",
3772
+ children: "低 (low)"
3773
+ }),
3774
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3775
+ value: "medium",
3776
+ children: "中 (medium)"
3777
+ }),
3778
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3779
+ value: "high",
3780
+ children: "高 (high)"
3781
+ }),
3782
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3783
+ value: "none",
3784
+ children: "关闭思考 (none)"
3785
+ })
3786
+ ] })]
3787
+ })
3788
+ }),
3569
3789
  presets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3570
3790
  label: "执行模式(preset)",
3571
3791
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
@@ -3615,12 +3835,12 @@ window.__ModuleLoader__.load({
3615
3835
  })
3616
3836
  }),
3617
3837
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3618
- label: editing ? "执行 Prompt" : "执行 Prompt(可选,默认 = 标题+描述)",
3838
+ label: editing ? "执行 Prompt(实际 Prompt = 标题+任务描述+Prompt)" : "执行 Prompt(可选;实际 Prompt = 标题+任务描述+Prompt)",
3619
3839
  full: true,
3620
3840
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
3621
3841
  value: prompt,
3622
3842
  onChange: (e) => setPrompt(e.target.value),
3623
- placeholder: "发给执行会话的完整指令。支持模板变量:{{lastExecution}}(上次执行结果)、{{lastComments}}(最近 3 条评论)"
3843
+ placeholder: "追加在「标题+任务描述」之后发给执行会话的补充指令。支持模板变量:{{lastExecution}}(上次执行结果)、{{lastComments}}(最近 3 条评论)"
3624
3844
  })
3625
3845
  }),
3626
3846
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
@@ -4246,6 +4466,7 @@ window.__ModuleLoader__.load({
4246
4466
  }),
4247
4467
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4248
4468
  className: "dsh-atb-tplm-meta",
4469
+ title: `${t.builtin === true ? "内置" : "自建"}${t.task.checklist !== void 0 && t.task.checklist.length > 0 ? ` · 清单 ${t.task.checklist.length} 项` : ""}${t.task.urgency !== void 0 ? ` · ${t.task.urgency}` : ""}`,
4249
4470
  children: [
4250
4471
  t.builtin === true ? "内置" : "自建",
4251
4472
  t.task.checklist !== void 0 && t.task.checklist.length > 0 ? ` · 清单 ${t.task.checklist.length} 项` : "",
@@ -4344,6 +4565,7 @@ window.__ModuleLoader__.load({
4344
4565
  if (state.sortBy === "updated") sorted.sort((a, b) => b.updatedAt - a.updatedAt);
4345
4566
  else if (state.sortBy === "created") sorted.sort((a, b) => b.createdAt - a.createdAt);
4346
4567
  else if (state.sortBy === "urgency") sorted.sort((a, b) => URGENCY_RANK[a.urgency] - URGENCY_RANK[b.urgency] || b.updatedAt - a.updatedAt);
4568
+ else if (state.sortBy === "title") sorted.sort((a, b) => a.title.localeCompare(b.title, void 0, { numeric: true }));
4347
4569
  return sorted;
4348
4570
  }
4349
4571
  /**
@@ -4473,6 +4695,10 @@ window.__ModuleLoader__.load({
4473
4695
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4474
4696
  value: "created",
4475
4697
  children: "创建时间"
4698
+ }),
4699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4700
+ value: "title",
4701
+ children: "按标题"
4476
4702
  })
4477
4703
  ]
4478
4704
  }),
@@ -4976,8 +5202,8 @@ window.__ModuleLoader__.load({
4976
5202
  /** Required client services (fiber inject waiting). */
4977
5203
  const inject = ["connection"];
4978
5204
  /**
4979
- * Mount the client half.
4980
- * @param ctx - the client context (connection injected).
5205
+ * Client entry: installs styles, starts the controller, mounts DOM seats.
5206
+ * @param ctx - the cordis client context.
4981
5207
  */
4982
5208
  function apply(ctx) {
4983
5209
  try {
@@ -4992,7 +5218,8 @@ window.__ModuleLoader__.load({
4992
5218
  for (const group of response.result.value.groups) for (const model of group.models) out.push({
4993
5219
  provider: group.id,
4994
5220
  model: model.id,
4995
- name: model.name
5221
+ name: model.name,
5222
+ ...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {}
4996
5223
  });
4997
5224
  return out;
4998
5225
  });
@@ -229,7 +229,8 @@ var ExecutionService = class {
229
229
  },
230
230
  ...model !== void 0 ? { agentOptions: {
231
231
  provider: model.provider,
232
- model: model.model
232
+ model: model.model,
233
+ ...model.reasoningEffort !== void 0 ? { reasoningEffort: model.reasoningEffort } : {}
233
234
  } } : {},
234
235
  ...composition !== void 0 ? { setup: composition.setup } : {}
235
236
  });
@@ -492,8 +493,9 @@ var ExecutionService = class {
492
493
  return text;
493
494
  }
494
495
  /**
495
- * The card body as a normal user bubble: the effective prompt (explicit
496
- * prompt, else title+description) with template variables resolved from
496
+ * The card body as a normal user bubble: the effective prompt (title+
497
+ * description, with the explicit prompt appended when set) with template
498
+ * variables resolved from
497
499
  * the task's own history at submit time (valuable for recurring patrols):
498
500
  * `{{lastExecution}}` → the previous execution's trigger/outcome/error;
499
501
  * `{{lastComments}}` → the last three comments (who + body).