@michengai/dsh-btw 0.1.22 → 0.1.23

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/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## [0.1.23] - 2026-10-15
4
+
5
+ - Relax hardcoded timeout: Replace the strict 90s timer with a 30-minute watchdog, supporting long chain-of-thought and max-reasoning models while safeguarding concurrency slots against stalled connections (#6).
6
+ - Prominent stop button during answering: Display a dedicated stop button with alert styling during generation, allowing users to manually abort reasoning and release subagent resources immediately (#6).
7
+ - Smart auto-collapse for older side-question bubbles: Automatically collapse older bubbles when new side questions arrive, keeping the input area neat while preserving manual expansion controls and robust focus recovery.
8
+ - Seamless dual-generation host compatibility: Retain strict typings for DSH 0.2.1-alpha.3+ `startActivation` while providing a robust `subagents.start` fallback for DSH 0.2.0-rc.2 and earlier.
4
9
 
5
10
  ## [0.1.22] - 2026-10-15
6
11
 
@@ -1,6 +1,11 @@
1
1
  # 更新日志
2
2
 
3
- ## 未发布
3
+ ## [0.1.23] - 2026-10-15
4
+
5
+ - 放宽硬超时限制:默认超时由写死的 90s 调整为 30 分钟兜底看门狗,支持长思维链与深度推理模型充分思考;同时保留长连接死锁自动回收机制保护并发槽位 (#6)。
6
+ - 回答中提供醒目的「停止回答」按钮:在旁问处于生成阶段展示独立停止图标与警示色悬浮高亮,用户可随时手动终止推理并释放子代理 (#6)。
7
+ - 优化旁问气泡自动折叠交互:连续提出新旁问时旧气泡自动折叠、最新气泡保持展开,避免撑大输入区;支持用户手动自由切换折叠/展开状态,新气泡到达后稳定重新夺回展开焦点。
8
+ - 完善双代宿主自适应兼容:主路径保留 DSH 0.2.1-alpha.3+ 的强类型 `startActivation`,并为 0.2.0-rc.2 及更早宿主恢复严谨的 `subagents.start` 传统调用回退分支。
4
9
 
5
10
  ## [0.1.22] - 2026-10-15
6
11
 
package/lib/client.js CHANGED
@@ -467,6 +467,11 @@ var MessageCircle = createLucideIcon("MessageCircle", [
467
467
  ["path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z", key: "vv11sd" }]
468
468
  ]);
469
469
 
470
+ // node_modules/lucide-react/dist/esm/icons/square.js
471
+ var Square = createLucideIcon("Square", [
472
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
473
+ ]);
474
+
470
475
  // node_modules/lucide-react/dist/esm/icons/x.js
471
476
  var X = createLucideIcon("X", [
472
477
  ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
@@ -503,6 +508,7 @@ var zh = {
503
508
  "action.collapse": "\u6298\u53E0\u56DE\u7B54",
504
509
  "action.close": "\u5173\u95ED\u65C1\u95EE",
505
510
  "action.retryClose": "\u91CD\u8BD5\u5173\u95ED",
511
+ "action.stop": "\u505C\u6B62\u56DE\u7B54",
506
512
  "status.answering": "\u6B63\u5728\u56DE\u7B54\u2026",
507
513
  "status.closing": "\u6B63\u5728\u5173\u95ED\u2026",
508
514
  "command.hint": "\u65C1\u95EE\u5185\u5BB9",
@@ -555,6 +561,7 @@ var en = {
555
561
  "action.collapse": "Collapse answer",
556
562
  "action.close": "Close side question",
557
563
  "action.retryClose": "Retry closing",
564
+ "action.stop": "Stop answering",
558
565
  "status.answering": "Answering\u2026",
559
566
  "status.closing": "Closing\u2026",
560
567
  "command.hint": "Side question",
@@ -13452,8 +13459,7 @@ function AnswerMarkdown({ text: text7, labels, imageLabel }) {
13452
13459
  }
13453
13460
 
13454
13461
  // src/client/BubbleView.tsx
13455
- function BubbleView({ item, close, t }) {
13456
- const [collapsed, setCollapsed] = (0, import_react5.useState)(false);
13462
+ function BubbleView({ item, close, t, collapsed, onToggleCollapse }) {
13457
13463
  const [copied, setCopied] = (0, import_react5.useState)(false);
13458
13464
  const [copyError, setCopyError] = (0, import_react5.useState)(false);
13459
13465
  const busy = item.phase === "answering" || item.phase === "closing";
@@ -13470,17 +13476,48 @@ function BubbleView({ item, close, t }) {
13470
13476
  setCopyError(true);
13471
13477
  }
13472
13478
  };
13479
+ const stopOrCloseTitle = item.closeFailed ? t("action.retryClose") : t(item.phase === "answering" ? "action.stop" : "action.close");
13473
13480
  return /* @__PURE__ */ import_react5.default.createElement("article", { className: "btw-bubble", "data-phase": item.phase, "aria-label": t("bubble.label") }, /* @__PURE__ */ import_react5.default.createElement("header", { className: "btw-header" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "btw-symbol" }, /* @__PURE__ */ import_react5.default.createElement(MessageCircle, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("span", { className: "btw-question" }, item.question), /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-actions" }, item.answer && /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(copied ? "action.copied" : "action.copy"), "aria-label": t("action.copy"), onClick: () => {
13474
13481
  void copy();
13475
- } }, copied ? /* @__PURE__ */ import_react5.default.createElement(Check, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(Copy, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(collapsed ? "action.expand" : "action.collapse"), "aria-label": t(collapsed ? "action.expand" : "action.collapse"), "aria-expanded": !collapsed, onClick: () => setCollapsed(!collapsed) }, collapsed ? /* @__PURE__ */ import_react5.default.createElement(ChevronUp, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(ChevronDown, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(item.closeFailed ? "action.retryClose" : "action.close"), "aria-label": t("action.close"), disabled: item.phase === "closing", onClick: close }, /* @__PURE__ */ import_react5.default.createElement(X, { size: 16 })))), !collapsed && item.reference && /* @__PURE__ */ import_react5.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, t("selection.reference")), /* @__PURE__ */ import_react5.default.createElement("blockquote", null, item.reference)), busy && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-status", role: "status" }, /* @__PURE__ */ import_react5.default.createElement(LoaderCircle, { size: 14, className: "btw-spinner" }), t(item.phase === "closing" ? "status.closing" : "status.answering")), !collapsed && item.answer && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-answer" }, /* @__PURE__ */ import_react5.default.createElement(AnswerMarkdown, { text: item.answer, labels, imageLabel: t("bubble.image") })), item.error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, item.closeFailed ? t("error.close", { detail: item.error }) : item.error), copyError && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.copy")));
13482
+ } }, copied ? /* @__PURE__ */ import_react5.default.createElement(Check, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(Copy, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(collapsed ? "action.expand" : "action.collapse"), "aria-label": t(collapsed ? "action.expand" : "action.collapse"), "aria-expanded": !collapsed, onClick: onToggleCollapse }, collapsed ? /* @__PURE__ */ import_react5.default.createElement(ChevronUp, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(ChevronDown, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement(
13483
+ "button",
13484
+ {
13485
+ type: "button",
13486
+ className: item.phase === "answering" ? "btw-action-stop" : void 0,
13487
+ title: stopOrCloseTitle,
13488
+ "aria-label": stopOrCloseTitle,
13489
+ disabled: item.phase === "closing",
13490
+ onClick: close
13491
+ },
13492
+ item.phase === "answering" ? /* @__PURE__ */ import_react5.default.createElement(Square, { size: 14 }) : /* @__PURE__ */ import_react5.default.createElement(X, { size: 16 })
13493
+ ))), !collapsed && item.reference && /* @__PURE__ */ import_react5.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, t("selection.reference")), /* @__PURE__ */ import_react5.default.createElement("blockquote", null, item.reference)), busy && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-status", role: "status" }, /* @__PURE__ */ import_react5.default.createElement(LoaderCircle, { size: 14, className: "btw-spinner" }), t(item.phase === "closing" ? "status.closing" : "status.answering")), !collapsed && item.answer && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-answer" }, /* @__PURE__ */ import_react5.default.createElement(AnswerMarkdown, { text: item.answer, labels, imageLabel: t("bubble.image") })), item.error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, item.closeFailed ? t("error.close", { detail: item.error }) : item.error), copyError && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.copy")));
13476
13494
  }
