@klarkxy/dsh-session-manager 0.3.2 → 0.3.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/lib/client.js CHANGED
@@ -21,7 +21,9 @@ const reasons = {
21
21
  stale: "这个聊天已经更新,请再试一次。",
22
22
  running: "这个聊天仍在运行。请等待当前工作结束后再试。",
23
23
  cleanup: "清理没有完成。",
24
- unsupported: "当前版本不能完成这个操作。"
24
+ unsupported: "当前版本不能完成这个操作。",
25
+ retryPending: "重发尚未完成。再次发送会继续同一次操作,不会新建另一份聊天。",
26
+ retryUncertain: "上次创建聊天的结果无法确认,已停止再次创建。请先检查会话列表中是否已有新聊天。"
25
27
  },
26
28
  en: {
27
29
  modified: "Later operations may have made changes. Retry is unavailable.",
@@ -30,7 +32,9 @@ const reasons = {
30
32
  stale: "This chat has changed. Try again.",
31
33
  running: "This chat is still running. Wait for the current work to finish, then try again.",
32
34
  cleanup: "Cleanup did not finish.",
33
- unsupported: "This runtime cannot complete that action."
35
+ unsupported: "This runtime cannot complete that action.",
36
+ retryPending: "Resend is incomplete. Send again to continue the same operation without creating another chat.",
37
+ retryUncertain: "The previous chat creation could not be confirmed. Another chat will not be created; check the session list for an existing new chat."
34
38
  }
35
39
  };
