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