@eddyskywalker/dsh-chatgpt-subscription 0.58.0 → 0.59.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/lib/client.js +136 -23
  3. package/lib/client.js.map +1 -1
  4. package/lib/codemode.js +1565 -0
  5. package/lib/index.js +414 -64
  6. package/lib/types/client/CodexImageToolView.d.ts.map +1 -1
  7. package/lib/types/client/progress-updates/progress-row.d.ts.map +1 -1
  8. package/lib/types/client/styles.d.ts.map +1 -1
  9. package/lib/types/compat.d.ts +10 -1
  10. package/lib/types/compat.d.ts.map +1 -1
  11. package/lib/types/entry-codemode.d.ts +37 -0
  12. package/lib/types/entry-codemode.d.ts.map +1 -0
  13. package/lib/types/host/agent-preset.d.ts +24 -2
  14. package/lib/types/host/agent-preset.d.ts.map +1 -1
  15. package/lib/types/host/antigravity/image-tool.d.ts +10 -2
  16. package/lib/types/host/antigravity/image-tool.d.ts.map +1 -1
  17. package/lib/types/host/cache-warmup/install.d.ts +10 -4
  18. package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
  19. package/lib/types/host/cache-warmup/projection.d.ts +17 -0
  20. package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
  21. package/lib/types/host/codemode/discovery.d.ts +33 -0
  22. package/lib/types/host/codemode/discovery.d.ts.map +1 -0
  23. package/lib/types/host/codemode/executor.d.ts +140 -0
  24. package/lib/types/host/codemode/executor.d.ts.map +1 -0
  25. package/lib/types/host/codemode/retention.d.ts +34 -0
  26. package/lib/types/host/codemode/retention.d.ts.map +1 -0
  27. package/lib/types/host/codemode/rules.d.ts +183 -0
  28. package/lib/types/host/codemode/rules.d.ts.map +1 -0
  29. package/lib/types/host/codemode/scope.d.ts +59 -0
  30. package/lib/types/host/codemode/scope.d.ts.map +1 -0
  31. package/lib/types/host/codemode/transport.d.ts +258 -0
  32. package/lib/types/host/codemode/transport.d.ts.map +1 -0
  33. package/lib/types/host/codex-images.d.ts +12 -0
  34. package/lib/types/host/codex-images.d.ts.map +1 -1
  35. package/package.json +6 -1
  36. package/presets/dispatch/README.md +12 -3
  37. package/presets/dispatch/agent.cordis.yml +10 -9
  38. package/presets/dispatch/preset.yml +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.59.0 - 2026-11-06