36
40
  const text = {
@@ -48,6 +52,12 @@ const text = {
48
52
  edit: "编辑并重发",
49
53
  send: "发送",
50
54
  editTitle: "编辑并重发",
55
+ editBranchWarning: "对话将从这条消息继续,之前的版本会移入归档。已执行的文件、命令和外部修改不会撤销,模型可能再次执行相关操作。",
56
+ editConfirm: "确认重发",
57
+ editAddAttachment: "添加附件",
58
+ editRemoveAttachment: "删除附件",
59
+ editImage: "图片",
60
+ editPreviewFailed: "预览不可用",
51
61
  draft: "消息文本",
52
62
  continueDefault: "继续",
53
63
  welcomeBack: "欢迎回来",
@@ -78,6 +88,20 @@ const text = {
78
88
  compactEnabledLabel: "建议压缩上下文",
79
89
  compactEnabledHint: "长时间离开后,建议先压缩上下文。",
80
90
  compactMinutesLabel: "离开多久后显示建议",
91
+ autoContinue: "自动继续",
92
+ autoContinueOff: "关闭",
93
+ autoContinueCautious: "谨慎",
94
+ autoContinueBold: "奔放",
95
+ autoContinueTooltip: "回复提前结束时自动发送继续。谨慎:有明确依据才继续;奔放:没完成就继续,低风险的选择按推荐方案走。会话开始后锁定。",
96
+ autoContinueLocked: "请在会话开始前选择自动继续",
97
+ autoContinueStale: "状态已在别处更改,已刷新",
98
+ autoContinueFailed: "切换失败",
99
+ autoContinueGroup: "自动继续",
100
+ autoContinueDefaultLabel: "新会话默认形态",
101
+ autoContinueDefaultHint: "只影响之后新建的会话;发送第一条消息前可在输入框上方单独选择,会话开始后保持锁定。",
102
+ autoContinueMaxLabel: "续跑次数上限",
103
+ autoContinueMaxHint: "你每发一条消息之后,最多自动继续的次数(1–10)。",
104
+ autoContinueMaxInvalid: "请填写 1–10 的整数。",
81
105
  minutesUnit: "分钟",
82
106
  modelLabel: "摘要模型",
83
107
  modelFollowOption: "当前会话 / 宿主默认模型",
@@ -104,6 +128,12 @@ const text = {
104
128
  edit: "Edit and resend",
105
129
  send: "Send",
106
130
  editTitle: "Edit and resend",
131
+ editBranchWarning: "The conversation continues from this message; the previous version moves to the archive. File, command, and external changes already made are not undone — the model may perform those actions again.",
132
+ editConfirm: "Confirm resend",
133
+ editAddAttachment: "Add attachments",
134
+ editRemoveAttachment: "Remove attachment",
135
+ editImage: "Image",
136
+ editPreviewFailed: "Preview unavailable",
107
137
  draft: "Message text",
108
138
  continueDefault: "Continue",
109
139
  welcomeBack: "Welcome back",
@@ -134,6 +164,20 @@ const text = {
134
164
  compactEnabledLabel: "Suggest context compaction",
135
165
  compactEnabledHint: "Suggest compacting the context after a long time away.",
136
166
  compactMinutesLabel: "Show suggestion after",
167
+ autoContinue: "Auto-continue",
168
+ autoContinueOff: "Off",
169
+ autoContinueCautious: "Cautious",
170
+ autoContinueBold: "Bold",
171
+ autoContinueTooltip: "Sends a follow-up when a reply stops early. Cautious: continues only on clear evidence. Bold: continues unless done, taking the recommended option on low-risk choices. Locked once the session starts.",
172
+ autoContinueLocked: "Choose Auto-continue before the session starts",
173
+ autoContinueStale: "State changed elsewhere; refreshed",
174
+ autoContinueFailed: "Could not change Auto-continue",
175
+ autoContinueGroup: "Auto-continue",
176
+ autoContinueDefaultLabel: "Default for new sessions",
177
+ autoContinueDefaultHint: "Only affects sessions created later. Choose above the composer before sending the first message; the choice stays locked once the session starts.",
178
+ autoContinueMaxLabel: "Maximum continuations",
179
+ autoContinueMaxHint: "How many times it may continue automatically after each message you send (1–10).",
180
+ autoContinueMaxInvalid: "Enter a whole number from 1 to 10.",
137
181
  minutesUnit: "minutes",
138
182
  modelLabel: "Recap model",
139
183
  modelFollowOption: "Current conversation / host default",
@@ -151,7 +195,9 @@ function uiText(locale, key) {
151
195
  return text[locale][key];
152
196
  }
153
197
  function failureReason(failure) {
154
- if (failure.code === "session/lifecycle-unavailable" || failure.code === "session/fork-unavailable" || failure.code === "session-manager/unavailable") return "unsupported";
198
+ if (failure.code === "session/retry-pending") return "retryPending";
199
+ if (failure.code === "session/retry-uncertain") return "retryUncertain";
200
+ if (failure.code === "session/lifecycle-unavailable" || failure.code === "session/retry-media-unavailable" || failure.code === "session-manager/unavailable") return "unsupported";
155
201
  if (failure.code === "session-manager/busy") return "running";
156
202
  if (failure.code === "session/retry-revision" || failure.code === "session/branch-moved" || failure.code === "session/retry-input" || failure.code === "session/retry-conflict") return "stale";
157
203
  if (failure.code === "session/delete-pending") return "cleanup";
@@ -189,11 +235,15 @@ const DELETE_DIALOG_ID = "@klarkxy/dsh-session-manager.delete-chat";
189
235
  const WELCOME_BACK_PREPARE_ENDPOINT = "welcomeback.prepare";
190
236
  const WELCOME_BACK_COMPACT_ENDPOINT = "welcomeback.compact";
191
237
  const CONTINUE_SUGGEST_ENDPOINT = "continue.suggest";
238
+ const EDIT_PREPARE_ENDPOINT = "edit.prepare";
239
+ const EDIT_FILE_ENDPOINT = "edit.file";
192
240
  const CONFIG_READ_ENDPOINT = "config.read";
193
241
  const CONFIG_WRITE_ENDPOINT = "config.write";
242
+ const AUTO_CONTINUE_STATUS_ENDPOINT = "autocontinue.status";
243
+ const AUTO_CONTINUE_UPDATE_ENDPOINT = "autocontinue.update";
194
244
  /** Card entry id in the chat flow; the recap renders at the latest completed turn's tail. */
195
245
  const WELCOME_BACK_CARD_ID = "@klarkxy/dsh-session-manager.welcome-back";
196
- const WELCOME_BACK_CARD_SLOT = "conversation.chat.turnTail";
246
+ const TURN_TAIL_SLOT = "conversation.chat.turnTail";
197
247
  const CONTINUE_GHOST_ID = "@klarkxy/dsh-session-manager.continue-ghost";
198
248
  const CONTINUE_GHOST_SLOT = "conversation.input.overlay";
199
249
  //#endregion
@@ -1660,10 +1710,117 @@ ${modelMenuCss}
1660
1710
  .${FREE_CHAT_ROOT} .dsh-free-actions {
1661
1711
  display: inline-flex;
1662
1712
  align-items: center;
1663
- gap: 4px;
1713
+ gap: 8px;
1664
1714
  min-width: 0;
1665
1715
  }
1666
1716
  .${FREE_CHAT_ROOT} .dsh-free-alert { margin: 0; }
1717
+ .${FREE_CHAT_ROOT} .dsh-free-warning {
1718
+ margin: 0 0 12px;
1719
+ color: var(--dsw-alias-label-secondary);
1720
+ }
1721
+ .${FREE_CHAT_ROOT}.dsm-user-row {
1722
+ display: flex;
1723
+ flex-direction: column;
1724
+ align-items: flex-end;
1725
+ gap: 6px;
1726
+ }
1727
+ .${FREE_CHAT_ROOT} .dsm-user-stack {
1728
+ display: flex;
1729
+ flex-direction: column;
1730
+ align-items: flex-end;
1731
+ gap: 8px;
1732
+ min-width: 0;
1733
+ max-width: 82%;
1734
+ }
1735
+ .${FREE_CHAT_ROOT} .dsm-user-bubble {
1736
+ max-width: 100%;
1737
+ padding: 10px 16px;
1738
+ border-radius: var(--dsw-radius-md);
1739
+ background: var(--dsw-alias-bg-layer-3);
1740
+ color: var(--dsw-alias-label-primary);
1741
+ font-size: 14px;
1742
+ line-height: 22px;
1743
+ white-space: pre-wrap;
1744
+ word-break: break-word;
1745
+ }
1746
+ .${FREE_CHAT_ROOT} .dsm-user-chrome {
1747
+ display: flex;
1748
+ align-items: center;
1749
+ gap: 8px;
1750
+ min-height: 28px;
1751
+ color: var(--dsw-alias-label-tertiary);
1752
+ }
1753
+ .${FREE_CHAT_ROOT} .dsm-user-time {
1754
+ font-size: 13px;
1755
+ white-space: nowrap;
1756
+ }
1757
+ .${FREE_CHAT_ROOT} .dsm-icon-action {
1758
+ display: inline-flex;
1759
+ align-items: center;
1760
+ justify-content: center;
1761
+ width: 28px;
1762
+ height: 28px;
1763
+ padding: 6px;
1764
+ border: none;
1765
+ border-radius: var(--dsw-radius-sm);
1766
+ background: transparent;
1767
+ color: var(--dsw-alias-label-tertiary);
1768
+ cursor: pointer;
1769
+ }
1770
+ .${FREE_CHAT_ROOT} .dsm-icon-action:hover:not(:disabled) {
1771
+ background: var(--dsw-alias-interactive-bg-hover);
1772
+ color: var(--dsw-alias-label-secondary);
1773
+ }
1774
+ .${FREE_CHAT_ROOT} .dsm-icon-action svg { width: 15px; height: 15px; }
1775
+ .${FREE_CHAT_ROOT} .dsm-user-attachments {
1776
+ display: flex;
1777
+ flex-wrap: wrap;
1778
+ justify-content: flex-end;
1779
+ max-width: 100%;
1780
+ gap: 8px;
1781
+ }
1782
+ .${FREE_CHAT_ROOT} .dsm-file-card {
1783
+ display: inline-flex;
1784
+ flex: 0 0 240px;
1785
+ align-items: center;
1786
+ gap: 10px;
1787
+ box-sizing: border-box;
1788
+ width: 240px;
1789
+ min-height: 64px;
1790
+ padding: 8px 12px;
1791
+ border: 0.5px solid var(--dsw-alias-border-l2);
1792
+ border-radius: var(--dsw-radius-md);
1793
+ background: var(--dsw-alias-bg-layer-3);
1794
+ }
1795
+ .${FREE_CHAT_ROOT} .dsm-file-icon { flex: none; width: 28px; height: 28px; }
1796
+ .${FREE_CHAT_ROOT} .dsm-file-content {
1797
+ display: flex;
1798
+ flex: 1;
1799
+ flex-direction: column;
1800
+ min-width: 0;
1801
+ }
1802
+ .${FREE_CHAT_ROOT} .dsm-file-name {
1803
+ overflow: hidden;
1804
+ color: var(--dsw-alias-label-primary);
1805
+ font-size: 14px;
1806
+ font-weight: 500;
1807
+ line-height: 22px;
1808
+ white-space: nowrap;
1809
+ text-overflow: ellipsis;
1810
+ }
1811
+ .${FREE_CHAT_ROOT} .dsm-file-meta {
1812
+ overflow: hidden;
1813
+ color: var(--dsw-alias-label-tertiary);
1814
+ font-size: 12px;
1815
+ line-height: 15px;
1816
+ white-space: nowrap;
1817
+ text-overflow: ellipsis;
1818
+ }
1819
+ .${FREE_CHAT_ROOT} .dsm-reference-summary {
1820
+ color: var(--dsw-alias-label-tertiary);
1821
+ font-size: 13px;
1822
+ line-height: 18px;
1823
+ }
1667
1824
  /* Settings page: one quiet reading surface. Rules, not nested wells, separate
1668
1825
  * sections; dependent controls align with the feature they belong to. */
1669
1826
  .${FREE_CHAT_ROOT} .dsm-settings {
@@ -1803,6 +1960,66 @@ ${modelMenuCss}
1803
1960
  color: var(--dsw-alias-label-secondary);
1804
1961
  }
1805
1962
  .${FREE_CHAT_ROOT} .dsh-welcome-done { margin: 6px 0 0; }
1963
+
1964
+ /* Hero-row Auto-continue chip: icon + mode label, tinted while a mode is on. */
1965
+ .${FREE_CHAT_ROOT}.smac-option {
1966
+ display: inline-flex;
1967
+ align-items: center;
1968
+ gap: 6px;
1969
+ }
1970
+ .${FREE_CHAT_ROOT} .smac-chip {
1971
+ display: inline-flex;
1972
+ align-items: center;
1973
+ gap: 4px;
1974
+ height: 28px;
1975
+ padding: 0 10px;
1976
+ border-radius: 999px;
1977
+ font-size: 13px;
1978
+ line-height: 20px;
1979
+ border: 1px solid var(--dsw-alias-border-l3);
1980
+ background: transparent;
1981
+ color: var(--dsw-alias-label-secondary);
1982
+ cursor: pointer;
1983
+ }
1984
+ .${FREE_CHAT_ROOT} .smac-chip svg { width: 16px; height: 16px; }
1985
+ .${FREE_CHAT_ROOT} .smac-chip .smac-chevron { width: 12px; height: 12px; }
1986
+ .${FREE_CHAT_ROOT} .smac-chip:hover:not(:disabled) {
1987
+ background: var(--dsw-alias-interactive-bg-hover);
1988
+ }
1989
+ .${FREE_CHAT_ROOT} .smac-chip.smac-chip-on {
1990
+ background: var(--dsw-alias-button-primary-fill);
1991
+ border-color: var(--dsw-alias-button-primary-fill);
1992
+ color: var(--dsw-alias-label-primary-foreground);
1993
+ }
1994
+ .${FREE_CHAT_ROOT} .smac-chip.smac-chip-on:hover:not(:disabled) {
1995
+ background: var(--dsw-alias-button-primary-hover);
1996
+ border-color: var(--dsw-alias-button-primary-hover);
1997
+ }
1998
+ .${FREE_CHAT_ROOT} .smac-chip:disabled {
1999
+ opacity: 0.5;
2000
+ cursor: default;
2001
+ }
2002
+ .${FREE_CHAT_ROOT} .smac-note {
2003
+ font-size: 12px;
2004
+ line-height: 18px;
2005
+ color: var(--dsw-alias-label-tertiary);
2006
+ }
2007
+
2008
+ .${FREE_CHAT_ROOT} .dsm-user-original { display: contents; }
2009
+ .${FREE_CHAT_ROOT} .dsm-user-original[hidden] { display: none; }
2010
+ .${FREE_CHAT_ROOT} .dsm-message-edit { width: min(100%, 720px); align-self: flex-end; }
2011
+ .${FREE_CHAT_ROOT} .dsm-message-editor { width: 100%; box-sizing: border-box; padding: 14px; border-radius: 18px; background: var(--dsw-alias-bg-layer-3); }
2012
+ .${FREE_CHAT_ROOT} .dsm-message-editor .dsh-free-draft { width: 100%; min-height: 80px; max-height: 360px; resize: vertical; border: none; background: transparent; box-shadow: none; padding: 4px; box-sizing: border-box; }
2013
+ .${FREE_CHAT_ROOT} .dsm-edit-footer { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
2014
+ .${FREE_CHAT_ROOT} .dsm-edit-spacer { flex: 1; }
2015
+ .${FREE_CHAT_ROOT} .dsm-edit-attachments { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
2016
+ .${FREE_CHAT_ROOT} .dsm-edit-attachment { display: flex; align-items: center; gap: 6px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; padding: 6px; max-width: 100%; }
2017
+ .${FREE_CHAT_ROOT} .dsm-edit-attachment img { width: 72px; height: 60px; object-fit: contain; border-radius: 6px; }
2018
+ .${FREE_CHAT_ROOT} .dsm-edit-attachment > span { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2019
+ .${FREE_CHAT_ROOT} .dsm-edit-attachment > svg { width: 24px; height: 24px; }
2020
+ .${FREE_CHAT_ROOT} .dsm-edit-image { position: relative; padding: 0; border: none; }
2021
+ .${FREE_CHAT_ROOT} .dsm-edit-image img { width: auto; height: auto; max-width: 220px; max-height: 140px; object-fit: contain; border-radius: 10px; }
2022
+ .${FREE_CHAT_ROOT} .dsm-edit-image > button { position: absolute; top: 4px; right: 4px; border-radius: 50%; background: var(--dsw-alias-bg-layer-3); width: 24px; height: 24px; padding: 0; }
1806
2023
  `;
1807
2024
  //#endregion
1808
2025
  //#region src/free-panel.tsx
@@ -2189,11 +2406,15 @@ function bindTemporaryWorkspaceHost(ctx) {
2189
2406
  //#region src/session-actions.ts
2190
2407
  const UNSUPPORTED_CAPABILITIES = {
2191
2408
  retry: false,
2409
+ retryModified: false,
2192
2410
  freeSession: false,
2193
2411
  deleteSession: false
2194
2412
  };
2195
2413
  function isRecoverable(failure) {
2196
- return failure.code === "transport" || failure.code === "session/delete-pending";
2414
+ return failure.code === "transport" || failure.code === "session/delete-pending" || failure.code === "session/retry-pending" || failure.code === "session/retry-uncertain";
2415
+ }
2416
+ function isStaleRetryFailure(failure) {
2417
+ return failure?.code === "session/retry-revision" || failure?.code === "session/branch-moved" || failure?.code === "session/retry-input" || failure?.code === "session/retry-conflict";
2197
2418
  }
2198
2419
  function readFailure(error) {
2199
2420
  if (isFailure(error)) return {
@@ -2254,6 +2475,8 @@ async function readCapabilities(host) {
2254
2475
  const record = value;
2255
2476
  return {
2256
2477
  retry: record.retry === true,
2478
+ retryModified: record.retryModified === true,
2479
+ ...record.retryContent === true ? { retryContent: true } : {},
2257
2480
  freeSession: record.freeSession === true,
2258
2481
  deleteSession: record.deleteSession === true
2259
2482
  };
@@ -2286,19 +2509,16 @@ async function deleteOwnedSession(host, sessionId, requestId) {
2286
2509
  if (phase !== "deleted" && phase !== "cleanup-pending") throw new HostOperationError("session/delete-pending", "missing-outcome");
2287
2510
  return { phase };
2288
2511
  }
2289
- function directRetryRequest(input) {
2512
+ /** Build the body for one edited prompt; the host keeps the original attachments when only text changes. */
2513
+ function editRetryRequest(input) {
2290
2514
  return {
2291
2515
  sessionId: input.sessionId,
2292
2516
  messageId: input.messageId,
2293
2517
  expectedRevision: input.expectedRevision,
2294
2518
  expectedInputRevision: input.expectedInputRevision,
2295
- requestId: input.requestId
2296
- };
2297
- }
2298
- function editRetryRequest(input) {
2299
- return {
2300
- ...directRetryRequest(input),
2301
- text: input.text
2519
+ requestId: input.requestId,
2520
+ ...input.content === void 0 ? { text: input.text } : { content: input.content },
2521
+ ...input.allowModified === true ? { allowModified: true } : {}
2302
2522
  };
2303
2523
  }
2304
2524
  var SubmissionLedger = class {
@@ -2483,6 +2703,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2483
2703
  let ready = false;
2484
2704
  let busy = false;
2485
2705
  let editing = false;
2706
+ let modified = false;
2707
+ let editNeedsPreview = false;
2486
2708
  let draft = "";
2487
2709
  let failure;
2488
2710
  let expectedRevision = 0;
@@ -2494,8 +2716,10 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2494
2716
  function build() {
2495
2717
  return {
2496
2718
  controlsRetry: controlsFor(capabilities, ready, host).retry,
2719
+ controlsContent: ready && capabilities.retryContent === true,
2497
2720
  busy,
2498
2721
  editing,
2722
+ modified,
2499
2723
  draft,
2500
2724
  sessionId: ownerSession,
2501
2725
  messageId: ownerMessage,
@@ -2511,7 +2735,7 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2511
2735
  const record = value;
2512
2736
  const inputRevision = typeof record.inputRevision === "string" ? record.inputRevision : "";
2513
2737
  const eligible = record.eligible === true;
2514
- if (eligible && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
2738
+ if ((eligible || record.reason === "modified") && inputRevision === "") throw new HostOperationError("session/lifecycle-unavailable", "input-revision");
2515
2739
  return {
2516
2740
  branchRevision: record.branchRevision,
2517
2741
  inputRevision,
@@ -2549,7 +2773,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2549
2773
  await ledger.replay(async (body) => {
2550
2774
  await callRetry(body, signal, ticket);
2551
2775
  });
2552
- if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId) editing = false;
2776
+ if (pending !== void 0 && ticket === call && !signal.aborted && ownerSession === pending.sessionId && ownerMessage === pending.messageId) {
2777
+ editing = false;
2778
+ modified = false;
2779
+ editNeedsPreview = false;
2780
+ }
2553
2781
  } catch (error) {
2554
2782
  if (ticket === call && !signal.aborted) failure = readFailure(error);
2555
2783
  } finally {
@@ -2568,8 +2796,9 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2568
2796
  ready = true;
2569
2797
  publish();
2570
2798
  },
2571
- async retry(sessionId, id, signal) {
2799
+ async beginEdit(sessionId, id, text, signal) {
2572
2800
  if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
2801
+ if (editing && (ownerSession !== sessionId || ownerMessage !== id)) return;
2573
2802
  if (ledger.pending() !== void 0) {
2574
2803
  if (!samePending(sessionId, id)) return;
2575
2804
  await replaySame(signal);
@@ -2578,63 +2807,25 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2578
2807
  const ticket = ++call;
2579
2808
  busy = true;
2580
2809
  failure = void 0;
2581
- editing = false;
2810
+ modified = false;
2811
+ editNeedsPreview = false;
2582
2812
  ownerSession = sessionId;
2583
2813
  ownerMessage = id;
2584
2814
  publish();
2585
2815
  try {
2586
2816
  const seen = await preview(sessionId, id, signal, ticket);
2587
2817
  if (seen === void 0) return;
2588
- if (!seen.eligible) {
2589
- failure = {
2590
- code: "session/retry-blocked",
2591
- reason: seen.reason ?? "unknown"
2592
- };
2593
- return;
2594
- }
2595
2818
  if (signal.aborted || ticket !== call) return;
2596
2819
  expectedRevision = seen.branchRevision;
2597
2820
  expectedInputRevision = seen.inputRevision;
2598
- const body = directRetryRequest({
2599
- sessionId,
2600
- messageId: id,
2601
- expectedRevision: seen.branchRevision,
2602
- expectedInputRevision: seen.inputRevision,
2603
- requestId: ids()
2604
- });
2605
- await ledger.submit(body, () => callRetry(body, signal, ticket));
2606
- } catch (error) {
2607
- if (ticket === call && !signal.aborted) failure = readFailure(error);
2608
- } finally {
2609
- settle();
2610
- }
2611
- },
2612
- async beginEdit(sessionId, id, text, signal) {
2613
- if (busy || !controlsFor(capabilities, ready, host).retry || signal.aborted) return;
2614
- if (ledger.pending() !== void 0) {
2615
- if (!samePending(sessionId, id)) return;
2616
- await replaySame(signal);
2617
- return;
2618
- }
2619
- const ticket = ++call;
2620
- busy = true;
2621
- failure = void 0;
2622
- ownerSession = sessionId;
2623
- ownerMessage = id;
2624
- publish();
2625
- try {
2626
- const seen = await preview(sessionId, id, signal, ticket);
2627
- if (seen === void 0) return;
2628
- if (!seen.eligible) {
2821
+ if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
2629
2822
  failure = {
2630
2823
  code: "session/retry-blocked",
2631
2824
  reason: seen.reason ?? "unknown"
2632
2825
  };
2633
2826
  return;
2634
2827
  }
2635
- if (signal.aborted || ticket !== call) return;
2636
- expectedRevision = seen.branchRevision;
2637
- expectedInputRevision = seen.inputRevision;
2828
+ modified = !seen.eligible;
2638
2829
  draft = text;
2639
2830
  editing = true;
2640
2831
  } catch (error) {
@@ -2651,9 +2842,11 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2651
2842
  cancelEdit() {
2652
2843
  if (busy) return;
2653
2844
  editing = false;
2845
+ modified = false;
2846
+ editNeedsPreview = false;
2654
2847
  publish();
2655
2848
  },
2656
- async sendEdit(sessionId, id, signal) {
2849
+ async sendEdit(sessionId, id, signal, content, confirmed) {
2657
2850
  if (signal.aborted || busy || !controlsFor(capabilities, ready, host).retry) return;
2658
2851
  if (!editing || ownerSession !== sessionId || ownerMessage !== id) return;
2659
2852
  if (ledger.pending() !== void 0) {
@@ -2663,23 +2856,49 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2663
2856
  }
2664
2857
  const ticket = ++call;
2665
2858
  busy = true;
2859
+ const refresh = editNeedsPreview || isStaleRetryFailure(failure);
2666
2860
  failure = void 0;
2667
2861
  const text = draft;
2668
2862
  publish();
2669
2863
  try {
2670
2864
  if (signal.aborted || ticket !== call) return;
2865
+ if (refresh) {
2866
+ editNeedsPreview = true;
2867
+ const seen = await preview(sessionId, id, signal, ticket);
2868
+ if (seen === void 0) return;
2869
+ expectedRevision = seen.branchRevision;
2870
+ expectedInputRevision = seen.inputRevision;
2871
+ if (!seen.eligible && (seen.reason !== "modified" || !capabilities.retryModified)) {
2872
+ failure = {
2873
+ code: "session/retry-blocked",
2874
+ reason: seen.reason ?? "unknown"
2875
+ };
2876
+ return;
2877
+ }
2878
+ modified = !seen.eligible;
2879
+ editNeedsPreview = false;
2880
+ }
2881
+ if (content !== void 0 && capabilities.retryContent !== true) throw new HostOperationError("session/lifecycle-unavailable");
2671
2882
  const body = editRetryRequest({
2672
2883
  sessionId,
2673
2884
  messageId: id,
2674
2885
  expectedRevision,
2675
2886
  expectedInputRevision,
2676
2887
  requestId: ids(),
2677
- text
2888
+ text,
2889
+ ...content === void 0 ? {} : { content },
2890
+ allowModified: confirmed === true
2678
2891
  });
2679
2892
  await ledger.submit(body, () => callRetry(body, signal, ticket));
2680
- if (ticket === call && !signal.aborted) editing = false;
2893
+ if (ticket === call && !signal.aborted) {
2894
+ editing = false;
2895
+ modified = false;
2896
+ editNeedsPreview = false;
2897
+ }
2681
2898
  } catch (error) {
2682
- if (ticket === call && !signal.aborted) failure = readFailure(error);
2899
+ const nextFailure = readFailure(error);
2900
+ if (isStaleRetryFailure(nextFailure)) editNeedsPreview = true;
2901
+ if (ticket === call && !signal.aborted) failure = nextFailure;
2683
2902
  } finally {
2684
2903
  settle();
2685
2904
  }
@@ -2688,6 +2907,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2688
2907
  if (ownerSession !== sessionId || ownerMessage !== messageId) return;
2689
2908
  call += 1;
2690
2909
  editing = false;
2910
+ modified = false;
2911
+ editNeedsPreview = false;
2691
2912
  failure = void 0;
2692
2913
  draft = "";
2693
2914
  if (!ledger.busy) busy = false;
@@ -2696,6 +2917,8 @@ function createRetryModel(host, ids = () => crypto.randomUUID()) {
2696
2917
  cancel() {
2697
2918
  call += 1;
2698
2919
  editing = false;
2920
+ modified = false;
2921
+ editNeedsPreview = false;
2699
2922
  failure = void 0;
2700
2923
  if (!ledger.busy) busy = false;
2701
2924
  publish();
@@ -3192,6 +3415,175 @@ function createWelcomeBackModel(host, ids = () => crypto.randomUUID(), minInterv
3192
3415
  };
3193
3416
  }
3194
3417
  //#endregion
3418
+ //#region src/auto-continue-option.tsx
3419
+ /**
3420
+ * The hero-row Auto-continue option: a chip that opens a three-mode menu on a
3421
+ * blank session and locks once the session starts. Mirrors the delegation
3422
+ * chip's revision discipline: stale reads never roll the chip back, a 'stale'
3423
+ * rejection refetches, and 'session-locked' explains itself in the tooltip.
3424
+ */
3425
+ var RpcError = class extends Error {
3426
+ code;
3427
+ constructor(code, message) {
3428
+ super(message);
3429
+ this.name = "RpcError";
3430
+ this.code = code;
3431
+ }
3432
+ };
3433
+ async function rpc(call, endpoint, payload) {
3434
+ const result = await call(endpoint, payload);
3435
+ if (!result || result.ok !== true) {
3436
+ const failed = result && "error" in result ? result.error : void 0;
3437
+ throw new RpcError(failed?.code ?? "unknown", failed?.message ?? "");
3438
+ }
3439
+ return result.value;
3440
+ }
3441
+ const MODES = [
3442
+ "off",
3443
+ "cautious",
3444
+ "bold"
3445
+ ];
3446
+ function AutoContinueOption({ sessionId, call, subscribeGeneration, useSession, locale }) {
3447
+ const [status, setStatus] = (0, react.useState)();
3448
+ const [saving, setSaving] = (0, react.useState)(false);
3449
+ const [notice, setNotice] = (0, react.useState)("");
3450
+ const [open, setOpen] = (0, react.useState)(false);
3451
+ const generationRef = (0, react.useRef)(0);
3452
+ const readSeqRef = (0, react.useRef)(0);
3453
+ const openState = useSession?.((snapshot) => snapshot.openState);
3454
+ const sessionRunning = useSession?.((snapshot) => snapshot.running === true);
3455
+ const sessionBlank = useSession?.((snapshot) => snapshot.blank);
3456
+ const submitting = useSession?.((snapshot) => snapshot.awaitingFirstTurn === true || (snapshot.pendingSubmissions?.length ?? 0) > 0);
3457
+ const load = (0, react.useCallback)(async () => {
3458
+ const generation = generationRef.current;
3459
+ const seq = ++readSeqRef.current;
3460
+ try {
3461
+ const next = await rpc(call, AUTO_CONTINUE_STATUS_ENDPOINT, { sessionId });
3462
+ if (generationRef.current !== generation || seq !== readSeqRef.current) return;
3463
+ setStatus((prev) => prev && prev.sessionId === next.sessionId && next.revision < prev.revision ? prev : next);
3464
+ } catch {}
3465
+ }, [call, sessionId]);
3466
+ (0, react.useEffect)(() => {
3467
+ generationRef.current += 1;
3468
+ readSeqRef.current += 1;
3469
+ setStatus(void 0);
3470
+ setSaving(false);
3471
+ setNotice("");
3472
+ setOpen(false);
3473
+ }, [sessionId]);
3474
+ (0, react.useEffect)(() => {
3475
+ load();
3476
+ }, [
3477
+ load,
3478
+ openState,
3479
+ sessionRunning,
3480
+ sessionBlank
3481
+ ]);
3482
+ (0, react.useEffect)(() => {
3483
+ const off = subscribeGeneration?.(() => {
3484
+ load();
3485
+ });
3486
+ const win = typeof window === "undefined" ? void 0 : window;
3487
+ const onFocus = () => {
3488
+ load();
3489
+ };
3490
+ win?.addEventListener("focus", onFocus);
3491
+ return () => {
3492
+ off?.();
3493
+ win?.removeEventListener("focus", onFocus);
3494
+ };
3495
+ }, [load, subscribeGeneration]);
3496
+ (0, react.useEffect)(() => {
3497
+ if (!notice) return;
3498
+ const timer = setTimeout(() => setNotice(""), 4e3);
3499
+ return () => clearTimeout(timer);
3500
+ }, [notice]);
3501
+ const choose = (0, react.useCallback)(async (mode) => {
3502
+ if (!status || saving || status.locked || sessionBlank === false || sessionRunning || submitting || mode === status.mode) return;
3503
+ const generation = generationRef.current;
3504
+ readSeqRef.current += 1;
3505
+ setSaving(true);
3506
+ setNotice("");
3507
+ try {
3508
+ const next = await rpc(call, AUTO_CONTINUE_UPDATE_ENDPOINT, {
3509
+ sessionId,
3510
+ mode,
3511
+ expectedRevision: status.revision
3512
+ });
3513
+ if (generationRef.current !== generation) return;
3514
+ setStatus(next);
3515
+ } catch (reason) {
3516
+ if (generationRef.current !== generation) return;
3517
+ if (reason instanceof RpcError && (reason.code === "stale" || reason.code === "session-locked")) {
3518
+ setNotice(reason.code === "stale" ? "autoContinueStale" : "autoContinueLocked");
3519
+ load();
3520
+ } else setNotice("autoContinueFailed");
3521
+ } finally {
3522
+ if (generationRef.current === generation) setSaving(false);
3523
+ }
3524
+ }, [
3525
+ call,
3526
+ load,
3527
+ saving,
3528
+ sessionId,
3529
+ status,
3530
+ sessionBlank,
3531
+ sessionRunning,
3532
+ submitting
3533
+ ]);
3534
+ if (!status || status.role === "executor" || sessionBlank === false) return null;
3535
+ const mode = status.mode;
3536
+ const locked = status.locked || sessionRunning === true || submitting === true;
3537
+ const tooltip = locked ? uiText(locale, "autoContinueLocked") : notice ? uiText(locale, notice) : uiText(locale, "autoContinueTooltip");
3538
+ const modeKey = mode === "cautious" ? "autoContinueCautious" : mode === "bold" ? "autoContinueBold" : "autoContinueOff";
3539
+ const label = `${uiText(locale, "autoContinue")} · ${uiText(locale, modeKey)}`;
3540
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3541
+ className: `${FREE_CHAT_ROOT} smac-option`,
3542
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
3543
+ open,
3544
+ align: "end",
3545
+ portal: true,
3546
+ listClassName: FREE_CHAT_ROOT,
3547
+ selectedId: mode,
3548
+ items: MODES.map((value) => ({
3549
+ id: value,
3550
+ label: uiText(locale, value === "off" ? "autoContinueOff" : value === "cautious" ? "autoContinueCautious" : "autoContinueBold")
3551
+ })),
3552
+ onSelect: (id) => {
3553
+ setOpen(false);
3554
+ choose(id);
3555
+ },
3556
+ onClose: () => setOpen(false),
3557
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
3558
+ label: tooltip,
3559
+ side: "bottom",
3560
+ gap: 4,
3561
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
3562
+ className: `smac-chip${mode !== "off" ? " smac-chip-on" : ""}`,
3563
+ active: mode !== "off",
3564
+ "aria-haspopup": "menu",
3565
+ "aria-expanded": open,
3566
+ "aria-label": label,
3567
+ disabled: locked || saving,
3568
+ onClick: () => setOpen((current) => !current),
3569
+ children: [
3570
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {}),
3571
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3572
+ className: "smac-chip-label",
3573
+ children: label
3574
+ }),
3575
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: "smac-chevron" })
3576
+ ]
3577
+ })
3578
+ })
3579
+ }), notice ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3580
+ role: "status",
3581
+ className: "smac-note",
3582
+ children: uiText(locale, notice)
3583
+ }) : null]
3584
+ });
3585
+ }
3586
+ //#endregion
3195
3587
  //#region ../dsh-plugin-kit/src/contracts.ts
3196
3588
  /** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
3197
3589
  function modelSettingsChangesIdentity(packageName) {
@@ -3618,6 +4010,8 @@ function toDraft(values) {
3618
4010
  compactMinutes: String(compactEnabled ? values.compactMinutes : 60),
3619
4011
  continueText: values.continueText,
3620
4012
  continueMode: values.continueMode,
4013
+ autoContinueMode: values.autoContinueMode,
4014
+ autoContinueMax: String(values.autoContinueMax),
3621
4015
  model: values.model === null ? "" : modelRouteKey(values.model.provider, values.model.model),
3622
4016
  ghostModel: values.ghostModel === null ? "" : modelRouteKey(values.ghostModel.provider, values.ghostModel.model)
3623
4017
  };
@@ -3628,8 +4022,14 @@ function minutesOf(text) {
3628
4022
  const value = Number.parseInt(text.trim(), 10);
3629
4023
  return Number.isSafeInteger(value) && value >= 1 ? value : void 0;
3630
4024
  }
4025
+ /** Automatic continuations per human message: a whole number from 1 to 10. */
4026
+ function autoMaxOf(text) {
4027
+ if (!/^\d+$/.test(text.trim())) return void 0;
4028
+ const value = Number.parseInt(text.trim(), 10);
4029
+ return Number.isSafeInteger(value) && value >= 1 && value <= 10 ? value : void 0;
4030
+ }
3631
4031
  function sameDraft(left, right) {
3632
- return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.model === right.model && left.ghostModel === right.ghostModel;
4032
+ return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.autoContinueMode === right.autoContinueMode && left.autoContinueMax === right.autoContinueMax && left.model === right.model && left.ghostModel === right.ghostModel;
3633
4033
  }
3634
4034
  const missingCatalog = async () => {
3635
4035
  throw new Error("Model catalog service unavailable");
@@ -3688,9 +4088,11 @@ function SessionManagerSettings(props) {
3688
4088
  const ghostLabel = selectableChoices.find((choice) => modelRouteKey(choice.provider, choice.model) === draft.ghostModel)?.label ?? copy("modelFollowOption");
3689
4089
  const recapMinutes = draft.recapEnabled ? minutesOf(draft.recapMinutes) : 0;
3690
4090
  const compactMinutes = draft.compactEnabled ? minutesOf(draft.compactMinutes) : 0;
4091
+ const autoContinueMax = autoMaxOf(draft.autoContinueMax);
3691
4092
  const recapInvalid = recapMinutes === void 0;
3692
4093
  const compactInvalid = compactMinutes === void 0;
3693
- const invalid = recapInvalid || compactInvalid;
4094
+ const autoMaxInvalid = autoContinueMax === void 0;
4095
+ const invalid = recapInvalid || compactInvalid || autoMaxInvalid;
3694
4096
  const fixedPanelId = `${tabsId}-fixed-panel`;
3695
4097
  const autoPanelId = `${tabsId}-auto-panel`;
3696
4098
  const save = () => {
@@ -3703,6 +4105,8 @@ function SessionManagerSettings(props) {
3703
4105
  compactMinutes,
3704
4106
  continueText: draft.continueText,
3705
4107
  continueMode: draft.continueMode,
4108
+ autoContinueMode: draft.autoContinueMode,
4109
+ autoContinueMax,
3706
4110
  ...draft.model === baseline.current?.model ? {} : { model: route === void 0 ? null : {
3707
4111
  provider: route.provider,
3708
4112
  model: route.model
@@ -3717,291 +4121,374 @@ function SessionManagerSettings(props) {
3717
4121
  className: `${FREE_CHAT_ROOT} dsh-ui-page`,
3718
4122
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3719
4123
  className: "dsm-settings",
3720
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3721
- className: "dsm-setting",
3722
- "aria-labelledby": `${tabsId}-ghost-heading`,
3723
- children: [
3724
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3725
- className: "dsm-setting-header",
3726
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3727
- id: `${tabsId}-ghost-heading`,
3728
- className: "dsh-ui-title",
3729
- children: copy("settingsMode")
3730
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3731
- className: "dsh-ui-hint",
3732
- children: copy("settingsModeHint")
3733
- })]
3734
- }),
3735
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3736
- className: "dsm-ghost-controls",
3737
- children: [
3738
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3739
- className: "dsh-ui-field",
3740
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3741
- className: "dsh-ui-label",
3742
- children: copy("continueSourceLabel")
3743
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
3744
- label: copy("continueSourceLabel"),
3745
- items: [{
3746
- id: `${tabsId}-fixed`,
3747
- panelId: fixedPanelId,
3748
- value: "fixed",
3749
- label: copy("modeFixed")
3750
- }, {
3751
- id: `${tabsId}-auto`,
3752
- panelId: autoPanelId,
3753
- value: "auto",
3754
- label: copy("modeAuto")
3755
- }],
3756
- value: draft.continueMode,
3757
- onChange: (value) => edit({
3758
- ...draft,
3759
- continueMode: value
3760
- })
3761
- })]
3762
- }),
3763
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3764
- role: "tabpanel",
3765
- id: fixedPanelId,
3766
- "aria-labelledby": `${tabsId}-fixed`,
3767
- hidden: draft.continueMode !== "fixed",
3768
- children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4124
+ children: [
4125
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4126
+ className: "dsm-setting",
4127
+ "aria-labelledby": `${tabsId}-ghost-heading`,
4128
+ children: [
4129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
4130
+ className: "dsm-setting-header",
4131
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
4132
+ id: `${tabsId}-ghost-heading`,
4133
+ className: "dsh-ui-title",
4134
+ children: copy("settingsMode")
4135
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4136
+ className: "dsh-ui-hint",
4137
+ children: copy("settingsModeHint")
4138
+ })]
4139
+ }),
4140
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4141
+ className: "dsm-ghost-controls",
4142
+ children: [
4143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3769
4144
  className: "dsh-ui-field",
3770
4145
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3771
4146
  className: "dsh-ui-label",
3772
- children: copy("continueTextLabel")
3773
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
3774
- className: "dsm-ghost-input",
3775
- value: draft.continueText,
3776
- placeholder: copy("continueTextPlaceholder"),
3777
- onChange: (event) => edit({
3778
- ...draft,
3779
- continueText: event.target.value
3780
- })
3781
- })]
3782
- }) : null
3783
- }),
3784
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3785
- role: "tabpanel",
3786
- id: autoPanelId,
3787
- "aria-labelledby": `${tabsId}-auto`,
3788
- hidden: draft.continueMode !== "auto",
3789
- children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3790
- className: "dsh-ui-field dsm-model-field",
3791
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3792
- className: "dsh-ui-label",
3793
- children: copy("ghostModelLabel")
3794
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
3795
- locale: props.locale,
3796
- open: ghostMenuOpen,
3797
- onOpenChange: setGhostMenuOpen,
3798
- listClassName: FREE_CHAT_ROOT,
3799
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3800
- type: "button",
3801
- className: "dsh-model-menu-trigger",
3802
- "aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
3803
- "aria-haspopup": "menu",
3804
- "aria-expanded": ghostMenuOpen,
3805
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3806
- className: "dsh-model-menu-triggerLabel",
3807
- children: ghostLabel
3808
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
3809
- }),
3810
- choices: selectableChoices,
3811
- selected: ghostRoute,
3812
- onPick: (route) => edit({
3813
- ...draft,
3814
- ghostModel: modelRouteKey(route.provider, route.model)
3815
- }),
3816
- leading: [{
3817
- id: "follow",
3818
- label: copy("modelFollowOption"),
3819
- selected: !ghostRoute
4147
+ children: copy("continueSourceLabel")
4148
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
4149
+ label: copy("continueSourceLabel"),
4150
+ items: [{
4151
+ id: `${tabsId}-fixed`,
4152
+ panelId: fixedPanelId,
4153
+ value: "fixed",
4154
+ label: copy("modeFixed")
4155
+ }, {
4156
+ id: `${tabsId}-auto`,
4157
+ panelId: autoPanelId,
4158
+ value: "auto",
4159
+ label: copy("modeAuto")
3820
4160
  }],
3821
- onPickLeading: () => edit({
4161
+ value: draft.continueMode,
4162
+ onChange: (value) => edit({
3822
4163
  ...draft,
3823
- ghostModel: ""
3824
- }),
3825
- modelValue: ghostLabel
4164
+ continueMode: value
4165
+ })
3826
4166
  })]
3827
- }) : null
3828
- })
3829
- ]
3830
- }),
3831
- draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3832
- className: "dsh-ui-hint",
3833
- children: copy("ghostModelHint")
3834
- }) : null
3835
- ]
3836
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3837
- className: "dsm-setting",
3838
- "aria-labelledby": `${tabsId}-return-heading`,
3839
- children: [
3840
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
4167
+ }),
4168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4169
+ role: "tabpanel",
4170
+ id: fixedPanelId,
4171
+ "aria-labelledby": `${tabsId}-fixed`,
4172
+ hidden: draft.continueMode !== "fixed",
4173
+ children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4174
+ className: "dsh-ui-field",
4175
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4176
+ className: "dsh-ui-label",
4177
+ children: copy("continueTextLabel")
4178
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
4179
+ className: "dsm-ghost-input",
4180
+ value: draft.continueText,
4181
+ placeholder: copy("continueTextPlaceholder"),
4182
+ onChange: (event) => edit({
4183
+ ...draft,
4184
+ continueText: event.target.value
4185
+ })
4186
+ })]
4187
+ }) : null
4188
+ }),
4189
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4190
+ role: "tabpanel",
4191
+ id: autoPanelId,
4192
+ "aria-labelledby": `${tabsId}-auto`,
4193
+ hidden: draft.continueMode !== "auto",
4194
+ children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4195
+ className: "dsh-ui-field dsm-model-field",
4196
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4197
+ className: "dsh-ui-label",
4198
+ children: copy("ghostModelLabel")
4199
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
4200
+ locale: props.locale,
4201
+ open: ghostMenuOpen,
4202
+ onOpenChange: setGhostMenuOpen,
4203
+ listClassName: FREE_CHAT_ROOT,
4204
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4205
+ type: "button",
4206
+ className: "dsh-model-menu-trigger",
4207
+ "aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
4208
+ "aria-haspopup": "menu",
4209
+ "aria-expanded": ghostMenuOpen,
4210
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4211
+ className: "dsh-model-menu-triggerLabel",
4212
+ children: ghostLabel
4213
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
4214
+ }),
4215
+ choices: selectableChoices,
4216
+ selected: ghostRoute,
4217
+ onPick: (route) => edit({
4218
+ ...draft,
4219
+ ghostModel: modelRouteKey(route.provider, route.model)
4220
+ }),
4221
+ leading: [{
4222
+ id: "follow",
4223
+ label: copy("modelFollowOption"),
4224
+ selected: !ghostRoute
4225
+ }],
4226
+ onPickLeading: () => edit({
4227
+ ...draft,
4228
+ ghostModel: ""
4229
+ }),
4230
+ modelValue: ghostLabel
4231
+ })]
4232
+ }) : null
4233
+ })
4234
+ ]
4235
+ }),
4236
+ draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4237
+ className: "dsh-ui-hint",
4238
+ children: copy("ghostModelHint")
4239
+ }) : null
4240
+ ]
4241
+ }),
4242
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4243
+ className: "dsm-setting",
4244
+ "aria-labelledby": `${tabsId}-auto-continue-heading`,
4245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
3841
4246
  className: "dsm-setting-header",
3842
4247
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3843
- id: `${tabsId}-return-heading`,
4248
+ id: `${tabsId}-auto-continue-heading`,
3844
4249
  className: "dsh-ui-title",
3845
- children: copy("welcomeBackGroup")
4250
+ children: copy("autoContinueGroup")
3846
4251
  })
3847
- }),
3848
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3849
- className: "dsm-feature",
3850
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3851
- className: "dsh-ui-toggle-row",
3852
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3853
- className: "dsh-ui-toggle-text",
4252
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4253
+ className: "dsm-feature-options",
4254
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4255
+ className: "dsh-ui-field",
4256
+ children: [
4257
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4258
+ className: "dsh-ui-label",
4259
+ children: copy("autoContinueDefaultLabel")
4260
+ }),
4261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4262
+ className: "dsh-ui-select",
4263
+ value: draft.autoContinueMode,
4264
+ onChange: (event) => edit({
4265
+ ...draft,
4266
+ autoContinueMode: event.target.value
4267
+ }),
4268
+ children: [
4269
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4270
+ value: "off",
4271
+ children: copy("autoContinueOff")
4272
+ }),
4273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4274
+ value: "cautious",
4275
+ children: copy("autoContinueCautious")
4276
+ }),
4277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4278
+ value: "bold",
4279
+ children: copy("autoContinueBold")
4280
+ })
4281
+ ]
4282
+ }),
4283
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4284
+ className: "dsh-ui-hint",
4285
+ children: copy("autoContinueDefaultHint")
4286
+ })
4287
+ ]
4288
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
4289
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4290
+ className: "dsh-ui-field",
3854
4291
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3855
- className: "dsh-ui-toggle-label",
3856
- children: copy("recapEnabledLabel")
4292
+ className: "dsh-ui-label",
4293
+ children: copy("autoContinueMaxLabel")
3857
4294
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3858
- className: "dsh-ui-hint",
3859
- children: copy("recapEnabledHint")
4295
+ className: "dsm-minutes-control",
4296
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
4297
+ className: "dsm-minutes",
4298
+ value: draft.autoContinueMax,
4299
+ inputMode: "numeric",
4300
+ "aria-invalid": autoMaxInvalid || void 0,
4301
+ "aria-describedby": autoMaxInvalid ? `${tabsId}-auto-max-error` : void 0,
4302
+ onChange: (event) => edit({
4303
+ ...draft,
4304
+ autoContinueMax: event.target.value
4305
+ })
4306
+ })
3860
4307
  })]
3861
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
3862
- label: copy("recapEnabledLabel"),
3863
- checked: draft.recapEnabled,
3864
- onChange: (next) => edit({
3865
- ...draft,
3866
- recapEnabled: next
3867
- })
3868
- })]
3869
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3870
- className: "dsm-feature-options",
3871
- "data-disabled": draft.recapEnabled ? void 0 : "true",
4308
+ }),
4309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4310
+ className: "dsh-ui-hint",
4311
+ children: copy("autoContinueMaxHint")
4312
+ }),
4313
+ autoMaxInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4314
+ id: `${tabsId}-auto-max-error`,
4315
+ className: "dsh-ui-error",
4316
+ role: "alert",
4317
+ children: copy("autoContinueMaxInvalid")
4318
+ }) : null
4319
+ ] })]
4320
+ })]
4321
+ }),
4322
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4323
+ className: "dsm-setting",
4324
+ "aria-labelledby": `${tabsId}-return-heading`,
4325
+ children: [
4326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
4327
+ className: "dsm-setting-header",
4328
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
4329
+ id: `${tabsId}-return-heading`,
4330
+ className: "dsh-ui-title",
4331
+ children: copy("welcomeBackGroup")
4332
+ })
4333
+ }),
4334
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4335
+ className: "dsm-feature",
3872
4336
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3873
- className: "dsh-ui-fields-grid",
3874
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3875
- className: "dsh-ui-field",
4337
+ className: "dsh-ui-toggle-row",
4338
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4339
+ className: "dsh-ui-toggle-text",
3876
4340
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3877
- className: "dsh-ui-label",
3878
- children: copy("recapMinutesLabel")
3879
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3880
- className: "dsm-minutes-control",
3881
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
4341
+ className: "dsh-ui-toggle-label",
4342
+ children: copy("recapEnabledLabel")
4343
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4344
+ className: "dsh-ui-hint",
4345
+ children: copy("recapEnabledHint")
4346
+ })]
4347
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4348
+ label: copy("recapEnabledLabel"),
4349
+ checked: draft.recapEnabled,
4350
+ onChange: (next) => edit({
4351
+ ...draft,
4352
+ recapEnabled: next
4353
+ })
4354
+ })]
4355
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4356
+ className: "dsm-feature-options",
4357
+ "data-disabled": draft.recapEnabled ? void 0 : "true",
4358
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4359
+ className: "dsh-ui-fields-grid",
4360
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4361
+ className: "dsh-ui-field",
4362
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4363
+ className: "dsh-ui-label",
4364
+ children: copy("recapMinutesLabel")
4365
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4366
+ className: "dsm-minutes-control",
4367
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
4368
+ className: "dsm-minutes",
4369
+ value: draft.recapMinutes,
4370
+ inputMode: "numeric",
4371
+ disabled: !draft.recapEnabled,
4372
+ "aria-invalid": recapInvalid || void 0,
4373
+ "aria-describedby": recapInvalid ? `${tabsId}-recap-error` : void 0,
4374
+ onChange: (event) => edit({
4375
+ ...draft,
4376
+ recapMinutes: event.target.value
4377
+ })
4378
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4379
+ className: "dsh-ui-meta",
4380
+ children: copy("minutesUnit")
4381
+ })]
4382
+ })]
4383
+ }), recapInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4384
+ id: `${tabsId}-recap-error`,
4385
+ className: "dsh-ui-error",
4386
+ role: "alert",
4387
+ children: copy("minutesInvalid")
4388
+ }) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4389
+ className: "dsh-ui-field dsm-model-field",
4390
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4391
+ className: "dsh-ui-label",
4392
+ children: copy("modelLabel")
4393
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
4394
+ locale: props.locale,
4395
+ open: menuOpen,
4396
+ onOpenChange: setMenuOpen,
4397
+ listClassName: FREE_CHAT_ROOT,
4398
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4399
+ type: "button",
4400
+ className: "dsh-model-menu-trigger",
4401
+ disabled: !draft.recapEnabled,
4402
+ "aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
4403
+ "aria-haspopup": "menu",
4404
+ "aria-expanded": menuOpen,
4405
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4406
+ className: "dsh-model-menu-triggerLabel",
4407
+ children: selectedLabel
4408
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
4409
+ }),
4410
+ choices: selectableChoices,
4411
+ selected: selectedRoute,
4412
+ onPick: (route) => edit({
4413
+ ...draft,
4414
+ model: modelRouteKey(route.provider, route.model)
4415
+ }),
4416
+ leading: [{
4417
+ id: "follow",
4418
+ label: copy("modelFollowOption"),
4419
+ selected: !selectedRoute
4420
+ }],
4421
+ onPickLeading: () => edit({
4422
+ ...draft,
4423
+ model: ""
4424
+ }),
4425
+ modelValue: selectedLabel
4426
+ })]
4427
+ })]
4428
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
4429
+ state: catalog,
4430
+ locale: props.locale
4431
+ })]
4432
+ })]
4433
+ }),
4434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4435
+ className: "dsm-feature",
4436
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4437
+ className: "dsh-ui-toggle-row",
4438
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4439
+ className: "dsh-ui-toggle-text",
4440
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4441
+ className: "dsh-ui-toggle-label",
4442
+ children: copy("compactEnabledLabel")
4443
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4444
+ className: "dsh-ui-hint",
4445
+ children: copy("compactEnabledHint")
4446
+ })]
4447
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4448
+ label: copy("compactEnabledLabel"),
4449
+ checked: draft.compactEnabled,
4450
+ onChange: (next) => edit({
4451
+ ...draft,
4452
+ compactEnabled: next
4453
+ })
4454
+ })]
4455
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4456
+ className: "dsm-feature-options dsm-compact-options",
4457
+ "data-disabled": draft.compactEnabled ? void 0 : "true",
4458
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4459
+ className: "dsh-ui-field",
4460
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4461
+ className: "dsh-ui-label",
4462
+ children: copy("compactMinutesLabel")
4463
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4464
+ className: "dsm-minutes-control",
4465
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
3882
4466
  className: "dsm-minutes",
3883
- value: draft.recapMinutes,
4467
+ value: draft.compactMinutes,
3884
4468
  inputMode: "numeric",
3885
- disabled: !draft.recapEnabled,
3886
- "aria-invalid": recapInvalid || void 0,
3887
- "aria-describedby": recapInvalid ? `${tabsId}-recap-error` : void 0,
4469
+ disabled: !draft.compactEnabled,
4470
+ "aria-invalid": compactInvalid || void 0,
4471
+ "aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
3888
4472
  onChange: (event) => edit({
3889
4473
  ...draft,
3890
- recapMinutes: event.target.value
4474
+ compactMinutes: event.target.value
3891
4475
  })
3892
4476
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3893
4477
  className: "dsh-ui-meta",
3894
4478
  children: copy("minutesUnit")
3895
4479
  })]
3896
4480
  })]
3897
- }), recapInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3898
- id: `${tabsId}-recap-error`,
4481
+ }), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4482
+ id: `${tabsId}-compact-error`,
3899
4483
  className: "dsh-ui-error",
3900
4484
  role: "alert",
3901
4485
  children: copy("minutesInvalid")
3902
- }) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3903
- className: "dsh-ui-field dsm-model-field",
3904
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3905
- className: "dsh-ui-label",
3906
- children: copy("modelLabel")
3907
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
3908
- locale: props.locale,
3909
- open: menuOpen,
3910
- onOpenChange: setMenuOpen,
3911
- listClassName: FREE_CHAT_ROOT,
3912
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3913
- type: "button",
3914
- className: "dsh-model-menu-trigger",
3915
- disabled: !draft.recapEnabled,
3916
- "aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
3917
- "aria-haspopup": "menu",
3918
- "aria-expanded": menuOpen,
3919
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3920
- className: "dsh-model-menu-triggerLabel",
3921
- children: selectedLabel
3922
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
3923
- }),
3924
- choices: selectableChoices,
3925
- selected: selectedRoute,
3926
- onPick: (route) => edit({
3927
- ...draft,
3928
- model: modelRouteKey(route.provider, route.model)
3929
- }),
3930
- leading: [{
3931
- id: "follow",
3932
- label: copy("modelFollowOption"),
3933
- selected: !selectedRoute
3934
- }],
3935
- onPickLeading: () => edit({
3936
- ...draft,
3937
- model: ""
3938
- }),
3939
- modelValue: selectedLabel
3940
- })]
3941
- })]
3942
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
3943
- state: catalog,
3944
- locale: props.locale
4486
+ }) : null] })
3945
4487
  })]
3946
- })]
3947
- }),
3948
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3949
- className: "dsm-feature",
3950
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3951
- className: "dsh-ui-toggle-row",
3952
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3953
- className: "dsh-ui-toggle-text",
3954
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3955
- className: "dsh-ui-toggle-label",
3956
- children: copy("compactEnabledLabel")
3957
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3958
- className: "dsh-ui-hint",
3959
- children: copy("compactEnabledHint")
3960
- })]
3961
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
3962
- label: copy("compactEnabledLabel"),
3963
- checked: draft.compactEnabled,
3964
- onChange: (next) => edit({
3965
- ...draft,
3966
- compactEnabled: next
3967
- })
3968
- })]
3969
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3970
- className: "dsm-feature-options dsm-compact-options",
3971
- "data-disabled": draft.compactEnabled ? void 0 : "true",
3972
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3973
- className: "dsh-ui-field",
3974
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3975
- className: "dsh-ui-label",
3976
- children: copy("compactMinutesLabel")
3977
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3978
- className: "dsm-minutes-control",
3979
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
3980
- className: "dsm-minutes",
3981
- value: draft.compactMinutes,
3982
- inputMode: "numeric",
3983
- disabled: !draft.compactEnabled,
3984
- "aria-invalid": compactInvalid || void 0,
3985
- "aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
3986
- onChange: (event) => edit({
3987
- ...draft,
3988
- compactMinutes: event.target.value
3989
- })
3990
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3991
- className: "dsh-ui-meta",
3992
- children: copy("minutesUnit")
3993
- })]
3994
- })]
3995
- }), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3996
- id: `${tabsId}-compact-error`,
3997
- className: "dsh-ui-error",
3998
- role: "alert",
3999
- children: copy("minutesInvalid")
4000
- }) : null] })
4001
- })]
4002
- })
4003
- ]
4004
- })]
4488
+ })
4489
+ ]
4490
+ })
4491
+ ]
4005
4492
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4006
4493
  className: "dsh-ui-form-footer dsm-settings-footer",
4007
4494
  children: [
@@ -4031,8 +4518,185 @@ function SessionManagerSettings(props) {
4031
4518
  });
4032
4519
  }
4033
4520
  //#endregion
4521
+ //#region src/message-editor.tsx
4522
+ function AttachmentPreview(props) {
4523
+ const [src, setSrc] = (0, react.useState)();
4524
+ const [failed, setFailed] = (0, react.useState)(false);
4525
+ const item = props.item;
4526
+ const name = item.type === "upload" ? item.file.name : item.type === "file" ? item.name : uiText(props.locale, "editImage");
4527
+ (0, react.useEffect)(() => {
4528
+ let active = true;
4529
+ let url;
4530
+ setSrc(void 0);
4531
+ setFailed(false);
4532
+ if (item.type === "upload" && item.file.type.startsWith("image/")) {
4533
+ url = URL.createObjectURL(item.file);
4534
+ setSrc(url);
4535
+ } else if (item.type === "image" && props.host.attachment) unwrap(props.host.attachment({
4536
+ sessionId: props.sessionId,
4537
+ attachmentId: item.attachmentId
4538
+ })).then((value) => {
4539
+ const image = value;
4540
+ if (active && image.data && image.attachment?.mediaType?.startsWith("image/")) setSrc(`data:${image.attachment.mediaType};base64,${image.data}`);
4541
+ else if (active) setFailed(true);
4542
+ }).catch(() => {
4543
+ if (active) setFailed(true);
4544
+ });
4545
+ return () => {
4546
+ active = false;
4547
+ if (url) URL.revokeObjectURL(url);
4548
+ };
4549
+ }, [
4550
+ item,
4551
+ props.host,
4552
+ props.sessionId
4553
+ ]);
4554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4555
+ src ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
4556
+ src,
4557
+ alt: name
4558
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, { path: name }),
4559
+ item.type === "file" || item.type === "upload" && !item.file.type.startsWith("image/") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4560
+ title: name,
4561
+ children: name
4562
+ }) : null,
4563
+ failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4564
+ role: "status",
4565
+ children: uiText(props.locale, "editPreviewFailed")
4566
+ }) : null
4567
+ ] });
4568
+ }
4569
+ /** Lives in the original bubble; drafts and attachments remain local until Send. */
4570
+ function MessageEditor(props) {
4571
+ const textarea = (0, react.useRef)(null);
4572
+ const copy = (key) => uiText(props.locale, key);
4573
+ const attachments = props.attachments ?? [];
4574
+ const disabled = props.busy || props.locked === true;
4575
+ (0, react.useEffect)(() => {
4576
+ textarea.current?.focus();
4577
+ textarea.current?.setSelectionRange(props.text.length, props.text.length);
4578
+ }, []);
4579
+ (0, react.useEffect)(() => {
4580
+ const element = textarea.current;
4581
+ if (!element) return;
4582
+ element.style.height = "auto";
4583
+ element.style.height = `${Math.max(80, Math.min(element.scrollHeight, 360))}px`;
4584
+ }, [props.text]);
4585
+ const add = (files) => {
4586
+ if (!disabled && files.length > 0) props.setAttachments?.([...attachments, ...files.map((file) => ({
4587
+ type: "upload",
4588
+ file
4589
+ }))]);
4590
+ };
4591
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4592
+ className: "dsm-message-edit",
4593
+ role: "group",
4594
+ "aria-label": copy("editTitle"),
4595
+ onDragOver: (event) => {
4596
+ if (!disabled && props.setAttachments && event.dataTransfer.types.includes("Files")) event.preventDefault();
4597
+ },
4598
+ onDrop: (event) => {
4599
+ if (props.setAttachments && event.dataTransfer.files.length) {
4600
+ event.preventDefault();
4601
+ add(Array.from(event.dataTransfer.files));
4602
+ }
4603
+ },
4604
+ children: [attachments.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4605
+ className: "dsm-edit-attachments",
4606
+ children: attachments.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4607
+ className: `dsm-edit-attachment ${item.type === "image" || item.type === "upload" && item.file.type.startsWith("image/") ? "dsm-edit-image" : ""}`,
4608
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttachmentPreview, {
4609
+ item,
4610
+ sessionId: props.sessionId,
4611
+ host: props.host ?? {},
4612
+ locale: props.locale
4613
+ }), props.setAttachments ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4614
+ size: "sm",
4615
+ variant: "ghost",
4616
+ disabled,
4617
+ "aria-label": `${copy("editRemoveAttachment")} ${index + 1}`,
4618
+ onClick: () => {
4619
+ props.setAttachments?.(attachments.filter((_, position) => position !== index));
4620
+ },
4621
+ children: "×"
4622
+ }) : null]
4623
+ }, item.type === "upload" ? `${index}:${item.file.name}` : `${index}:${item.attachmentId}`))
4624
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4625
+ className: "dsm-message-editor",
4626
+ children: [
4627
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4628
+ ref: textarea,
4629
+ className: "dsh-ui-textarea dsh-free-draft",
4630
+ "aria-label": copy("draft"),
4631
+ value: props.text,
4632
+ disabled,
4633
+ onChange: (event) => {
4634
+ props.setText(event.target.value);
4635
+ },
4636
+ onPaste: (event) => {
4637
+ const files = Array.from(event.clipboardData.files);
4638
+ if (files.length && props.setAttachments) {
4639
+ event.preventDefault();
4640
+ add(files);
4641
+ }
4642
+ },
4643
+ onKeyDown: (event) => {
4644
+ if (event.key === "Escape" && !props.busy) {
4645
+ event.stopPropagation();
4646
+ props.onCancel();
4647
+ }
4648
+ }
4649
+ }),
4650
+ props.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4651
+ className: "dsh-ui-error",
4652
+ role: "alert",
4653
+ children: props.error
4654
+ }) : null,
4655
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4656
+ className: "dsm-edit-footer",
4657
+ children: [
4658
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsm-edit-spacer" }),
4659
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4660
+ variant: "ghost",
4661
+ disabled: props.busy,
4662
+ onClick: props.onCancel,
4663
+ children: copy("cancel")
4664
+ }),
4665
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4666
+ variant: "primary",
4667
+ disabled: props.busy || !props.text.trim() && !attachments.length,
4668
+ onClick: props.onSend,
4669
+ children: copy("send")
4670
+ })
4671
+ ]
4672
+ })
4673
+ ]
4674
+ })]
4675
+ });
4676
+ }
4677
+ //#endregion
4678
+ //#region src/edit-icon.tsx
4679
+ /** The stock edit primitive includes a box; message editing uses a standalone pencil. */
4680
+ function EditMessageIcon() {
4681
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4682
+ width: "16",
4683
+ height: "16",
4684
+ viewBox: "0 0 16 16",
4685
+ fill: "none",
4686
+ "aria-hidden": "true",
4687
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
4688
+ d: "m10.5 2.5 3 3M2 14l1-4L10.5 2.5a2.12 2.12 0 0 1 3 3L6 13l-4 1Z",
4689
+ stroke: "currentColor",
4690
+ strokeWidth: "1",
4691
+ strokeLinecap: "round",
4692
+ strokeLinejoin: "round"
4693
+ })
4694
+ });
4695
+ }
4696
+ //#endregion
4034
4697
  //#region src/user-actions.tsx
4035
4698
  function UserMessageActions(props) {
4699
+ const [confirming, setConfirming] = (0, react.useState)(false);
4036
4700
  const snapshot = (0, react.useSyncExternalStore)(props.model.subscribe, props.model.getSnapshot, props.model.getSnapshot);
4037
4701
  const signal = (0, react.useRef)(new AbortController());
4038
4702
  (0, react.useEffect)(() => {
@@ -4056,75 +4720,624 @@ function UserMessageActions(props) {
4056
4720
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4057
4721
  className: `${FREE_CHAT_ROOT} dsh-free-actions`,
4058
4722
  children: [
4059
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
4060
- label: copy("retry"),
4061
- side: "bottom",
4062
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4063
- size: "sm",
4064
- variant: "ghost",
4065
- "aria-label": copy("retry"),
4066
- disabled: snapshot.busy,
4067
- onClick: () => {
4068
- props.model.retry(props.sessionId, props.messageId, signal.current.signal);
4069
- },
4070
- children: copy("retry")
4071
- })
4072
- }),
4073
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
4723
+ !props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
4074
4724
  label: copy("edit"),
4075
4725
  side: "bottom",
4076
4726
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4077
4727
  size: "sm",
4728
+ className: "dsm-icon-action",
4078
4729
  variant: "ghost",
4079
4730
  "aria-label": copy("edit"),
4080
- disabled: snapshot.busy,
4731
+ disabled: snapshot.busy || snapshot.editing && !mine,
4081
4732
  onClick: () => {
4082
4733
  props.model.beginEdit(props.sessionId, props.messageId, props.text, signal.current.signal);
4083
4734
  },
4084
- children: copy("edit")
4735
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
4085
4736
  })
4086
- }),
4087
- failure ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4737
+ }) : null,
4738
+ failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4088
4739
  className: "dsh-ui-error dsh-free-alert",
4089
4740
  role: "alert",
4090
4741
  children: failureText(props.locale, failure)
4091
4742
  }) : null,
4743
+ editing && !props.hideEdit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
4744
+ text: draft,
4745
+ setText: (value) => {
4746
+ props.model.setDraft(value);
4747
+ },
4748
+ sessionId: props.sessionId,
4749
+ locale: props.locale,
4750
+ busy: snapshot.busy,
4751
+ error: failure ? failureText(props.locale, failure) : void 0,
4752
+ onCancel: () => {
4753
+ props.model.cancelEdit();
4754
+ },
4755
+ onSend: () => {
4756
+ setConfirming(true);
4757
+ }
4758
+ }) : null,
4092
4759
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
4093
- open: editing,
4760
+ open: confirming && editing,
4094
4761
  className: FREE_CHAT_ROOT,
4095
4762
  title: copy("editTitle"),
4096
4763
  closeLabel: copy("close"),
4097
4764
  onClose: () => {
4098
- props.model.cancelEdit();
4765
+ setConfirming(false);
4099
4766
  },
4100
4767
  footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4101
4768
  variant: "ghost",
4102
- disabled: snapshot.busy,
4769
+ "data-modal-autofocus": true,
4103
4770
  onClick: () => {
4104
- props.model.cancelEdit();
4771
+ setConfirming(false);
4105
4772
  },
4106
4773
  children: copy("cancel")
4107
4774
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4108
4775
  variant: "primary",
4109
- disabled: snapshot.busy,
4110
4776
  onClick: () => {
4111
- props.model.sendEdit(props.sessionId, props.messageId, signal.current.signal);
4777
+ setConfirming(false);
4778
+ props.model.sendEdit(props.sessionId, props.messageId, signal.current.signal, void 0, true);
4112
4779
  },
4113
- children: copy("send")
4780
+ children: copy("editConfirm")
4114
4781
  })] }),
4115
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4116
- className: "dsh-ui-textarea dsh-free-draft",
4117
- "data-modal-autofocus": true,
4118
- "aria-label": copy("draft"),
4119
- value: draft,
4120
- onChange: (event) => {
4121
- props.model.setDraft(event.target.value);
4122
- }
4782
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
4783
+ })
4784
+ ]
4785
+ });
4786
+ }
4787
+ //#endregion
4788
+ //#region src/edit-host.ts
4789
+ function bindEditHost(client) {
4790
+ const rpc = client.connection?.rpc;
4791
+ const remote = client.remote?.session;
4792
+ return {
4793
+ ...typeof rpc?.call === "function" ? {
4794
+ prepare: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_PREPARE_ENDPOINT, request, signal),
4795
+ stageFile: (request, signal) => rpc.call(TEMP_WORKSPACE_CHANNEL, EDIT_FILE_ENDPOINT, request, signal)
4796
+ } : {},
4797
+ uploadFile: async (sessionId, file, signal) => {
4798
+ const query = new URLSearchParams({
4799
+ sessionId,
4800
+ name: file.name
4801
+ });
4802
+ const response = await fetch(`/api/session/uploadFileBinary?${query}`, {
4803
+ method: "POST",
4804
+ credentials: "same-origin",
4805
+ headers: { "content-type": "application/octet-stream" },
4806
+ body: file,
4807
+ signal
4808
+ });
4809
+ if (!response.ok) throw new HostOperationError("session/attachment-invalid");
4810
+ return response.json();
4811
+ },
4812
+ ...typeof remote?.attachment === "function" ? { attachment: (request) => remote.attachment(request) } : {}
4813
+ };
4814
+ }
4815
+ function readPreview(value) {
4816
+ if (typeof value !== "object" || value === null) throw new HostOperationError("session-manager/unavailable");
4817
+ const record = value;
4818
+ if (typeof record.sessionId !== "string" || typeof record.promptSeq !== "number" || typeof record.text !== "string" || typeof record.modified !== "boolean") throw new HostOperationError("session-manager/unavailable");
4819
+ const content = record.content;
4820
+ if (content !== void 0 && (!Array.isArray(content) || !content.every(isEditContent))) throw new HostOperationError("session-manager/unavailable");
4821
+ return {
4822
+ sessionId: record.sessionId,
4823
+ promptSeq: record.promptSeq,
4824
+ text: record.text,
4825
+ ...content === void 0 ? {} : { content },
4826
+ modified: record.modified
4827
+ };
4828
+ }
4829
+ function isEditContent(value) {
4830
+ if (typeof value !== "object" || value === null) return false;
4831
+ const part = value;
4832
+ return part.type === "text" ? typeof part.text === "string" : (part.type === "image" || part.type === "file" && typeof part.name === "string" && typeof part.bytes === "number") && typeof part.attachmentId === "string" && part.attachmentId !== "";
4833
+ }
4834
+ /** Build the exact body for the edited prompt; images are read from the source Session. */
4835
+ async function buildContent(host, preview, text, signal, attachments) {
4836
+ const content = [];
4837
+ let wroteText = false;
4838
+ const parts = attachments === void 0 ? preview.content ?? [{
4839
+ type: "text",
4840
+ text: preview.text
4841
+ }] : [{
4842
+ type: "text",
4843
+ text
4844
+ }, ...attachments];
4845
+ for (const part of parts) {
4846
+ signal.throwIfAborted();
4847
+ if (part.type === "text") {
4848
+ if (text === preview.text) content.push(part);
4849
+ else if (!wroteText) content.push({
4850
+ type: "text",
4851
+ text
4852
+ });
4853
+ wroteText = true;
4854
+ continue;
4855
+ }
4856
+ if (part.type === "file") {
4857
+ content.push(part);
4858
+ continue;
4859
+ }
4860
+ if (part.type === "upload") {
4861
+ if ([
4862
+ "image/png",
4863
+ "image/jpeg",
4864
+ "image/webp",
4865
+ "image/gif"
4866
+ ].includes(part.file.type)) {
4867
+ const data = await new Promise((resolve, reject) => {
4868
+ const reader = new FileReader();
4869
+ reader.onload = () => {
4870
+ resolve(String(reader.result).split(",")[1]);
4871
+ };
4872
+ reader.onerror = () => {
4873
+ reject(reader.error);
4874
+ };
4875
+ reader.readAsDataURL(part.file);
4876
+ });
4877
+ signal.throwIfAborted();
4878
+ content.push({
4879
+ type: "image",
4880
+ data,
4881
+ mediaType: part.file.type,
4882
+ name: part.file.name
4883
+ });
4884
+ } else content.push(part);
4885
+ continue;
4886
+ }
4887
+ if (!host.attachment) throw new HostOperationError("session/lifecycle-unavailable");
4888
+ const image = await unwrap(host.attachment({
4889
+ sessionId: preview.sessionId,
4890
+ attachmentId: part.attachmentId
4891
+ }));
4892
+ if (image?.attachment?.attachmentId !== part.attachmentId || typeof image.data !== "string" || image.data === "" || typeof image.attachment.mediaType !== "string" || !image.attachment.mediaType.startsWith("image/")) throw new HostOperationError("session/attachment-invalid");
4893
+ content.push({
4894
+ type: "image",
4895
+ data: image.data,
4896
+ mediaType: image.attachment.mediaType,
4897
+ ...typeof image.attachment.name === "string" ? { name: image.attachment.name } : {}
4898
+ });
4899
+ }
4900
+ if (!wroteText && text !== "") content.push({
4901
+ type: "text",
4902
+ text
4903
+ });
4904
+ signal.throwIfAborted();
4905
+ return content;
4906
+ }
4907
+ /** Prepare the exact edit body for the existing Session; the host sends it on submit. */
4908
+ async function prepareEditContent(host, preview, text, submission, signal, attachments) {
4909
+ submission.prepared ??= await buildContent(host, preview, text, signal, attachments);
4910
+ await stageContent(host, preview, submission, preview.sessionId, signal);
4911
+ return submission.content;
4912
+ }
4913
+ async function stageContent(host, preview, submission, sessionId, signal) {
4914
+ const prepared = submission.prepared;
4915
+ if (submission.content !== void 0) return;
4916
+ const content = [];
4917
+ const receipts = submission.receipts ??= /* @__PURE__ */ new Map();
4918
+ for (const [index, part] of prepared.entries()) {
4919
+ signal.throwIfAborted();
4920
+ if (part.type !== "upload" && !(part.type === "file" && "attachmentId" in part)) {
4921
+ content.push(part);
4922
+ continue;
4923
+ }
4924
+ let receiptId = receipts.get(index);
4925
+ if (receiptId === void 0) {
4926
+ const result = part.type === "upload" ? host.uploadFile?.(sessionId, part.file, signal) : host.stageFile?.({
4927
+ sessionId: preview.sessionId,
4928
+ promptSeq: preview.promptSeq,
4929
+ attachmentId: part.attachmentId
4930
+ }, signal);
4931
+ if (!result) throw new HostOperationError("session/lifecycle-unavailable");
4932
+ const value = await unwrap(result);
4933
+ if (typeof value?.receiptId !== "string" || value.receiptId === "") throw new HostOperationError("session/attachment-invalid");
4934
+ receiptId = value.receiptId;
4935
+ receipts.set(index, receiptId);
4936
+ }
4937
+ content.push({
4938
+ type: "file",
4939
+ receiptId
4940
+ });
4941
+ }
4942
+ submission.content = content;
4943
+ }
4944
+ /** Read the exact human prompt that the editor edits. */
4945
+ async function previewEdit(host, sessionId, promptSeq, signal) {
4946
+ if (typeof host.prepare !== "function") throw new HostOperationError("session/lifecycle-unavailable");
4947
+ return readPreview(await unwrap(host.prepare({
4948
+ sessionId,
4949
+ promptSeq
4950
+ }, signal)));
4951
+ }
4952
+ //#endregion
4953
+ //#region src/edit-actions.tsx
4954
+ /** Edit one human prompt in its own Session; the host switches branches when the edit is sent. */
4955
+ function EditActions(props) {
4956
+ const sessionId = props.sessionId ?? "";
4957
+ const native = (0, react.useSyncExternalStore)(props.nativeRetry.subscribe, props.nativeRetry.getSnapshot, props.nativeRetry.getSnapshot);
4958
+ const [confirming, setConfirming] = (0, react.useState)(false);
4959
+ const [busy, setBusy] = (0, react.useState)(false);
4960
+ const [editing, setEditing] = (0, react.useState)(false);
4961
+ const [draft, setDraft] = (0, react.useState)("");
4962
+ const [attachments, setAttachments] = (0, react.useState)([]);
4963
+ const [mediaChanged, setMediaChanged] = (0, react.useState)(false);
4964
+ const [locked, setLocked] = (0, react.useState)(false);
4965
+ const [failure, setFailure] = (0, react.useState)();
4966
+ const preview = (0, react.useRef)();
4967
+ const submission = (0, react.useRef)();
4968
+ const signal = (0, react.useRef)(new AbortController());
4969
+ (0, react.useEffect)(() => {
4970
+ const controller = new AbortController();
4971
+ signal.current = controller;
4972
+ return () => {
4973
+ controller.abort();
4974
+ };
4975
+ }, [sessionId, props.promptSeq]);
4976
+ const allowed = props.useSessionAccess?.(sessionId, "session/prompt") ?? true;
4977
+ const copy = (key) => uiText(props.locale, key);
4978
+ const mine = native.sessionId === sessionId && native.messageId === props.messageId;
4979
+ (0, react.useEffect)(() => {
4980
+ if (editing && !busy && native.controlsRetry && !mine) setEditing(false);
4981
+ }, [
4982
+ editing,
4983
+ busy,
4984
+ native.controlsRetry,
4985
+ mine
4986
+ ]);
4987
+ if (!allowed || !sessionId || props.promptSeq === void 0 || !props.host.prepare) return props.render?.(null, null) ?? null;
4988
+ const run = async (action) => {
4989
+ if (busy || signal.current.signal.aborted) return;
4990
+ setBusy(true);
4991
+ setFailure(void 0);
4992
+ try {
4993
+ if (!native.controlsRetry || !props.messageId) throw new HostOperationError("session/lifecycle-unavailable");
4994
+ if (action === "edit") {
4995
+ const value = await previewEdit(props.host, sessionId, props.promptSeq, signal.current.signal);
4996
+ await props.nativeRetry.beginEdit(sessionId, props.messageId, value.text, signal.current.signal);
4997
+ const state = props.nativeRetry.getSnapshot();
4998
+ if (!state.editing) {
4999
+ setFailure(state.failure);
5000
+ return;
5001
+ }
5002
+ preview.current = value;
5003
+ submission.current = { requestId: (props.ids ?? (() => crypto.randomUUID()))() };
5004
+ setDraft(value.text);
5005
+ setAttachments((value.content ?? []).filter((part) => part.type !== "text"));
5006
+ setMediaChanged(false);
5007
+ setLocked(false);
5008
+ setEditing(true);
5009
+ return;
5010
+ }
5011
+ if (!preview.current || !submission.current) return;
5012
+ if (mediaChanged && native.controlsContent !== true) throw new HostOperationError("session/retry-media-unavailable");
5013
+ setLocked(true);
5014
+ const content = mediaChanged ? await prepareEditContent(props.host, preview.current, draft, submission.current, signal.current.signal, attachments) : void 0;
5015
+ props.nativeRetry.setDraft(draft);
5016
+ await props.nativeRetry.sendEdit(sessionId, props.messageId, signal.current.signal, content, true);
5017
+ const state = props.nativeRetry.getSnapshot();
5018
+ setFailure(state.failure);
5019
+ if (!state.editing) setEditing(false);
5020
+ else if (isStaleRetryFailure(state.failure)) setLocked(false);
5021
+ } catch (error) {
5022
+ if (!signal.current.signal.aborted) setFailure(readFailure(error));
5023
+ } finally {
5024
+ if (!signal.current.signal.aborted) setBusy(false);
5025
+ }
5026
+ };
5027
+ const cancel = () => {
5028
+ if (busy) return;
5029
+ props.nativeRetry.cancelEdit();
5030
+ setEditing(false);
5031
+ setFailure(void 0);
5032
+ };
5033
+ const invalidatePrepared = () => {
5034
+ if (submission.current) submission.current = { requestId: submission.current.requestId };
5035
+ };
5036
+ const actions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5037
+ className: `${FREE_CHAT_ROOT} dsh-free-actions`,
5038
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
5039
+ label: copy("edit"),
5040
+ side: "bottom",
5041
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5042
+ className: "dsm-icon-action",
5043
+ size: "sm",
5044
+ variant: "ghost",
5045
+ "aria-label": copy("edit"),
5046
+ disabled: busy || native.busy || native.editing && !mine,
5047
+ onClick: () => {
5048
+ run("edit");
5049
+ },
5050
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditMessageIcon, {})
5051
+ })
5052
+ }), failure && !editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5053
+ className: "dsh-ui-error dsh-free-alert",
5054
+ role: "alert",
5055
+ children: failureText(props.locale, failure)
5056
+ }) : null]
5057
+ });
5058
+ const editor = editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageEditor, {
5059
+ text: draft,
5060
+ setText: (text) => {
5061
+ invalidatePrepared();
5062
+ setDraft(text);
5063
+ },
5064
+ attachments,
5065
+ setAttachments: native.controlsContent ? (items) => {
5066
+ invalidatePrepared();
5067
+ setAttachments(items);
5068
+ setMediaChanged(true);
5069
+ } : void 0,
5070
+ sessionId,
5071
+ host: props.host,
5072
+ locale: props.locale,
5073
+ busy,
5074
+ locked,
5075
+ onCancel: cancel,
5076
+ onSend: () => {
5077
+ setConfirming(true);
5078
+ },
5079
+ error: failure ? failureText(props.locale, failure) : void 0
5080
+ }) : null;
5081
+ const content = props.render ? props.render(actions, editor) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [actions, editor] });
5082
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [content, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5083
+ open: confirming && editing,
5084
+ className: FREE_CHAT_ROOT,
5085
+ title: copy("editTitle"),
5086
+ closeLabel: copy("close"),
5087
+ onClose: () => {
5088
+ setConfirming(false);
5089
+ },
5090
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5091
+ variant: "ghost",
5092
+ "data-modal-autofocus": true,
5093
+ onClick: () => {
5094
+ setConfirming(false);
5095
+ },
5096
+ children: copy("cancel")
5097
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5098
+ variant: "primary",
5099
+ onClick: () => {
5100
+ setConfirming(false);
5101
+ run("send");
5102
+ },
5103
+ children: copy("editConfirm")
5104
+ })] }),
5105
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy("editBranchWarning") })
5106
+ })] });
5107
+ }
5108
+ //#endregion
5109
+ //#region src/user-node.tsx
5110
+ function contentParts(content) {
5111
+ const texts = [];
5112
+ const attachments = [];
5113
+ const rest = [];
5114
+ for (const block of content) if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
5115
+ else if (block.type === "image" && block.attachment !== void 0) attachments.push({
5116
+ type: "image",
5117
+ image: { attachment: block.attachment }
5118
+ });
5119
+ else if (block.type === "file" && block.attachment !== void 0) attachments.push({
5120
+ type: "file",
5121
+ file: block.attachment
5122
+ });
5123
+ else rest.push(block);
5124
+ return {
5125
+ text: texts.join(""),
5126
+ attachments,
5127
+ rest
5128
+ };
5129
+ }
5130
+ function pad2(value) {
5131
+ return String(value).padStart(2, "0");
5132
+ }
5133
+ function messageClock(time, t) {
5134
+ const date = new Date(time);
5135
+ const now = /* @__PURE__ */ new Date();
5136
+ const clock = `${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
5137
+ if (date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) return clock;
5138
+ const params = {
5139
+ y: date.getFullYear(),
5140
+ m: date.getMonth() + 1,
5141
+ d: date.getDate()
5142
+ };
5143
+ return `${date.getFullYear() === now.getFullYear() ? t("clock.md", params) : t("clock.ymd", params)} ${clock}`;
5144
+ }
5145
+ function MessageChrome(props) {
5146
+ const [copied, setCopied] = (0, react.useState)(false);
5147
+ const timer = (0, react.useRef)(void 0);
5148
+ const pending = (0, react.useRef)(false);
5149
+ (0, react.useEffect)(() => () => {
5150
+ pending.current = false;
5151
+ if (timer.current !== void 0) clearTimeout(timer.current);
5152
+ }, []);
5153
+ const copy = () => {
5154
+ if (copied || pending.current) return;
5155
+ pending.current = true;
5156
+ (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(props.text).then((ok) => {
5157
+ pending.current = false;
5158
+ if (!ok) return;
5159
+ setCopied(true);
5160
+ timer.current = setTimeout(() => {
5161
+ setCopied(false);
5162
+ }, 1e3);
5163
+ });
5164
+ };
5165
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5166
+ className: "dsm-user-chrome",
5167
+ children: [
5168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5169
+ className: "dsm-user-time",
5170
+ children: messageClock(props.time, props.t)
5171
+ }),
5172
+ props.children,
5173
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
5174
+ label: copied ? props.t("copied") : props.t("copy"),
5175
+ side: "bottom",
5176
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5177
+ size: "sm",
5178
+ variant: "ghost",
5179
+ className: "dsm-icon-action",
5180
+ "aria-label": copied ? props.t("copied") : props.t("copy"),
5181
+ onClick: copy,
5182
+ children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {})
4123
5183
  })
4124
5184
  })
4125
5185
  ]
4126
5186
  });
4127
5187
  }
5188
+ /** User-message bubble whose own action row edits the exact prompt in place. */
5189
+ function UserNodeWithEdit(props) {
5190
+ const { text, attachments, rest } = contentParts(props.node.data.content);
5191
+ const compactImages = attachments.length > 1;
5192
+ const truncated = (total) => props.t("json.truncated", { total });
5193
+ const data = props.node.data;
5194
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditActions, {
5195
+ promptSeq: data.seq,
5196
+ sessionId: props.sessionId,
5197
+ locale: props.locale,
5198
+ host: props.host,
5199
+ nativeRetry: props.nativeRetry,
5200
+ useSessionAccess: props.useSessionAccess,
5201
+ ids: props.ids,
5202
+ messageId: props.node.id,
5203
+ render: (actions, editor) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5204
+ className: `${FREE_CHAT_ROOT} dsm-user-row`,
5205
+ children: [editor, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5206
+ className: "dsm-user-original",
5207
+ hidden: Boolean(editor),
5208
+ children: [!props.renderMessageImages && props.renderFactorySlot ? props.renderFactorySlot("conversation.chat.user.content", {
5209
+ content: {
5210
+ text,
5211
+ attachments,
5212
+ extraBlocks: rest,
5213
+ ...data.referenceLabels === void 0 ? {} : { referenceLabels: data.referenceLabels }
5214
+ },
5215
+ loadImage: props.loadImage,
5216
+ facts: {
5217
+ ...data.referenceLabels === void 0 ? {} : { sessionLabels: data.referenceLabels },
5218
+ ...data.skillNames === void 0 ? {} : { skillNames: data.skillNames }
5219
+ },
5220
+ interactive: true
5221
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5222
+ className: "dsm-user-stack",
5223
+ children: [
5224
+ attachments.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5225
+ className: "dsm-user-attachments",
5226
+ "data-message-attachments": true,
5227
+ children: attachments.map((attachment, index) => attachment.type === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: props.renderMessageImages?.({
5228
+ images: [attachment.image],
5229
+ align: "end",
5230
+ compact: compactImages
5231
+ }) }, `image:${index}`) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5232
+ className: "dsm-file-card",
5233
+ title: attachment.file.name,
5234
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
5235
+ path: attachment.file.name,
5236
+ className: "dsm-file-icon"
5237
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5238
+ className: "dsm-file-content",
5239
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5240
+ className: "dsm-file-name",
5241
+ children: attachment.file.name
5242
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5243
+ className: "dsm-file-meta",
5244
+ children: [(0, _deepseek_ai_dsh_client_ui_primitives.fileExtension)(attachment.file.name).toUpperCase().slice(0, 8), (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(attachment.file.bytes)].filter(Boolean).join(" ")
5245
+ })]
5246
+ })]
5247
+ }, `file:${index}`))
5248
+ }) : null,
5249
+ text !== "" || rest.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5250
+ className: "dsm-user-bubble",
5251
+ children: [text, rest.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
5252
+ label: props.t("message.extraBlock"),
5253
+ payload: block,
5254
+ truncatedLabel: truncated
5255
+ }, index))]
5256
+ }) : null,
5257
+ data.referenceLabels !== void 0 && data.referenceLabels.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5258
+ className: "dsm-reference-summary",
5259
+ children: props.t("message.referenceSummary", { labels: data.referenceLabels.join(props.t("message.referenceSeparator")) })
5260
+ }) : null
5261
+ ]
5262
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(MessageChrome, {
5263
+ text,
5264
+ time: data.time,
5265
+ t: props.t,
5266
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserMessageActions, {
5267
+ messageId: props.node.id,
5268
+ text,
5269
+ sessionId: props.sessionId ?? "",
5270
+ locale: props.locale,
5271
+ model: props.nativeRetry,
5272
+ hideEdit: Boolean(actions)
5273
+ }), actions]
5274
+ })]
5275
+ })]
5276
+ })
5277
+ });
5278
+ }
5279
+ //#endregion
5280
+ //#region src/retry-contracts.ts
5281
+ const RETRY_CAPABILITIES_ENDPOINT = "retry.capabilities";
5282
+ const RETRY_PREVIEW_ENDPOINT = "retry.preview";
5283
+ const RETRY_COMMIT_ENDPOINT = "retry.commit";
5284
+ //#endregion
5285
+ //#region src/retry-host.ts
5286
+ function bindRetryHost(rpc) {
5287
+ const call = (endpoint, payload, signal) => {
5288
+ if (typeof rpc?.call !== "function") throw new HostOperationError("session/lifecycle-unavailable");
5289
+ return rpc.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload, signal);
5290
+ };
5291
+ return {
5292
+ async capabilities() {
5293
+ const value = await unwrap(call(RETRY_CAPABILITIES_ENDPOINT, {}));
5294
+ if (!isCapabilities(value)) throw new HostOperationError("session-manager/unavailable");
5295
+ return value;
5296
+ },
5297
+ async preview(request, signal) {
5298
+ const value = await unwrap(call(RETRY_PREVIEW_ENDPOINT, request, signal));
5299
+ if (!isPreview(value)) throw new HostOperationError("session-manager/unavailable");
5300
+ return value;
5301
+ },
5302
+ async commit(request, signal) {
5303
+ const value = await unwrap(call(RETRY_COMMIT_ENDPOINT, request, signal));
5304
+ if (!isCommit(value)) throw new HostOperationError("session-manager/unavailable");
5305
+ return value;
5306
+ }
5307
+ };
5308
+ }
5309
+ /**
5310
+ * Commit then navigate: the forked child is the chat the user keeps, so the
5311
+ * browser follows a successful commit to the new session. `open` is absent
5312
+ * only when the host has no workspace navigation; the commit value returns
5313
+ * either way.
5314
+ */
5315
+ function retryCommitAndOpen(commit, open) {
5316
+ return async (request, signal) => {
5317
+ const value = await commit(request, signal);
5318
+ try {
5319
+ await open?.(value.sessionId);
5320
+ } catch {
5321
+ throw new HostOperationError("session/retry-pending");
5322
+ }
5323
+ return value;
5324
+ };
5325
+ }
5326
+ function isCapabilities(value) {
5327
+ if (typeof value !== "object" || value === null) return false;
5328
+ const item = value;
5329
+ return typeof item.retry === "boolean" && typeof item.retryModified === "boolean" && typeof item.retryContent === "boolean";
5330
+ }
5331
+ function isPreview(value) {
5332
+ if (typeof value !== "object" || value === null) return false;
5333
+ const item = value;
5334
+ return typeof item.sessionId === "string" && typeof item.messageId === "string" && Number.isSafeInteger(item.promptSeq) && typeof item.branchRevision === "number" && typeof item.inputRevision === "string" && typeof item.eligible === "boolean" && typeof item.modified === "boolean";
5335
+ }
5336
+ function isCommit(value) {
5337
+ if (typeof value !== "object" || value === null) return false;
5338
+ const item = value;
5339
+ return typeof item.sessionId === "string" && typeof item.sourceSessionId === "string" && typeof item.archived === "boolean";
5340
+ }
4128
5341
  //#endregion
