dsh-input-traffic 0.7.9 → 0.7.11

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,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ### 修复:注入上下文不再占位等待区
6
+
7
+ `next-step` 带里混着两类行:人真的插的话(`source.kind === 'user'`,web 控制器会给它盖上 `rpcId`),与服务/插件推给模型的上下文(宿主自己的 `user-approval` / `policy` / `time-context` 通知,以及第三方插件的意见)。旧实现把整个 `next-step` 都当“🟡 插话中”画出来,于是“The approval policy changed from "ask" to "never"”这类系统通知出现在用户的等待区里,还带着编辑 / 重排 / 重发按钮——那是对着不是人写的东西开的操作。现在新增纯函数 `isHumanSteer(row)`,黄色带只画真人插话;没带 `source` 的旧行(本地 fixture、老宿主)照旧可见,不会误伤真队列。上下文本身仍照常进模型。
8
+
3
9
  ## 0.7.7 (2026-10-27)
4
10
 
5
11
  两条外部 issue(feidky)的修复;无宿主兼容面变更,`engines.dsh` 枚举与 peers 保持 0.7.6。
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-input-traffic",
3
- "version": "0.7.9",
3
+ "version": "0.7.11",
4
4
  "main": "./lib/index.js",
5
5
  "description": "Busy-time input queue: three-tier planning, drag-to-reorder, interject/interrupt, batch clear, session freeze.",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -704,6 +704,31 @@ window.__ModuleLoader__.load({
704
704
  return null;
705
705
  }
706
706
  /**
707
+ * Whether one `next-step` inbox row is a steer the person typed, rather than
708
+ * context some producer injected into the same band.
709
+ *
710
+ * The host merges two different things into `next-step`:
711
+ * - a human steer — `source.kind === 'user'` (the web controller stamps it with
712
+ * an `rpcId`), the yellow tier this dock exists to plan;
713
+ * - model-facing context pushed by a service or plugin with its own
714
+ * `source.kind` — the host's own `user-approval` / `policy` / `time-context`
715
+ * notices, and third-party notes such as perm-gate's block opinion.
716
+ *
717
+ * Only the first is an input the person still owns. Drawing the second as a
718
+ * planned message put host notices (“The approval policy changed…”​) into the
719
+ * waiting area with edit / reorder / resend affordances that cannot work on
720
+ * them, and competed with the user's own queue for attention.
721
+ *
722
+ * Lenient on purpose: a row with no `source` at all (local fixtures, older
723
+ * hosts) stays visible, so nothing legitimately queued can disappear.
724
+ * @param row - one `next-step` inbox row.
725
+ * @returns true when the row is a human steer.
726
+ */
727
+ function isHumanSteer(row) {
728
+ const kind = row.source?.kind;
729
+ return kind === void 0 || kind === "user";
730
+ }
731
+ /**
707
732
  * Auto-grow one edit textarea to its content. The CSS max-height caps the
708
733
  * growth; beyond it the textarea scrolls internally. Height is reset first so
709
734
  * shrink (deleting lines) also tracks the content.
@@ -735,7 +760,7 @@ window.__ModuleLoader__.load({
735
760
  ...row,
736
761
  text: textOf(row.content) ?? row.text ?? null
737
762
  })), [inbox]);
738
- const steering = (0, react.useMemo)(() => (inbox?.["next-step"] ?? []).map((row) => ({
763
+ const steering = (0, react.useMemo)(() => (inbox?.["next-step"] ?? []).filter(isHumanSteer).map((row) => ({
739
764
  ...row,
740
765
  text: textOf(row.content) ?? row.text ?? null
741
766
  })), [inbox]);
@@ -2000,10 +2025,16 @@ window.__ModuleLoader__.load({
2000
2025
  return raw.subscribe(() => setPolled(raw.getSnapshot()));
2001
2026
  });
2002
2027
  const tabs = props.useTabs !== void 0 ? props.useTabs((value) => value) : polled;
2028
+ const ownId = props.ownId ?? "__own__";
2029
+ const rows = props.renderOwn ? [{
2030
+ id: ownId,
2031
+ order: Number.NEGATIVE_INFINITY,
2032
+ label: ownId
2033
+ }, ...tabs.filter((tab) => tab.id !== ownId)] : tabs;
2003
2034
  return (0, react.createElement)("div", { style: {
2004
2035
  display: "grid",
2005
2036
  gap: "12px"
2006
- } }, tabs.map((row) => (0, react.createElement)("div", { key: row.id }, props.renderSlot ? props.renderSlot(FAMILY_CHILD_KEY, {}, {
2037
+ } }, rows.map((row) => (0, react.createElement)("div", { key: row.id }, props.renderOwn && row.id === ownId ? props.renderOwn() : props.renderSlot ? props.renderSlot(FAMILY_CHILD_KEY, {}, {
2007
2038
  only: row.id,
2008
2039
  fallback: null
2009
2040
  }) : props.renderContributor?.(row.id) ?? null)));
@@ -2046,7 +2077,7 @@ window.__ModuleLoader__.load({
2046
2077
  * @param log - console 前缀(带插件名)。
2047
2078
  * @returns 取消定时器与撤销接管注册的 disposer。
2048
2079
  */
2049
- function claimFamilyHostIfOrphaned(slots, isHeld, label, log) {
2080
+ function claimFamilyHostIfOrphaned(slots, isHeld, label, log, ownTab) {
2050
2081
  let disposeClaim;
2051
2082
  let claimed = false;
2052
2083
  const timer = setTimeout(() => {
@@ -2077,7 +2108,9 @@ window.__ModuleLoader__.load({
2077
2108
  label,
2078
2109
  inject: () => ({
2079
2110
  hooks: { tabs: ledger },
2080
- renderContributor
2111
+ renderContributor,
2112
+ ownId: ownTab?.id,
2113
+ renderOwn: ownTab === void 0 ? void 0 : () => (0, react.createElement)(ownTab.component, ownTab.inject ? ownTab.inject() : {})
2081
2114
  }),
2082
2115
  children: { [FAMILY_CHILD_KEY]: {
2083
2116
  kind: "list",
@@ -2161,19 +2194,25 @@ window.__ModuleLoader__.load({
2161
2194
  console.warn("[dsh-input-traffic] bridge busyEnter pin failed:", e);
2162
2195
  });
2163
2196
  });
2164
- let familyTabHeld = false;
2165
- ctx.slots.inject("dsh-family.tab", () => {
2166
- familyTabHeld = true;
2167
- return ctx.slots.register({
2168
- name: "dsh-family.tab",
2169
- id: "input-traffic",
2170
- order: 50,
2171
- label: () => tFamily("family.title"),
2172
- locale: NS,
2173
- inject: () => ({ scope: familyScopeRef.scope ?? bridgeScope })
2174
- }, InputTrafficFamilyCard);
2175
- });
2176
- ctx.effect(() => claimFamilyHostIfOrphaned(ctx.slots, () => familyTabHeld, () => "起子插件设置", "[dsh-input-traffic]"), "dsh-input-traffic: family host backstop");
2197
+ ctx.slots.inject("dsh-family.tab", () => ctx.slots.register({
2198
+ name: "dsh-family.tab",
2199
+ id: "input-traffic",
2200
+ order: 50,
2201
+ label: () => tFamily("family.title"),
2202
+ locale: NS,
2203
+ inject: () => ({ scope: familyScopeRef.scope ?? bridgeScope })
2204
+ }, InputTrafficFamilyCard));
2205
+ ctx.effect(() => claimFamilyHostIfOrphaned(ctx.slots, () => {
2206
+ try {
2207
+ return ctx.slots.entries("settings.section").some((e) => String((e.options ?? {}).id ?? "") === "dsh-family");
2208
+ } catch {
2209
+ return false;
2210
+ }
2211
+ }, () => "起子插件设置", "[dsh-input-traffic]", {
2212
+ id: "input-traffic",
2213
+ component: InputTrafficFamilyCard,
2214
+ inject: () => ({ scope: familyScopeRef.scope ?? bridgeScope })
2215
+ }), "dsh-input-traffic: family host backstop");
2177
2216
  registerSettingsFaces(ctx, {
2178
2217
  namespace: CONVERSATION_SETTINGS_NAMESPACE,
2179
2218
  field: BUSY_ENTER_FIELD,