6
+
7
+ - **[codemode] 程序内的每条子调用重新写回会话:界面能看见「启动了哪个工具」**
8
+ - **缺的是什么**:内核的 PTC 传输会为每条子调用 append `tool/ptc-dispatch-start` / `tool/ptc-dispatch`,界面(`tool-call-tree`)靠这一对事件把子调用画到父调用下面;本行的程序交给内核运行时执行,而那两条事件不在运行时的对外契约里,于是 Code Mode 会话里回放事件**一条都没有**(实测 26 条 `tool/call` 对 0 条 `tool/ptc-dispatch*`),一次程序在界面上只有「一行日志 + 一具黑盒」。
9
+ - **补法**:[`src/host/codemode/transport.ts`](src/host/codemode/transport.ts) 新增 `createDispatchLog`,在子调用**真正进入执行 lane 之后**写 start、结算时写 settle,字段(`rootCallId` / `parentCallId` / `subCallId` / `name` / `arguments`)与内核逐字对齐;`subCallId` 沿用既有的 `<父>:ptc:<n>` 命名,因此 `isNestedCall` 与界面两侧都不用改。
10
+ - **时序诚实**:写调用受互斥闸门串行化,start 记在 lane 里而不是提交时——排队中被放弃的调用一条都不写(与内核「调度器真的开始才记」一致),并发时的顺序不会撒谎。
11
+ - **成对且有界**:失败也写 settle(带 `isError` 与 `error` 身份),只写 start 会留下永远不收尾的行;回放副本的单个文本块截到 8000 字符,程序本身拿到的值不受影响。
12
+ - **不碰程序**:`entry-codemode.ts` 的 `callTool` 只是**多回传** `content` / `meta` / `error.info`,`value` / `text` 语义不变;append 异常一律吞掉,老代 harness 没有这两种事件时降级为「没有回放」,而不是 `code_run` 失败。
13
+ - **验证**:`test/codemode.test.ts` 新增 5 项(成对写入与 id 对齐 / 失败收尾 / 并发时序 / append 抛错降级 / 无会话时静默);把 start 或 settle 各抹掉一处,各有 3 项转红。
14
+
15
+ - **[preset] `dispatch`(调度模式)改用 Code Mode,替代 PTC 呈现**
16
+ - 新增本包子入口 `./codemode`(`src/entry-codemode.ts` + `src/host/codemode/*`):预设自身注册的工具保持原生可调,预设清单以外的工具(MCP、profile 级插件、按 agent 注册的工具集)不进入模型上下文,只能在 `code_run` 程序中通过 `code_tools_list` / `code_tools_search` / `code_tools_describe` 发现后调用。
17
+ - 遮蔽接缝是 per-scope `ctx.tools.setExecutor()`(0.2.2+):`describe()` 决定请求面,`execute()` 在派发前拒绝模型直呼被遮蔽的名字并给出发现指引;程序内子调用整批委托给内核原生 executor,保留完整策略 / 审批 / post-execute 管线。
18
+ - **kimi-code 缓存前提**:说明段恒定(无计数、无 namespace 清单);`code_run` 与 `run_code` 同等享受 `ref://` 大输出预览与 `cache_warmup_result` 精确补读。
19
+ - 代次降级:解析不到 `@deepseek-ai/dsh-tool-executor-native` 时自动退回 `tool-executor-ptc`(或更早的 `agent-tool-presentation`),旧代行为不变。
20
+ - 依赖:新增 `@deepseek-ai/dsh-tool-executor-native`(真实依赖;profile 插件解析不到宿主内核包)。已知限制:不提供程序级 `sandbox_permissions` 升级。(原先还写着「程序内子调用不写回放事件」,该限制已由 0.59.0 的 `createDispatchLog` 解除,见上一条。)
21
+
5
22
  ## 0.58.0 - 2026-11-04
6
23
 
7
24
  - **[harness] 支持 DSH 0.2.2-rc.1,并保留 0.2.2-alpha.3、latest 0.2.1-rc.5 与旧代兼容**
package/lib/client.js CHANGED
@@ -155,13 +155,41 @@ window.__ModuleLoader__.load({
155
155
  }
156
156
  //#endregion
157
157
  //#region src/client/CodexImageToolView.tsx
158
+ /**
159
+ * Aspect ratios the Gemini image tool accepts in its `aspectRatio` argument.
160
+ *
161
+ * Mirrors `ANTIGRAVITY_IMAGE_ASPECT_RATIOS` in the host tool schema. The card
162
+ * only *sizes a placeholder* with it, so a drift here degrades to the default
163
+ * square instead of producing a wrong request — the value is never sent
164
+ * anywhere, and the host enum stays the single authority over what is legal.
165
+ */
166
+ const IMAGE_ASPECT_RATIOS = /* @__PURE__ */ new Set([
167
+ "1:1",
168
+ "2:3",
169
+ "3:2",
170
+ "3:4",
171
+ "4:3",
172
+ "4:5",
173
+ "5:4",
174
+ "9:16",
175
+ "16:9",
176
+ "21:9",
177
+ "1:4",
178
+ "4:1",
179
+ "1:8",
180
+ "8:1"
181
+ ]);
182
+ /** The model picks its own shape when the call names no ratio; the card guesses square. */
183
+ const DEFAULT_IMAGE_ASPECT_RATIO = "1:1";
184
+ /** The tray's short side, in px: a hint of the frame, not a billboard. */
185
+ const PLACEHOLDER_SHORT_SIDE = 112;
158
186
  function CodexImageToolView({ block, loadImage, t }) {
159
187
  const settled = "kind" in block;
160
188
  const isError = settled ? block.isError : false;
161
- const prompt = promptFromBlock$1(block);
162
189
  const images = settled && !isError ? imageBlocks(block.content) : [];
163
190
  const label = isError ? t("imageToolFailed") : settled ? t("imageToolDone") : t("imageToolRunning");
164
- const summary = prompt ?? textSummary$1(settled ? block.content : []);
191
+ const summary = promptFromBlock$1(block) ?? textSummary$1(settled ? block.content : []);
192
+ const tray = settled ? null : placeholderTray(aspectRatioFromBlock(block));
165
193
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
166
194
  className: "dsh-codex-image-tool",
167
195
  "data-state": isError ? "error" : settled ? "done" : "running",
@@ -174,12 +202,15 @@ window.__ModuleLoader__.load({
174
202
  }),
175
203
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
176
204
  className: "dsh-codex-image-title",
205
+ role: "status",
206
+ "aria-live": "polite",
177
207
  children: label
178
208
  }),
