dsh-taskboard 0.5.3 → 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,9 +130,11 @@ 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 导出的文件即同格式可直接恢复
@@ -216,6 +218,14 @@ 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
+
219
229
  ### 0.5.3
220
230
 
221
231
  - **执行 Prompt 改为追加式**:自定义 Prompt 不再整体替换开场指令——实际发给执行会话的是「标题+任务描述+Prompt」,写在描述里的背景不再被漏掉;新建/编辑表单文案与 `taskboard_create` 工具描述同步更新
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.3";
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", {
@@ -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
  });