13477
13495
  function BubbleDock({ store, sessionId, t }) {
13478
13496
  const snapshot = (0, import_react5.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
13479
13497
  const items = snapshot.filter((item) => item.sessionId === sessionId);
13498
+ const latestId = items.at(-1)?.id;
13499
+ const [manual, setManual] = (0, import_react5.useState)();
13500
+ const hasNewlyAddedBubble = manual !== void 0 && items.some((item) => !manual.knownIds.includes(item.id));
13501
+ const currentIds = items.map((item) => item.id);
13502
+ const isPinnedValid = manual !== void 0 && !hasNewlyAddedBubble && manual.id !== null ? items.some((item) => item.id === manual.id) : false;
13503
+ const effectiveExpandedId = manual === void 0 || hasNewlyAddedBubble ? latestId : manual.id === null ? null : isPinnedValid ? manual.id : latestId;
13480
13504
  if (!items.length) return null;
13481
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-dock" }, items.map((item) => /* @__PURE__ */ import_react5.default.createElement(BubbleView, { key: item.id, item, t, close: () => {
13482
- void store.close(item.id);
13483
- } })));
13505
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-dock" }, items.map((item) => /* @__PURE__ */ import_react5.default.createElement(
13506
+ BubbleView,
13507
+ {
13508
+ key: item.id,
13509
+ item,
13510
+ t,
13511
+ collapsed: item.id !== effectiveExpandedId,
13512
+ onToggleCollapse: () => {
13513
+ if (item.id === effectiveExpandedId) setManual({ id: null, knownIds: currentIds });
13514
+ else setManual({ id: item.id, knownIds: currentIds });
13515
+ },
13516
+ close: () => {
13517
+ void store.close(item.id);
13518
+ }
13519
+ }
13520
+ )));
13484
13521
  }
