dsh-codex-subscription 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -483,6 +483,41 @@ window.__ModuleLoader__.load({
483
483
  };
484
484
  }
485
485
  //#endregion
486
+ //#region src/client-session-compat.js
487
+ async function withComposerSession(sessions, id, operation) {
488
+ const reference = typeof sessions.retain === "function" ? sessions.retain(id, { source: "controllerOperation" }) : void 0;
489
+ try {
490
+ if (reference) await reference.ready;
491
+ const context = reference ? reference.binding.ctx : sessions.scope(id);
492
+ if (!context) throw new Error("Image composer is unavailable");
493
+ return await operation(context);
494
+ } finally {
495
+ reference?.release();
496
+ }
497
+ }
498
+ function openComposerSession(sessions, workspace, id) {
499
+ if (typeof workspace?.openSession === "function") workspace.openSession(id);
500
+ else if (typeof sessions.open === "function") sessions.open(id);
501
+ else throw new Error("Session navigation is unavailable");
502
+ }
503
+ function createSessionOpeners() {
504
+ const entries = /* @__PURE__ */ new Map();
505
+ return {
506
+ register(id, callback) {
507
+ const callbacks = entries.get(id) ?? /* @__PURE__ */ new Set();
508
+ entries.set(id, callbacks);
509
+ callbacks.add(callback);
510
+ return () => {
511
+ callbacks.delete(callback);
512
+ if (!callbacks.size) entries.delete(id);
513
+ };
514
+ },
515
+ has: (id) => entries.has(id),
516
+ get: (id) => [...entries.get(id) ?? []].at(-1),
517
+ clear: () => entries.clear()
518
+ };
519
+ }
520
+ //#endregion
486
521
  //#region src/image-edit.js
487
522
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
488
523
  const validCoordinate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
@@ -645,7 +680,10 @@ window.__ModuleLoader__.load({
645
680
  loadImage,
646
681
  attempt
647
682
  ]);
648
- const item = image.attachment ?? image.preview;
683
+ const item = {
684
+ ...image.attachment ?? image.preview,
685
+ ...image.label ? { name: image.label } : {}
686
+ };
649
687
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
650
688
  type: "button",
651
689
  className: "codexImageThumb",
@@ -670,11 +708,12 @@ window.__ModuleLoader__.load({
670
708
  }) : failed ? t("accountRetry") : "…"
671
709
  });
672
710
  }