179
- summary !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
209
+ summary === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
180
210
  className: "dsh-codex-image-summary",
211
+ title: summary,
181
212
  children: summary
182
- }) : null
213
+ })
183
214
  ]
184
215
  }), images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
185
216
  className: "dsh-codex-image-grid",
@@ -189,7 +220,19 @@ window.__ModuleLoader__.load({
189
220
  label: attachment.name ?? t("image"),
190
221
  t
191
222
  }, String(attachment.attachmentId)))
192
- }) : null]
223
+ }) : tray === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
224
+ className: "dsh-codex-image-grid",
225
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
226
+ className: "dsh-codex-image-placeholder",
227
+ "data-ratio": tray.ratio,
228
+ "data-short-side": tray.axis,
229
+ style: {
230
+ aspectRatio: tray.ratio.replace(":", " / "),
231
+ ...tray.size
232
+ },
233
+ "aria-hidden": "true"
234
+ })
235
+ })]
193
236
  });
194
237
  }
195
238
  function GeneratedImage({ attachment, load, label, t }) {
@@ -223,24 +266,64 @@ window.__ModuleLoader__.load({
223
266
  });
224
267
  }
225
268
  function promptFromBlock$1(block) {
226
- const partial = partialPromptFromBlock(block);
269
+ return argumentFromBlock(block, "prompt");
270
+ }
271
+ /**
272
+ * Size the tray from one short side and let the ratio derive the other.
273
+ *
274
+ * A portrait frame fixes its height, and its width then stays narrow on its own.
275
+ * A landscape frame — and the 4:1 / 8:1 strips are exactly the case that forced
276
+ * this — fixes its width instead: deriving an 8:1 width from a 112px height
277
+ * would be 896px, which `max-width` would then clamp into a near-square box and
278
+ * the strip would stop looking like a strip. `min(…, 100%)` hands a narrow
279
+ * column back to the card and the height shrinks with it, so the shape survives
280
+ * the clamp.
281
+ */
282
+ function placeholderTray(ratio) {
283
+ const [w, h] = ratio.split(":").map(Number);
284
+ return w !== void 0 && h !== void 0 && w >= h ? {
285
+ axis: "width",
286
+ ratio,
287
+ size: { width: `min(${Math.round(PLACEHOLDER_SHORT_SIDE * (w / h))}px, 100%)` }
288
+ } : {
289
+ axis: "height",
290
+ ratio,
291
+ size: { height: PLACEHOLDER_SHORT_SIDE }
292
+ };
293
+ }
294
+ /** The placeholder's shape: the call's own ratio when it is one the tool accepts. */
295
+ function aspectRatioFromBlock(block) {
296
+ const ratio = argumentFromBlock(block, "aspectRatio")?.trim();
297
+ return ratio !== void 0 && IMAGE_ASPECT_RATIOS.has(ratio) ? ratio : DEFAULT_IMAGE_ASPECT_RATIO;
298
+ }
299
+ /**
300
+ * One string argument, read from whichever shape the current stage carries.
301
+ *
302
+ * While the arguments are still streaming, the harness hands over a lazy view
303
+ * instead of the JSON, so `args.text(key)` is tried first; once they are
304
+ * complete the same argument comes back as `argsRaw`.
305
+ */
306
+ function argumentFromBlock(block, key) {
307
+ const partial = partialArgumentFromBlock(block, key);
227
308
  if (partial !== null) return partial;
228
309
  const raw = "kind" in block ? block.call?.argsRaw : dispatchedArgsRaw(block);
229
310
  if (typeof raw !== "string" || raw.trim() === "") return null;
230
311
  try {
231
312
  const value = JSON.parse(raw);
232
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.prompt === "string" ? value.prompt : null;
313
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
314
+ const found = value[key];
315
+ return typeof found === "string" ? found : null;
233
316
  } catch {
234
317
  return null;
235
318
  }
236
319
  }