13485
13522
 
13486
13523
  // src/client/composer.ts
@@ -13729,6 +13766,8 @@ var CSS = `
13729
13766
  .btw-actions button{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:background-color 120ms ease}
13730
13767
  .btw-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
13731
13768
  .btw-actions button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:-2px}
13769
+ .btw-actions button.btw-action-stop{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-primary))}
13770
+ .btw-actions button.btw-action-stop:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-primary))}
13732
13771
  .btw-actions button:disabled{cursor:wait;opacity:.5}
13733
13772
  .btw-status{display:flex;align-items:center;gap:8px;padding:0 14px 12px;color:var(--dsw-alias-label-secondary);font-size:13px}
13734
13773
  .btw-spinner{animation:btw-spin 1.2s linear infinite}@keyframes btw-spin{to{transform:rotate(360deg)}}
@@ -13892,6 +13931,7 @@ lucide-react/dist/esm/icons/chevron-up.js:
13892
13931
  lucide-react/dist/esm/icons/copy.js:
13893
13932
  lucide-react/dist/esm/icons/loader-circle.js:
13894
13933
  lucide-react/dist/esm/icons/message-circle.js:
13934
+ lucide-react/dist/esm/icons/square.js:
13895
13935
  lucide-react/dist/esm/icons/x.js:
13896
13936
  lucide-react/dist/esm/lucide-react.js:
13897
13937
  (**
package/lib/index.js CHANGED
@@ -31,6 +31,7 @@ var zh = {
31
31
  "action.collapse": "\u6298\u53E0\u56DE\u7B54",
32
32
  "action.close": "\u5173\u95ED\u65C1\u95EE",
33
33
  "action.retryClose": "\u91CD\u8BD5\u5173\u95ED",
34
+ "action.stop": "\u505C\u6B62\u56DE\u7B54",
34
35
  "status.answering": "\u6B63\u5728\u56DE\u7B54\u2026",
35
36
  "status.closing": "\u6B63\u5728\u5173\u95ED\u2026",
36
37
  "command.hint": "\u65C1\u95EE\u5185\u5BB9",
@@ -83,6 +84,7 @@ var en = {
83
84
  "action.collapse": "Collapse answer",
84
85
  "action.close": "Close side question",
85
86
  "action.retryClose": "Retry closing",
87
+ "action.stop": "Stop answering",
86
88
  "status.answering": "Answering\u2026",
87
89
  "status.closing": "Closing\u2026",
88
90
  "command.hint": "Side question",
@@ -157,8 +159,9 @@ ${question}`;
157
159
 