673
- function MessageImagePreviews({ images, align, ...props }) {
711
+ function MessageImagePreviews({ images, align, compact, thumbnail, ...props }) {
674
712
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
675
713
  className: "codexMessageImages",
676
714
  "data-align": align,
677
- "data-single": images.length === 1,
715
+ "data-single": images.length === 1 && !compact && !thumbnail,
716
+ "data-thumbnail": thumbnail || void 0,
678
717
  children: images.map((image, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageImagePreview, {
679
718
  image,
680
719
  ...props
@@ -689,6 +728,7 @@ window.__ModuleLoader__.load({
689
728
  .codexMessageImages{flex-wrap:wrap}.codexMessageImages[data-align=end]{justify-content:flex-end}
690
729
  .codexMessageImages[data-single=true] .codexImageThumb{width:240px;height:auto;max-width:100%}
691
730
  .codexMessageImages[data-single=true] img{height:auto;max-height:320px;object-fit:contain}
731
+ .codexMessageImages[data-thumbnail=true]{padding:0}.codexMessageImages[data-thumbnail=true] img{object-fit:contain}
692
732
  `;
693
733
  //#endregion
694
734
  //#region src/image-conversation-node.js
@@ -832,6 +872,10 @@ window.__ModuleLoader__.load({
832
872
  "preferences/status",
833
873
  "preferences/models",
834
874
  "preferences/update",
875
+ "runtime/status",
876
+ "runtime/install",
877
+ "runtime/remove",
878
+ "runtime/cancel",
835
879
  "reset-credit/inspect",
836
880
  "reset-credit/prepare",
837
881
  "reset-credit/consume",
@@ -4462,6 +4506,24 @@ window.__ModuleLoader__.load({
4462
4506
  //#endregion
4463
4507
  //#region src/client-locales.js
4464
4508
  const zh = {
4509
+ runtimeLoading: "正在检查组件…",
4510
+ runtimeRestart: "组件已更新,请重启 DSH 后使用。",
4511
+ runtime_installing: "正在通过 DSH 安装组件…",
4512
+ runtime_removing: "正在通过 DSH 卸载组件…",
4513
+ runtime_applying: "正在应用安装结果…",
4514
+ runtimeInstall: "安装组件",
4515
+ runtimeRemove: "卸载组件",
4516
+ runtimeCancel: "取消安装",
4517
+ runtimeKeep: "保留",
4518
+ runtimeConfirmRemove: "确认卸载",
4519
+ runtimeRefresh: "刷新状态",
4520
+ runtimeActive: "有 Codex 子任务正在运行,结束后才能安装或卸载。",
4521
+ runtimeConfirm: "卸载后将切回 DSH 子任务。普通订阅聊天、生图、登录和草稿保留。确定卸载可选组件?",
4522
+ runtimeFailed: "操作未完成。请刷新状态后重试;详细原因可在 DSH 插件管理中查看。",
4523
+ runtimeBuildBlocked: "DSH 拦截了依赖安装脚本。请在官方插件管理中审核后再安装。",
4524
+ runtimeCancelled: "安装已取消,可重新安装。",
4525
+ runtimeUnavailable: "当前宿主未提供组件管理接口,请更新 DSH 或按说明手动管理。",
4526
+ runtimeInstallHint: "此处只管理官方 Codex 子任务组件,由 DSH 负责安装与卸载。安装后不会自动开启子任务;仅切回 DSH 不会卸载组件。",
4465
4527
  compactionTitle: "云端压缩",
4466
4528
  compaction_dsh: "关闭",
4467
4529
  compaction_cloud: "开启",
@@ -4474,6 +4536,11 @@ window.__ModuleLoader__.load({
4474
4536
  connectionHint: "默认 SSE。WebSocket 实验性复用连接与上下文传输;连接失败可回退 SSE。下次请求生效,不扩大上下文容量。",
4475
4537
  subagentBackendHint: "Codex 复用订阅登录和工作区权限;默认继承当前订阅模型。开启 DSH 子任务模型选择并新建会话后,可在聊天中指定允许的订阅模型与推理档位;非订阅会话需指定。共享上下文子任务仍用 DSH。",
4476
4538
  subagentBackendUnavailable: "当前宿主缺少子代理服务,请更新 DSH。",
4539
+ subagentRuntimeMissing: "尚未准备 Codex 子任务运行时,仅影响此选项。普通订阅对话无需安装。",
4540
+ subagentRuntimePrepare: "准备方法(含离线说明)",
4541
+ subagentRuntimeInstalled: "已检测到 Codex 子任务组件;启动时会检查可执行文件。",
4542
+ subagentRuntimeManage: "安装、卸载与存储",
4543
+ subagentRuntimeCacheHint: "卸载后安装缓存可能仍保留,由 DSH 或 Portable 统一管理。此处不会删除登录、草稿或生成图片。",
4477
4544
  sketchRecovered: "已恢复未保存草稿",
4478
4545
  sketchRecoveryFailed: "恢复检查点保存失败,请手动保存或导出草稿。",
4479
4546
  sketchStorageBlocked: "草稿正被其他窗口占用,请关闭其他草图窗口后重试。",
@@ -4890,6 +4957,24 @@ window.__ModuleLoader__.load({
4890
4957
  imageRemoveAnnotation: "删除标注"
4891
4958
  };
4892
4959
  const en = {
4960
+ runtimeLoading: "Checking component…",
4961
+ runtimeRestart: "Component changed. Restart DSH before using it.",
4962
+ runtime_installing: "Installing through DSH…",
4963
+ runtime_removing: "Uninstalling through DSH…",
4964
+ runtime_applying: "Applying installation…",
4965
+ runtimeInstall: "Install component",
4966
+ runtimeRemove: "Uninstall component",
4967
+ runtimeCancel: "Cancel installation",
4968
+ runtimeKeep: "Keep",
4969
+ runtimeConfirmRemove: "Confirm uninstall",
4970
+ runtimeRefresh: "Refresh status",
4971
+ runtimeActive: "Codex subtasks are running. Wait for them to finish before installing or uninstalling.",
4972
+ runtimeConfirm: "Uninstalling switches subtasks back to DSH. Subscription chat, images, sign-in and sketches stay available. Uninstall this optional component?",
4973
+ runtimeFailed: "The operation did not complete. Refresh and retry; see DSH plugin management for details.",
4974
+ runtimeBuildBlocked: "DSH blocked dependency build scripts. Review them in the official plugin manager before installing.",
4975
+ runtimeCancelled: "Installation cancelled. You can try again.",
4976
+ runtimeUnavailable: "This host has no component management interface. Update DSH or use the manual instructions.",
4977
+ runtimeInstallHint: "DSH installs and removes the official Codex subtask component here. Installation does not enable subtasks automatically. Switching to DSH alone does not uninstall it.",
4893
4978
  compactionTitle: "Cloud compaction",
4894
4979
  compaction_dsh: "Off",
4895
4980
  compaction_cloud: "On",
@@ -4902,6 +4987,11 @@ window.__ModuleLoader__.load({
4902
4987
  connectionHint: "SSE by default. Experimental WebSocket reuses connections and context transfers and can fall back to SSE on connection failure. Applies to the next request; context limits stay the same.",
4903
4988
  subagentBackendHint: "Codex reuses subscription login and workspace permissions, inheriting the current subscription model by default. Enable DSH subtask model selection and start a new session to request an allowed subscription model and reasoning effort in chat; other model sessions require an explicit selection. Shared-context subtasks stay in DSH.",
4904
4989
  subagentBackendUnavailable: "Subagent services are unavailable. Update DSH to use this option.",
4990
+ subagentRuntimeMissing: "Codex subtask runtime is not prepared. Subscription chat does not require it.",
4991
+ subagentRuntimePrepare: "Setup and offline instructions",
4992
+ subagentRuntimeInstalled: "Codex subtask component detected; its executable is checked on startup.",
4993
+ subagentRuntimeManage: "Install, uninstall and storage",
4994
+ subagentRuntimeCacheHint: "Package caches may remain after uninstalling. DSH or Portable manages them centrally. Sign-in, sketches and generated images are not deleted here.",
4905
4995
  sketchRecovered: "Unsaved sketch recovered",
4906
4996
  sketchRecoveryFailed: "Recovery checkpoint failed. Save or export your draft.",
4907
4997
  sketchStorageBlocked: "Draft storage is in use. Close other sketch windows and retry.",
@@ -5536,6 +5626,14 @@ window.__ModuleLoader__.load({
5536
5626
  @container (max-width:480px){.codexSubscriptionAccountRow,.codexSubscriptionSectionHead{align-items:flex-start;flex-direction:column}
5537
5627
  .codexSubscriptionActions{width:100%}
5538
5628
  .codexSubscriptionSearchChoices{grid-template-columns:1fr}}
5629
+ .codexSubscriptionRuntime{font-size:12px;line-height:1.6;margin-top:12px;color:var(--dsw-alias-label-secondary)}
5630
+ .codexSubscriptionRuntime summary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-primary);border-radius:6px}
5631
+ .codexSubscriptionRuntime summary:focus-visible{outline:2px solid currentColor;outline-offset:4px}
5632
+ .codexSubscriptionRuntime p{margin:8px 0}
5633
+ .codexSubscriptionRuntime .codexSubscriptionPreference{flex-wrap:wrap;align-items:flex-start}
5634
+ .codexSubscriptionRuntime .codexSubscriptionPreference>span{flex:1;min-width:180px;font-size:12px;color:var(--dsw-alias-label-secondary)}
5635
+ .codexSubscriptionRuntime button{flex-shrink:0;white-space:nowrap}
5636
+ .codexSubscriptionRuntime code{display:block;overflow-wrap:anywhere;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;user-select:text}
5539
5637
  @media(max-width:640px){.codexSubscriptionCard{padding:14px}}
5540
5638
  `;
5541
5639
  //#endregion
@@ -6672,6 +6770,7 @@ window.__ModuleLoader__.load({
6672
6770
  let modelRefreshStarted = false;
6673
6771
  let disposed = false;
6674
6772
  let subagentBackendAvailable = false;
6773
+ let subagentRuntimeInstalled = false;
6675
6774
  const sameModels = (left, right) => left.length === right.length && left.every((model, index) => JSON.stringify(model) === JSON.stringify(right[index]));
6676
6775
  const nativeSnapshot = () => scope.getSnapshot();
6677
6776
  const read = () => {
@@ -6689,6 +6788,7 @@ window.__ModuleLoader__.load({
6689
6788
  compactionMode: value?.compactionMode === "cloud" ? "cloud" : "dsh",
6690
6789
  subagentBackend: value?.subagentBackend === "codex" ? "codex" : "dsh",
6691
6790
  subagentBackendAvailable,
6791
+ subagentRuntimeInstalled,
6692
6792
  quickQuotaMode: normalizeQuickQuotaMode(value?.[QUICK_QUOTA_MODE_FIELD], value?.[LEGACY_QUICK_QUOTA_FIELD]),
6693
6793
  searchProvider: normalizeSearchProvider(value?.[SEARCH_PROVIDER_FIELD]),
6694
6794
  speedMode: normalizeSpeedMode(value?.[SPEED_MODE_FIELD]),
@@ -6723,6 +6823,7 @@ window.__ModuleLoader__.load({
6723
6823
  });
6724
6824
  const acceptFallback = (value) => {
6725
6825
  subagentBackendAvailable = value?.subagentBackendAvailable === true;
6826
+ subagentRuntimeInstalled = value?.subagentRuntimeInstalled === true;
6726
6827
  if (!modelRefreshStarted) {
6727
6828
  contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
6728
6829
  verbosityModels = Array.isArray(value?.verbosityModels) ? value.verbosityModels : [];
@@ -6760,6 +6861,7 @@ window.__ModuleLoader__.load({
6760
6861
  const value = unwrap(await rpc.call(CHANNEL, "preferences/status", {}));
6761
6862
  if (current !== generation || disposed) return;
6762
6863
  subagentBackendAvailable = value?.subagentBackendAvailable === true;
6864
+ subagentRuntimeInstalled = value?.subagentRuntimeInstalled === true;
6763
6865
  if (nativeSnapshot().status === "ready") {
6764
6866
  if (!modelRefreshStarted) {
6765
6867
  contextModels = Array.isArray(value?.contextModels) ? value.contextModels : [];
@@ -7132,12 +7234,19 @@ window.__ModuleLoader__.load({
7132
7234
  //#region src/sidebar-quota.js
7133
7235
  const isDisplayableWindow = (window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent >= 0 && window.remainingPercent <= 100 && Number.isFinite(window?.windowSeconds) && window.windowSeconds > 0;
7134
7236
  const normalized = (value) => String(value ?? "").toLocaleLowerCase("en-US").replaceAll(/[^a-z0-9]+/gu, "-");
7135
- const limitMatchesModel = (limit, model) => {
7237
+ const exactModelLimit = (limit, model) => {
7238
+ const id = normalized(model);
7239
+ return id.length > 0 && [limit?.id, limit?.name].some((value) => typeof value === "string" && normalized(value) === id);
7240
+ };
7241
+ const limitMatchesModel = (limit, model, hasExactLimit) => {
7242
+ if (hasExactLimit) return exactModelLimit(limit, model);
7243
+ if (normalized(model) === "gpt-reserve") return false;
7136
7244
  if (/\bspark\b/u.test(normalized(model))) return /\bspark\b/u.test(normalized(`${limit?.id ?? ""} ${limit?.name ?? ""}`));
7137
7245
  return limit?.id === "codex";
7138
7246
  };
7139
7247
  function selectModelQuotaWindows(usage, model) {
7140
- return (Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : []).map((selected) => ({
7248
+ const hasExactLimit = Array.isArray(usage?.rateLimits) && usage.rateLimits.some((limit) => exactModelLimit(limit, model));
7249
+ return (Array.isArray(usage?.rateLimits) ? usage.rateLimits.filter((limit) => limitMatchesModel(limit, model, hasExactLimit) && Array.isArray(limit.windows)).flatMap((limit) => limit.windows).filter(isDisplayableWindow) : []).map((selected) => ({
7141
7250
  remainingPercent: selected.remainingPercent,
7142
7251
  windowSeconds: selected.windowSeconds,
7143
7252
  ...Number.isSafeInteger(selected.resetsAt) ? { resetsAt: selected.resetsAt } : {},
@@ -7146,7 +7255,7 @@ window.__ModuleLoader__.load({
7146
7255
  }
7147
7256
  //#endregion
7148
7257
  //#region src/version.js
7149
- const PACKAGE_VERSION = "2.1.2";
7258
+ const PACKAGE_VERSION = "2.1.3";
7150
7259
  //#endregion
7151
7260
  //#region src/client-recovery.js
7152
7261
  async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
@@ -8003,6 +8112,158 @@ window.__ModuleLoader__.load({
8003
8112
  ] });
8004
8113
  }
8005
8114
  //#endregion
8115
+ //#region src/client-runtime-management.jsx
8116
+ function RuntimeManagement({ rpc, preference, t }) {
8117
+ const [state, setState] = (0, react.useState)();
8118
+ const [error, setError] = (0, react.useState)(false);
8119
+ const [sending, setSending] = (0, react.useState)(false);
8120
+ const [confirm, setConfirm] = (0, react.useState)(false);
8121
+ const alive = (0, react.useRef)(false);
8122
+ const busy = [
8123
+ "installing",
8124
+ "removing",
8125
+ "applying"
8126
+ ].includes(state?.phase);
8127
+ const read = async () => {
8128
+ try {
8129
+ const result = await rpc.call(CHANNEL, "runtime/status", {});
8130
+ if (!alive.current) return;
8131
+ if (!result.ok) {
8132
+ setError(true);
8133
+ return;
8134
+ }
8135
+ setState(result.value);
8136
+ setError(false);
8137
+ } catch {
8138
+ if (alive.current) setError(true);
8139
+ }
8140
+ };
8141
+ (0, react.useEffect)(() => {
8142
+ alive.current = true;
8143
+ read();
8144
+ return () => {
8145
+ alive.current = false;
8146
+ };
8147
+ }, [rpc]);
8148
+ (0, react.useEffect)(() => {
8149
+ if (!busy) return;
8150
+ let stopped = false, timer;
8151
+ const poll = async () => {
8152
+ await read();
8153
+ if (!stopped) timer = setTimeout(poll, 1500);
8154
+ };
8155
+ timer = setTimeout(poll, 1500);
8156
+ return () => {
8157
+ stopped = true;
8158
+ clearTimeout(timer);
8159
+ };
8160
+ }, [busy, rpc]);
8161
+ const act = async (action) => {
8162
+ if (sending) return;
8163
+ setSending(true);
8164
+ setError(false);
8165
+ setConfirm(false);
8166
+ try {
8167
+ const result = await rpc.call(CHANNEL, `runtime/${action}`, {});
8168
+ if (!alive.current) return;
8169
+ if (!result.ok) {
8170
+ await read();
8171
+ if (alive.current) setError(true);
8172
+ return;
8173
+ }
8174
+ setState(result.value);
8175
+ if (action === "remove") preference.load();
8176
+ } catch {
8177
+ if (alive.current) setError(true);
8178
+ } finally {
8179
+ if (alive.current) setSending(false);
8180
+ }
8181
+ };
8182
+ const locked = sending || busy || state?.restartRequired || state?.active > 0;
8183
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8184
+ className: "codexSubscriptionRuntime",
8185
+ children: [
8186
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8187
+ className: "codexSubscriptionPreference",
8188
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8189
+ role: "status",
8190
+ children: t(!state ? "runtimeLoading" : state.restartRequired ? "runtimeRestart" : busy ? `runtime_${state.phase}` : state.installed ? "subagentRuntimeInstalled" : "subagentRuntimeMissing")
8191
+ }), state?.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
8192
+ type: "button",
8193
+ variant: "outline",
8194
+ disabled: locked || state.installed && !state.removable,
8195
+ onClick: () => state.installed ? setConfirm(true) : void act("install"),
8196
+ children: t(state.installed ? "runtimeRemove" : "runtimeInstall")
8197
+ }) : null]
8198
+ }),
8199
+ state?.active > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("runtimeActive") }) : null,
8200
+ busy && state.phase === "installing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
8201
+ type: "button",
8202
+ variant: "outline",
8203
+ disabled: sending,
8204
+ onClick: () => {
8205
+ act("cancel");
8206
+ },
8207
+ children: t("runtimeCancel")
8208
+ }) : null,
8209
+ confirm ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8210
+ role: "group",
8211
+ "aria-label": t("runtimeRemove"),
8212
+ children: [
8213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("runtimeConfirm") }),
8214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
8215
+ type: "button",
8216
+ variant: "outline",
8217
+ onClick: () => setConfirm(false),
8218
+ children: t("runtimeKeep")
8219
+ }),
8220
+ " ",
8221
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
8222
+ type: "button",
8223
+ variant: "outline",
8224
+ disabled: locked,
8225
+ onClick: () => {
8226
+ act("remove");
8227
+ },
8228
+ children: t("runtimeConfirmRemove")
8229
+ })
8230
+ ]
8231
+ }) : null,
8232
+ error || state?.phase === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8233
+ role: "alert",
8234
+ children: t(state?.error === "build-blocked" ? "runtimeBuildBlocked" : "runtimeFailed")
8235
+ }) : null,
8236
+ state?.phase === "cancelled" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8237
+ role: "status",
8238
+ children: t("runtimeCancelled")
8239
+ }) : null,
8240
+ !busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
8241
+ type: "button",
8242
+ variant: "outline",
8243
+ disabled: sending,
8244
+ onClick: () => {
8245
+ read();
8246
+ preference.load();
8247
+ },
8248
+ children: t("runtimeRefresh")
8249
+ }) : null,
8250
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [
8251
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("subagentRuntimeManage") }),
8252
+ state && !state.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("runtimeUnavailable") }) : null,
8253
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("runtimeInstallHint") }),
8254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: "@deepseek-ai/dsh-subagent-codex@0.1.5-rc.2" }),
8255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("subagentRuntimeCacheHint") }),
8256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
8257
+ href: "https://github.com/WSL043/dsh-codex-subscription/blob/main/README.md#codex-subtask-runtime",
8258
+ target: "_blank",
8259
+ rel: "noreferrer",
8260
+ children: t("subagentRuntimePrepare")
8261
+ })
8262
+ ] })
8263
+ ]
8264
+ });
8265
+ }
8266
+ //#endregion
8006
8267
  //#region src/context-draft-state.js