237
- function partialPromptFromBlock(block) {
320
+ function partialArgumentFromBlock(block, key) {
238
321
  if (!("args" in block)) return null;
239
322
  const args = block.args;
240
323
  if (typeof args?.text !== "function") return null;
241
324
  try {
242
- const prompt = args.text.call(args, "prompt");
243
- return typeof prompt === "string" ? prompt : null;
325
+ const value = args.text.call(args, key);
326
+ return typeof value === "string" ? value : null;
244
327
  } catch {
245
328
  return null;
246
329
  }
@@ -23564,18 +23647,32 @@ window.__ModuleLoader__.load({
23564
23647
  .dsh-codex-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
23565
23648
  .dsh-codex-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
23566
23649
  .dsh-codex-image-tool{border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
23567
- .dsh-codex-image-tool-head{align-items:center;display:flex;font-size:13px;gap:8px;min-width:0}
23650
+ /* One line, always. In a 320px column the label used to be squeezed until it
23651
+ stacked one character per row, which reads as broken text rather than a card.
23652
+ The dot and the label therefore keep their own size (flex:none + nowrap) and
23653
+ the summary is the only thing that gives way — it shrinks to zero width and
23654
+ ellipsizes, with the full prompt left in its title attribute. */
23655
+ .dsh-codex-image-tool-head{align-items:center;display:flex;flex-wrap:nowrap;font-size:13px;gap:8px;min-width:0}
23568
23656
  .dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;corner-shape:round;display:inline-block;flex:none;height:8px;width:8px}
23569
23657
  .dsh-codex-image-tool[data-state=error] .dsh-codex-image-dot{background:var(--dsw-alias-label-danger,#d94b4b)}
23570
- .dsh-codex-image-title{font-weight:600}
23571
- .dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
23658
+ .dsh-codex-image-title{flex:none;font-weight:600;white-space:nowrap}
23659
+ .dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
23572
23660
  .dsh-codex-image-grid{display:flex;flex-wrap:wrap;gap:8px}
23661
+ /* The tray the image will land in, drawn while the call is still running so the
23662
+ card does not jump from one text row to a full picture when the result
23663
+ arrives. Neither dimension is set here: the view fixes the short side (height
23664
+ for a portrait frame, width for a landscape one) and the inline aspect-ratio
23665
+ derives the other, which is what keeps the 4:1 and 8:1 strips from being
23666
+ clamped into squarer boxes. max-width is the card's edge. The breathe is the
23667
+ same restrained opacity pulse the settings skeleton uses, and the
23668
+ reduced-motion block below turns it off. */
23669
+ .dsh-codex-image-placeholder{animation:dsh-codex-pulse 2s ease-in-out infinite;background:var(--dsw-alias-bg-layer-2);border:0.5px dashed var(--dsw-alias-border-l2);border-radius:8px;display:block;flex:none;max-width:100%}
23573
23670
  .dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
23574
23671
  .dsh-codex-image-loading,.dsh-codex-image-failed{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
23575
23672
  .dsh-codex-image-failed{color:var(--dsw-alias-label-danger,#d94b4b)}
23576
23673
  @keyframes dsh-codex-pulse{0%,100%{opacity:.45}50%{opacity:.85}}
23577
23674
  @media(max-width:560px){.dsh-codex-row,.dsh-codex-pref-row,.dsh-codex-quota-fact,.dsh-codex-reset-credits{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value,.dsh-codex-quota-fact strong{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}.dsh-codex-segments{width:100%}.dsh-codex-select{max-width:none;width:100%}.dsh-codex-context-row{align-items:flex-start;flex-direction:column;gap:5px}.dsh-codex-segments label{flex:1}.dsh-codex-segments span{text-align:center}.dsh-codex-image-tool{max-width:100%;margin-left:0}}
23578
- @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
23675
+ @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}.dsh-codex-image-placeholder{animation:none}}
23579
23676
  `;
23580
23677
  /**
23581
23678
  * Install the ChatGPT tab's stylesheet as a document-owned, never-removed
@@ -54970,22 +55067,36 @@ window.__ModuleLoader__.load({
54970
55067
  done: "rowDone",
54971
55068
  error: "rowError"
54972
55069
  };
54973
- /** 行的外框样式。 */
55070
+ /**
55071
+ * 行的外框样式。
55072
+ *
55073
+ * 字号与官方工具行标题对齐(13px):同一条时间线上混着 12px 的进展行与 13px 的
55074
+ * 工具行,读者看到的是两套字号。
55075
+ */
54974
55076
  const rowStyle = {
54975
55077
  display: "flex",
54976
55078
  flexDirection: "column",
54977
55079
  gap: "6px",
54978
55080
  width: "100%",
54979
- fontSize: "12px",
55081
+ fontSize: "13px",
54980
55082
  color: "var(--dsw-alias-label-primary, #1f2329)"
54981
55083
  };
54982
- /** 标题行按钮样式(整行可点,去掉原生按钮外观)。 */
55084
+ /**
55085
+ * 标题行按钮样式(整行可点,去掉原生按钮外观)。
55086
+ *
55087
+ * `minHeight: 36px` 与 `padding: 6px 0` 抄的是官方工具行那层 `_row_` 的实测取值
55088
+ * (`height:36px; min-height:36px; padding:6px 0`)。少了这两个值,进展行只有
55089
+ * 18px 左右,夹在一排 36px 的工具行里会明显更矮、更挤——用户报的就是这个。
55090
+ * 用 minHeight 而不是 height:正文换行、标题很长时允许它撑高,官方那种固定高度
55091
+ * 在文字折行时会把内容裁掉。
55092
+ */
54983
55093
  const headerStyle = {
54984
55094
  display: "flex",
54985
- alignItems: "baseline",
55095
+ alignItems: "center",
54986
55096
  gap: "8px",
54987
55097
  width: "100%",
54988
- padding: "0",
55098
+ minHeight: "36px",
55099
+ padding: "6px 0",
54989
55100
  border: "0",
54990
55101
  background: "transparent",
54991
55102
  color: "inherit",
@@ -54997,9 +55108,9 @@ window.__ModuleLoader__.load({
54997
55108
  const titleStyle = {
54998
55109
  flex: "1 1 auto",
54999
55110
  minWidth: "0",
55000
- fontSize: "12px",
55111
+ fontSize: "13px",
55001
55112
  fontWeight: 500,
55002
- lineHeight: "1.5",
55113
+ lineHeight: "16px",
55003
55114
  overflowWrap: "anywhere"
55004
55115
  };
55005
55116
  /** 状态词的小字样式。 */
@@ -55012,6 +55123,8 @@ window.__ModuleLoader__.load({
55012
55123
  /**
55013
55124
  * 正文样式:只管外边距、字号与颜色。
55014
55125
  *
55126
+ * 字号与标题一致(13px)—— 早先正文比标题小一号,展开后两节字不同大,读起来碎。
55127
+ *
55015
55128
  * **不再**在这里写 `whiteSpace: 'pre-wrap'`:正文交给 {@link MarkdownLite} 渲染,
55016
55129
  * 换行该不该保留由它产出的块级元素决定。早先这里是纯文本 `<p>{message}</p>`,
55017
55130
  * 于是列表符号、`**粗体**` 和管道符表格全部原样上屏 —— 同一份进展在侧栏面板里
@@ -55020,8 +55133,8 @@ window.__ModuleLoader__.load({
55020
55133
  const messageStyle = {
55021
55134
  overflowWrap: "anywhere",
55022
55135
  margin: "0",
55023
- fontSize: "12px",
55024
- lineHeight: "1.55",
55136
+ fontSize: "13px",
55137
+ lineHeight: "1.6",
55025
55138
  color: "var(--dsw-alias-label-secondary, #5b6270)"
55026
55139
  };
55027
55140
  /**