4129
5342
  //#region src/client.tsx
4130
5343
  const name = "dsh-session-manager-client";
@@ -4144,7 +5357,14 @@ function apply(ctx) {
4144
5357
  mountModelSettingsChanges(client, PLUGIN_NAME);
4145
5358
  const host = bindHost(client);
4146
5359
  const chats = createFreeChatModel(host);
4147
- const retries = createRetryModel(host);
5360
+ const retryHost = bindRetryHost(client.connection?.rpc);
5361
+ const retries = createRetryModel({
5362
+ list: host.list,
5363
+ capabilities: () => retryHost.capabilities(),
5364
+ previewRetry: (request) => retryHost.preview(request),
5365
+ retry: retryCommitAndOpen(retryHost.commit, host.openSession)
5366
+ });
5367
+ const editHost = bindEditHost(client);
4148
5368
  const temporaryHost = bindTemporaryWorkspaceHost(client);
4149
5369
  const temporary = createTemporaryWorkspaceModel(temporaryHost, void 0, () => uiText(copyLocale(client.locale?.getSnapshot().active), "tempWorkspace"));
4150
5370
  const presenceHost = bindPresenceHost(client);
@@ -4168,6 +5388,21 @@ function apply(ctx) {
4168
5388
  ...props
4169
5389
  }))));