8007
8268
  const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value ?? {}, key);
8008
8269
  /**
@@ -8274,7 +8535,7 @@ window.__ModuleLoader__.load({
8274
8535
  ]
8275
8536
  });
8276
8537
  }
8277
- function PreferencesCard({ preference, t, section = "display" }) {
8538
+ function PreferencesCard({ preference, rpc, t, section = "display" }) {
8278
8539
  const snapshot = usePreferenceSnapshot(preference);
8279
8540
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8280
8541
  className: section === "advanced" ? "codexSubscriptionAdvancedPreferences" : "codexSubscriptionCard codexSubscriptionPreferencesCard",
@@ -8371,10 +8632,10 @@ window.__ModuleLoader__.load({
8371
8632
  })]
8372
8633
  })
8373
8634
  }),
8374
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
8635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8375
8636
  className: "codexSubscriptionCard codexSubscriptionPreferencesCard",
8376
8637
  "aria-label": t("subagentBackendTitle"),
8377
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8638
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8378
8639
  className: "codexSubscriptionPreference",
8379
8640
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8380
8641
  className: "codexSubscriptionPreferenceCopy",
@@ -8400,14 +8661,18 @@ window.__ModuleLoader__.load({
8400
8661
  type: "radio",
8401
8662
  name: "codex-subagent-backend",
8402
8663
  checked: snapshot.subagentBackend === value,
8403
- disabled: !snapshot.writable || !snapshot.subagentBackendAvailable,
8664
+ disabled: !snapshot.writable || !snapshot.subagentBackendAvailable || value === "codex" && !snapshot.subagentRuntimeInstalled,
8404
8665
  onChange: () => {
8405
8666
  preference.set({ subagentBackend: value });
8406
8667
  }
8407
8668
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`subagentBackend_${value}`) })]
8408
8669
  }, value))
8409
8670
  })]
8410
- })
8671
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RuntimeManagement, {
8672
+ rpc,
8673
+ preference,
8674
+ t
8675
+ })]
8411
8676
  })
8412
8677
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickQuotaPreference, {
8413
8678
  preference,
@@ -9453,6 +9718,7 @@ window.__ModuleLoader__.load({
9453
9718
  preference
9454
9719
  }, resetKey),
9455
9720
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferencesCard, {
9721
+ rpc,
9456
9722
  preference,
9457
9723
  t
9458
9724
  })
@@ -9465,6 +9731,7 @@ window.__ModuleLoader__.load({
9465
9731
  hidden: tab !== "advanced",
9466
9732
  children: [
9467
9733
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreferencesCard, {
9734
+ rpc,
9468
9735
  preference,
9469
9736
  t,
9470
9737
  section: "advanced"
@@ -9594,9 +9861,15 @@ window.__ModuleLoader__.load({
9594
9861
  else ctx.inject(["remote.session"], installDirectorySlots);
9595
9862
  const conversation = ctx.get("conversation");
9596
9863
  const uiConversation = ctx.get("uiConversation");
9597
- const sketchOpeners = /* @__PURE__ */ new Map();
9864
+ const sketchOpeners = createSessionOpeners();
9598
9865
  const sketchSessions = createSketchSessionRegistry();