158
160
  // src/server/jobs.ts
159
161
  var failure = (error) => ({ kind: "error", text: error instanceof Error ? error.message : String(error) });
162
+ var DEFAULT_TIMEOUT_MS = 18e5;
160
163
  var SideJobs = class {
161
- constructor(start, timeoutMs = 9e4, options = {}) {
164
+ constructor(start, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
162
165
  this.start = start;
163
166
  this.timeoutMs = timeoutMs;
164
167
  this.options = options;
@@ -182,10 +185,10 @@ var SideJobs = class {
182
185
  const cancel = () => job.controller.abort(new Error(t("error.cancelled")));
183
186
  signal?.addEventListener("abort", cancel, { once: true });
184
187
  if (signal?.aborted) cancel();
185
- const timer = setTimeout(() => job.controller.abort(new Error(t("error.timeout"))), this.timeoutMs);
186
- timer.unref?.();
188
+ const timer = this.timeoutMs > 0 ? setTimeout(() => job.controller.abort(new Error(t("error.timeout"))), this.timeoutMs) : void 0;
189
+ timer?.unref?.();
187
190
  job.finished = this.execute(job, { scope, id, question, signal: job.controller.signal, toolFilter: { allow: [] }, context, locale }).finally(() => {
188
- clearTimeout(timer);
191
+ if (timer) clearTimeout(timer);
189
192
  signal?.removeEventListener("abort", cancel);
190
193
  this.release(job);
191
194
  });
@@ -448,50 +451,84 @@ function apply(ctx) {
448
451
  throw new Error(translate(request.locale)("error.provider"));
449
452
  }
450
453
  const label = `michengai-btw:${globalThis.crypto.randomUUID()}`;
451
- const childId = SessionId(globalThis.crypto.randomUUID());
452
454
  labels.add(label);
453
- let childAgent;
454
- try {
455
- const stopCreated = ctx.on("agent/created", ({ agent }) => {
456
- if (agent.id !== childId) return void 0;
457
- childAgent = agent;
458
- arm(agent);
459
- const scoped = agent;
460
- if (scoped.ctx?.systemPrompt) {
461
- scoped.ctx.systemPrompt.section({
462
- name: PERSONA_PREFIX_SECTION,
463
- order: scoped.ctx.systemPrompt.getSectionOrder("DEPLOYMENT_PERSONA_PREFIX"),
464
- text: PERSONA
465
- });
466
- }
467
- if (scoped.ctx?.tools) scoped.ctx.tools.restrict(request.toolFilter);
468
- return void 0;
469
- });
470
- let activation;
455
+ if (typeof ctx.subagents.startActivation === "function") {
456
+ const childId = SessionId(globalThis.crypto.randomUUID());
457
+ let childAgent;
471
458
  try {
472
- activation = await ctx.subagents.startActivation({
473
- provider: "fork",
474
- label,
475
- childId,
476
- request: {
477
- parent: request.context,
478
- prompt: [{ type: "text", text: `\u4EE5\u4E0B\u662F\u552F\u4E00\u9700\u8981\u56DE\u7B54\u7684\u65B0\u95EE\u9898\uFF1B\u5148\u524D\u5185\u5BB9\u4EC5\u4F9B\u53C2\u8003\u3002
459
+ const stopCreated = ctx.on("agent/created", ({ agent }) => {
460
+ if (agent.id !== childId) return void 0;
461
+ childAgent = agent;
462
+ arm(agent);
463
+ const scoped = agent;
464
+ if (scoped.ctx?.systemPrompt) {
465
+ scoped.ctx.systemPrompt.section({
466
+ name: PERSONA_PREFIX_SECTION,
467
+ order: scoped.ctx.systemPrompt.getSectionOrder("DEPLOYMENT_PERSONA_PREFIX"),
468
+ text: PERSONA
469
+ });
470
+ }
471
+ if (scoped.ctx?.tools) scoped.ctx.tools.restrict(request.toolFilter);
472
+ return void 0;
473
+ });
474
+ let activation;
475
+ try {
476
+ activation = await ctx.subagents.startActivation({
477
+ provider: "fork",
478
+ label,
479
+ childId,
480
+ request: {
481
+ parent: request.context,
482
+ prompt: [{ type: "text", text: `\u4EE5\u4E0B\u662F\u552F\u4E00\u9700\u8981\u56DE\u7B54\u7684\u65B0\u95EE\u9898\uFF1B\u5148\u524D\u5185\u5BB9\u4EC5\u4F9B\u53C2\u8003\u3002
479
483
 
480
484
  ${request.question}` }]
481
- },
482
- signal: request.signal,
483
- delivery: "caller"
484
- });
485
- } finally {
486
- stopCreated();
485
+ },
486
+ signal: request.signal,
487
+ delivery: "caller"
488
+ });
489
+ } finally {
490
+ stopCreated();
491
+ }
492
+ return {
493
+ result: activation.result,
494
+ dispose: async () => {
495
+ try {
496
+ await activation.dispose();
497
+ } finally {
498
+ await releaseChild(childAgent);
499
+ labels.delete(label);
500
+ }
501
+ }
502
+ };
503
+ } catch (error) {
504
+ labels.delete(label);
505
+ throw error;
487
506
  }
507
+ }
508
+ const legacySubagents = ctx.subagents;
509
+ if (typeof legacySubagents.start !== "function") {
510
+ labels.delete(label);
511
+ throw new Error(translate(request.locale)("error.provider"));
512
+ }
513
+ try {
514
+ const run = await legacySubagents.start("fork", {
515
+ parent: request.context,
516
+ label,
517
+ signal: request.signal,
518
+ toolFilter: request.toolFilter,
519
+ persona: PERSONA,
520
+ prompt: [{ type: "text", text: `\u4EE5\u4E0B\u662F\u552F\u4E00\u9700\u8981\u56DE\u7B54\u7684\u65B0\u95EE\u9898\uFF1B\u5148\u524D\u5185\u5BB9\u4EC5\u4F9B\u53C2\u8003\u3002
521
+
522
+ ${request.question}` }]
523
+ });
524
+ if (run.localAgent) arm(run.localAgent);
488
525
  return {
489
- result: activation.result,
526
+ result: run.result,
490
527
  dispose: async () => {
491
528
  try {
492
- await activation.dispose();
529
+ await run.dispose();
493
530
  } finally {
494
- await releaseChild(childAgent);
531
+ await releaseChild(run.localAgent);
495
532
  labels.delete(label);
496
533
  }
497
534
  }
@@ -500,7 +537,7 @@ ${request.question}` }]
500
537
  labels.delete(label);
501
538
  throw error;
502
539
  }
503
- }, 9e4, {
540
+ }, DEFAULT_TIMEOUT_MS, {
504
541
  onError: (error) => ctx.logger.warn(error),
505
542
  onIdle: async () => {
506
543
  stopWatch();
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { Bubble, BubbleStore } from './bubbles';
3
3
  import type { BtwTranslate } from '../locales';
4
- export declare function BubbleView({ item, close, t }: {
4
+ export declare function BubbleView({ item, close, t, collapsed, onToggleCollapse }: {
5
5
  item: Bubble;
6
6
  close: () => void;
7
7
  t: BtwTranslate;
8
+ collapsed: boolean;
9
+ onToggleCollapse: () => void;
8
10
  }): React.JSX.Element;
9
11
  export declare function BubbleDock({ store, sessionId, t }: {
10
12
  store: BubbleStore;
@@ -1 +1 @@
1
- export declare const CSS = "\n.btw-reference-preview{display:block;margin-top:6px;max-height:3.2em;overflow:hidden;overflow-wrap:anywhere;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.btw-selection-dialog,.btw-selection-menu{position:fixed;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;font:inherit;font-size:13px;max-height:calc(100dvh - 32px);overflow:auto;box-shadow:0 12px 36px #0003}\n.btw-selection-dialog{width:min(520px,calc(100vw - 32px));margin:auto}.btw-selection-menu{margin:0;width:max-content;max-width:calc(100vw - 16px);padding:0;inset:auto;border-radius:10px;overflow:hidden;border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-family:inherit;font-size:12px;font-weight:500;line-height:16px;box-shadow:0 2px 6px #0001}\n.btw-selection-menu::backdrop{background:transparent}.btw-selection-dialog::backdrop{background:#0004}\n.btw-selection-dialog h3{margin:0 0 12px;font-size:16px}.btw-selection-dialog label{display:flex;flex-direction:column;gap:8px;margin-top:12px}\n.btw-selection-dialog textarea{box-sizing:border-box;width:100%;min-height:96px;resize:vertical;font:inherit;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-base);color:inherit}\n.btw-selection-dialog button,.btw-selection-menu button{font:inherit;color:inherit;background:var(--dsw-alias-interactive-bg-hover);border:0;border-radius:6px;padding:8px 12px;cursor:pointer;min-height:36px}\n.btw-selection-menu [role=\"toolbar\"]{display:flex;align-items:center}.btw-selection-menu button+button{border-left:1px solid var(--dsw-alias-border-l1)}\n.btw-selection-menu button{display:inline-flex;align-items:center;height:28px;min-height:28px;padding:0 8px;border-radius:0;text-align:left;white-space:nowrap;background:transparent}.btw-selection-menu button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.btw-selection-dialog button:disabled{opacity:.5;cursor:default}.btw-selection-dialog :focus-visible,.btw-selection-menu :focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:2px}\n.btw-selection-menu :focus-visible{outline-width:1px;outline-offset:-2px}\n.btw-selection-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.btw-selection-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6;margin:10px 4px}\n.btw-reference{padding:0 14px 10px;font-size:12px}.btw-reference summary{cursor:pointer;color:var(--dsw-alias-label-secondary)}.btw-reference blockquote{margin:8px 0 0;padding:8px;border-left:2px solid var(--dsw-alias-border-l2);white-space:pre-wrap;overflow-wrap:anywhere;max-height:150px;overflow:auto}.btw-selection-dialog .btw-reference{padding:0}\n.btw-dock{display:flex;flex:none;flex-direction:column;gap:8px;width:calc(100% - var(--dsh-composer-side-clearance,0px) - var(--dsh-composer-side-clearance,0px));max-width:var(--dsh-composer-card-max-width,100%);margin:0 auto;max-height:440px;overflow:auto;padding:4px 0 10px;box-sizing:border-box;letter-spacing:0}\n.btw-bubble{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;min-width:0;flex-shrink:0;font-size:13px;line-height:1.6;font-family:var(--dsw-font-family,inherit);letter-spacing:0}\n.btw-header{display:flex;align-items:center;gap:8px;padding:8px 10px 8px 14px;min-width:0;min-height:44px}\n.btw-symbol{display:flex;align-items:center;color:var(--dsw-alias-label-secondary);height:28px;flex-shrink:0}\n.btw-question{flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px;max-height:66px;overflow:auto}\n.btw-actions{display:flex;gap:4px;flex-shrink:0}\n.btw-actions button{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:background-color 120ms ease}\n.btw-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.btw-actions button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:-2px}\n.btw-actions button:disabled{cursor:wait;opacity:.5}\n.btw-status{display:flex;align-items:center;gap:8px;padding:0 14px 12px;color:var(--dsw-alias-label-secondary);font-size:13px}\n.btw-spinner{animation:btw-spin 1.2s linear infinite}@keyframes btw-spin{to{transform:rotate(360deg)}}\n.btw-answer{padding:0 14px 14px;max-height:300px;overflow:auto;overflow-wrap:anywhere;font-size:13px;line-height:1.7}\n.btw-answer>*:first-child{margin-top:0}.btw-answer>*:last-child{margin-bottom:0}\n.btw-answer p{margin:8px 0}.btw-answer h1,.btw-answer h2,.btw-answer h3{font-size:15px;line-height:1.5;margin:12px 0 6px;font-weight:600}\n.btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}.btw-answer .katex-display{max-width:100%;overflow:auto}\n.btw-answer code{font-family:Consolas,monospace;font-size:12px}.btw-answer :not(pre)>code{background:var(--dsw-alias-markdown-code-block);border-radius:4px;padding:1px 3px}.btw-answer a{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}\n.btw-answer table{display:block;max-width:100%;overflow:auto;border-collapse:collapse}.btw-answer th,.btw-answer td{border:1px solid var(--dsw-alias-border-l2);padding:5px 9px}\n.btw-answer ul,.btw-answer ol{padding-left:22px}.btw-answer blockquote{margin:8px 0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary)}\n.btw-error{margin:0;padding:0 14px 12px;font-size:13px;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}\n@media(max-width:480px){.btw-dock{max-height:330px}.btw-header{padding:6px 8px 6px 12px;gap:6px}.btw-answer{padding:0 12px 12px;max-height:230px}.btw-status,.btw-error{padding-left:12px}.btw-actions{gap:0}.btw-actions button{width:36px;height:36px}}\n@media(prefers-reduced-motion:reduce){.btw-spinner{animation:none}.btw-actions button{transition:none}}\n";
1
+ export declare const CSS = "\n.btw-reference-preview{display:block;margin-top:6px;max-height:3.2em;overflow:hidden;overflow-wrap:anywhere;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.btw-selection-dialog,.btw-selection-menu{position:fixed;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;font:inherit;font-size:13px;max-height:calc(100dvh - 32px);overflow:auto;box-shadow:0 12px 36px #0003}\n.btw-selection-dialog{width:min(520px,calc(100vw - 32px));margin:auto}.btw-selection-menu{margin:0;width:max-content;max-width:calc(100vw - 16px);padding:0;inset:auto;border-radius:10px;overflow:hidden;border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-family:inherit;font-size:12px;font-weight:500;line-height:16px;box-shadow:0 2px 6px #0001}\n.btw-selection-menu::backdrop{background:transparent}.btw-selection-dialog::backdrop{background:#0004}\n.btw-selection-dialog h3{margin:0 0 12px;font-size:16px}.btw-selection-dialog label{display:flex;flex-direction:column;gap:8px;margin-top:12px}\n.btw-selection-dialog textarea{box-sizing:border-box;width:100%;min-height:96px;resize:vertical;font:inherit;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-base);color:inherit}\n.btw-selection-dialog button,.btw-selection-menu button{font:inherit;color:inherit;background:var(--dsw-alias-interactive-bg-hover);border:0;border-radius:6px;padding:8px 12px;cursor:pointer;min-height:36px}\n.btw-selection-menu [role=\"toolbar\"]{display:flex;align-items:center}.btw-selection-menu button+button{border-left:1px solid var(--dsw-alias-border-l1)}\n.btw-selection-menu button{display:inline-flex;align-items:center;height:28px;min-height:28px;padding:0 8px;border-radius:0;text-align:left;white-space:nowrap;background:transparent}.btw-selection-menu button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.btw-selection-dialog button:disabled{opacity:.5;cursor:default}.btw-selection-dialog :focus-visible,.btw-selection-menu :focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:2px}\n.btw-selection-menu :focus-visible{outline-width:1px;outline-offset:-2px}\n.btw-selection-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.btw-selection-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6;margin:10px 4px}\n.btw-reference{padding:0 14px 10px;font-size:12px}.btw-reference summary{cursor:pointer;color:var(--dsw-alias-label-secondary)}.btw-reference blockquote{margin:8px 0 0;padding:8px;border-left:2px solid var(--dsw-alias-border-l2);white-space:pre-wrap;overflow-wrap:anywhere;max-height:150px;overflow:auto}.btw-selection-dialog .btw-reference{padding:0}\n.btw-dock{display:flex;flex:none;flex-direction:column;gap:8px;width:calc(100% - var(--dsh-composer-side-clearance,0px) - var(--dsh-composer-side-clearance,0px));max-width:var(--dsh-composer-card-max-width,100%);margin:0 auto;max-height:440px;overflow:auto;padding:4px 0 10px;box-sizing:border-box;letter-spacing:0}\n.btw-bubble{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;min-width:0;flex-shrink:0;font-size:13px;line-height:1.6;font-family:var(--dsw-font-family,inherit);letter-spacing:0}\n.btw-header{display:flex;align-items:center;gap:8px;padding:8px 10px 8px 14px;min-width:0;min-height:44px}\n.btw-symbol{display:flex;align-items:center;color:var(--dsw-alias-label-secondary);height:28px;flex-shrink:0}\n.btw-question{flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px;max-height:66px;overflow:auto}\n.btw-actions{display:flex;gap:4px;flex-shrink:0}\n.btw-actions button{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:background-color 120ms ease}\n.btw-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.btw-actions button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:-2px}\n.btw-actions button.btw-action-stop{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-primary))}\n.btw-actions button.btw-action-stop:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-primary))}\n.btw-actions button:disabled{cursor:wait;opacity:.5}\n.btw-status{display:flex;align-items:center;gap:8px;padding:0 14px 12px;color:var(--dsw-alias-label-secondary);font-size:13px}\n.btw-spinner{animation:btw-spin 1.2s linear infinite}@keyframes btw-spin{to{transform:rotate(360deg)}}\n.btw-answer{padding:0 14px 14px;max-height:300px;overflow:auto;overflow-wrap:anywhere;font-size:13px;line-height:1.7}\n.btw-answer>*:first-child{margin-top:0}.btw-answer>*:last-child{margin-bottom:0}\n.btw-answer p{margin:8px 0}.btw-answer h1,.btw-answer h2,.btw-answer h3{font-size:15px;line-height:1.5;margin:12px 0 6px;font-weight:600}\n.btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}.btw-answer .katex-display{max-width:100%;overflow:auto}\n.btw-answer code{font-family:Consolas,monospace;font-size:12px}.btw-answer :not(pre)>code{background:var(--dsw-alias-markdown-code-block);border-radius:4px;padding:1px 3px}.btw-answer a{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}\n.btw-answer table{display:block;max-width:100%;overflow:auto;border-collapse:collapse}.btw-answer th,.btw-answer td{border:1px solid var(--dsw-alias-border-l2);padding:5px 9px}\n.btw-answer ul,.btw-answer ol{padding-left:22px}.btw-answer blockquote{margin:8px 0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary)}\n.btw-error{margin:0;padding:0 14px 12px;font-size:13px;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}\n@media(max-width:480px){.btw-dock{max-height:330px}.btw-header{padding:6px 8px 6px 12px;gap:6px}.btw-answer{padding:0 12px 12px;max-height:230px}.btw-status,.btw-error{padding-left:12px}.btw-actions{gap:0}.btw-actions button{width:36px;height:36px}}\n@media(prefers-reduced-motion:reduce){.btw-spinner{animation:none}.btw-actions button{transition:none}}\n";
@@ -24,6 +24,7 @@ export declare const zh: {
24
24
  'action.collapse': string;
25
25
  'action.close': string;
26
26
  'action.retryClose': string;
27
+ 'action.stop': string;
27
28
  'status.answering': string;
28
29
  'status.closing': string;
29
30
  'command.hint': string;
@@ -29,6 +29,7 @@ interface JobOptions {
29
29
  /** 卸载后所有实际资源均释放时调用;用于释放执行保护。 */
30
30
  onIdle?: () => void | Promise<void>;
31
31
  }
32
+ export declare const DEFAULT_TIMEOUT_MS = 1800000;
32
33
  /** 管理一次性任务;父会话和请求标识共同构成所有权边界。 */
33
34
  export declare class SideJobs {
34
35
  private readonly start;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-btw",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "DSH 一次性只读旁问插件:基于当前上下文回答,独立气泡,不执行工具 · One-shot read-only side-ask plugin: answers from current context in an independent bubble, no tool execution",
5
5
  "repository": {
6
6
  "type": "git",