4170
5390
  stops.push(installTemporaryWorkspacePicker(client.slots, temporary, temporaryHost.createWorkspace, client.locale));
5391
+ stops.push(client.slots.inject("conversation.hero.options", () => client.slots.register({
5392
+ name: "conversation.hero.options",
5393
+ id: "auto-continue",
5394
+ order: 30,
5395
+ inject: () => ({
5396
+ call: (endpoint, payload) => client.connection?.rpc?.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload),
5397
+ subscribeGeneration: (listener) => client.connection?.generation?.subscribe(listener)
5398
+ })
5399
+ }, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoContinueOption, {
5400
+ sessionId: props.sessionId,
5401
+ call: props.call,
5402
+ subscribeGeneration: props.subscribeGeneration,
5403
+ useSession: props.useSession,
5404
+ locale: useLiveLocale(client.locale)
5405
+ }))));
4171
5406
  stops.push(client.slots.inject(USER_ACTIONS_SLOT, () => client.slots.register({
4172
5407
  name: USER_ACTIONS_SLOT,
4173
5408
  id: USER_ACTIONS_ID,
@@ -4193,8 +5428,22 @@ function apply(ctx) {
4193
5428
  locale: useLiveLocale(client.locale),
4194
5429
  model: continues
4195
5430
  }))));
4196
- stops.push(client.slots.inject(WELCOME_BACK_CARD_SLOT, () => client.slots.register({
4197
- name: WELCOME_BACK_CARD_SLOT,
5431
+ stops.push(client.slots.inject("conversation.chat.node", () => client.slots.register({
5432
+ name: "conversation.chat.node",
5433
+ key: "user",
5434
+ locale: "chat",
5435
+ priority: -10
5436
+ }, (props) => {
5437
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserNodeWithEdit, {
5438
+ ...props,
5439
+ sessionId: props.sessionId,
5440
+ locale: useLiveLocale(client.locale),
5441
+ host: editHost,
5442
+ nativeRetry: retries
5443
+ });
5444
+ })));
5445
+ stops.push(client.slots.inject(TURN_TAIL_SLOT, () => client.slots.register({
5446
+ name: TURN_TAIL_SLOT,
4198
5447
  id: WELCOME_BACK_CARD_ID,
4199
5448
  order: 30
4200
5449
  }, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WelcomeBackCard, {