9599
- ctx.effect(() => () => sketchSessions.dispose(), "codex-subscription: sketch sessions");
9866
+ const lifetime = new AbortController();
9867
+ ctx.effect(() => () => {
9868
+ lifetime.abort();
9869
+ imageViewer.close();
9870
+ sketchOpeners.clear();
9871
+ sketchSessions.dispose();
9872
+ }, "codex-subscription: sketch sessions");
9600
9873
  ctx.inject(["inputTriggers"], (triggerContext) => triggerContext.effect(() => triggerContext.get("inputTriggers").registerSource(createSketchTrigger({
9601
9874
  enabled: () => {
9602
9875
  const value = preference.getSnapshot();
@@ -9632,33 +9905,38 @@ window.__ModuleLoader__.load({
9632
9905
  const openSketchImage = (sessionId) => async (src, name) => {
9633
9906
  const settings = preference.getSnapshot(), open = sketchOpeners.get(sessionId);
9634
9907
  if (!settings.imageSketch || !settings.imageEditing || !open) throw Error("Sketch unavailable");
9635
- const response = await fetch(src);
9908
+ const response = await fetch(src, { signal: lifetime.signal });
9636
9909
  if (!response.ok) throw Error("Image unavailable");
9637
9910
  const blob = await response.blob();
9638
9911
  if (blob.size > 20 * 1024 * 1024) throw Error("Image too large");
9912
+ lifetime.signal.throwIfAborted();
9913
+ const liveOpen = sketchOpeners.get(sessionId);
9914
+ if (!liveOpen || !preference.getSnapshot().imageSketch || !preference.getSnapshot().imageEditing) throw Error("Sketch unavailable");
9639
9915
  imageViewer.close();
9640
- open("sketch", document.activeElement, new File([blob], name || "image.png", { type: blob.type || "image/png" }));
9916
+ liveOpen("sketch", document.activeElement, new File([blob], name || "image.png", { type: blob.type || "image/png" }));
9641
9917
  };
9642
9918
  const attachForEdit = (sessionId) => async (src, filename, draft, annotations = [], referenceName, sourceInDraft = false) => {
9643
9919
  if (!preference.getSnapshot().imageEditing) throw new Error("Image editing is disabled");
9644
- const actx = sessions.scope(sessionId);
9645
- if (actx === void 0 || conversation.input?.for === void 0) throw new Error("This DSH version does not provide the image composer bridge");
9646
- const response = await fetch(src);
9647
- if (!response.ok) throw new Error("Could not read generated image");
9648
- const blob = await response.blob();
9649
- const files = sourceInDraft ? [] : [new File([blob], filename, { type: blob.type || "image/png" })];
9650
- if (annotations.length > 0) {
9651
- const reference = await createAnnotatedImageReference(blob, annotations);
9652
- files.push(new File([reference], referenceName, { type: "image/png" }));
9653
- }
9654
- const input = conversation.input.for(actx);
9655
- if (!preference.getSnapshot().imageEditing) throw new Error("Image editing is disabled");
9656
- if (files.length) attachImageFiles(conversation, input, files, sessionId);
9657
- sessions.open(sessionId);
9658
- if (sourceInDraft && !annotations.length) return;
9659
- if (!input.state.getSnapshot().draft.trim()) input.setDraft(draft);
9660
- else if (annotations.length) if (!input.state.getSnapshot().occurrences?.length) appendImagePrompt(input, draft);
9661
- else input.notify("info", draft);
9920
+ return withComposerSession(sessions, sessionId, async (actx) => {
9921
+ if (conversation.input?.for === void 0) throw new Error("This DSH version does not provide the image composer bridge");
9922
+ const response = await fetch(src, { signal: lifetime.signal });
9923
+ if (!response.ok) throw new Error("Could not read generated image");
9924
+ const blob = await response.blob();
9925
+ const files = sourceInDraft ? [] : [new File([blob], filename, { type: blob.type || "image/png" })];
9926
+ if (annotations.length > 0) {
9927
+ const reference = await createAnnotatedImageReference(blob, annotations);
9928
+ files.push(new File([reference], referenceName, { type: "image/png" }));
9929
+ }
9930
+ lifetime.signal.throwIfAborted();
9931
+ const input = conversation.input.for(actx);
9932
+ if (!preference.getSnapshot().imageEditing) throw new Error("Image editing is disabled");
9933
+ if (files.length) attachImageFiles(conversation, input, files, sessionId);
9934
+ openComposerSession(sessions, ctx.get("uiWorkspace"), sessionId);
9935
+ if (sourceInDraft && !annotations.length) return;
9936
+ if (!input.state.getSnapshot().draft.trim()) input.setDraft(draft);
9937
+ else if (annotations.length) if (!input.state.getSnapshot().occurrences?.length) appendImagePrompt(input, draft);
9938
+ else input.notify("info", draft);
9939
+ });
9662
9940
  };
9663
9941
  const nativeAttachments = () => ctx.slots.entries("conversation.input.attachments").find((entry) => entry.component !== ComposerImagePreviews && entry.locale === "conversation" && !entry.inject && !entry.store && !entry.children);
9664
9942
  const watchNativeAttachments = (callback) => ctx.slots.subscribe("conversation.input.attachments", callback);
@@ -9706,12 +9984,7 @@ window.__ModuleLoader__.load({
9706
9984
  sessionId,
9707
9985
  rpc,
9708
9986
  sessionState: sketchSessions.get(sessionId),
9709
- registerOpen: (callback) => {
9710
- sketchOpeners.set(sessionId, callback);
9711
- return () => {
9712
- if (sketchOpeners.get(sessionId) === callback) sketchOpeners.delete(sessionId);
9713
- };
9714
- },
9987
+ registerOpen: (callback) => sketchOpeners.register(sessionId, callback),
9715
9988
  attachSketch: (blob) => {
9716
9989
  const current = preference.getSnapshot();
9717
9990
  if (!current.imageSketch || !current.imageEditing) throw new Error("Sketch editing is disabled");