dsh-vision-fallback 0.5.0 → 0.6.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.
package/lib/index.js CHANGED
@@ -449,13 +449,16 @@ class ReplacementCoordinator {
449
449
  replacements = new Map();
450
450
  this.pending.set(session, replacements);
451
451
  }
452
+ const activeMessageIds = new Set(originalMessages.map((message) => message.id));
453
+ for (const messageId of replacements.keys()) {
454
+ if (!activeMessageIds.has(messageId)) replacements.delete(messageId);
455
+ }
452
456
  for (let index = 0; index < originalMessages.length; index += 1) {
453
457
  const original = originalMessages[index];
454
458
  const rewritten = rewrittenMessages[index];
455
459
  if (original === rewritten || !contentHasImage(original.content ?? [])) continue;
456
460
  replacements.set(original.id, rewritten);
457
461
  }
458
- if (replacements.size > 128) replacements.clear();
459
462
  }
460
463
 
461
464
  onSessionEvent(session, event) {
@@ -512,9 +515,9 @@ function apply(ctx, config) {
512
515
  ctx.on("agent/pre-step", async ({ agent, signal }, next) => {
513
516
  const decision = await next();
514
517
  if (decision.kind === "reject" || signal.aborted) return decision;
515
- const contextMessages = [...agent.session.deriveMessages(), ...decision.messages];
516
- const rewritten = await controller.preprocess(decision.messages, contextMessages, signal);
517
- replacements.stage(agent.session, decision.messages, rewritten);
518
+ const modelMessages = [...agent.session.deriveMessages(), ...decision.messages];
519
+ const rewritten = await controller.preprocess(modelMessages, modelMessages, signal);
520
+ replacements.stage(agent.session, modelMessages, rewritten);
518
521
  return decision;
519
522
  });
520
523
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vision-fallback",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "DSH 静默视觉增强:主模型照常选择,图片自动交给固定视觉模型后以隐藏上下文返回主模型。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -198,6 +198,52 @@ test("surface replacement 事件不会再次触发替换", async () => {
198
198
  assert.deepEqual(appends[0].options.surfaceOp, { op: "replace", start: 12, end: 12 });
199
199
  });
200
200
 
201
+ test("历史工具结果中的图片只以文字形式进入模型视图", async () => {
202
+ const originalFetch = globalThis.fetch;
203
+ globalThis.fetch = async () => ({
204
+ ok: true,
205
+ json: async () => ({ choices: [{ message: { content: "这是主题插件的界面截图" } }] })
206
+ });
207
+ const historicalToolResult = userMessage("tool-result-1", [{
208
+ type: "tool-result",
209
+ toolCallId: "read-image-1",
210
+ content: [
211
+ { type: "text", text: "图片读取成功" },
212
+ { type: "image", attachment }
213
+ ],
214
+ isError: false
215
+ }]);
216
+ const current = userMessage("u2", [{ type: "text", text: "这是什么插件?" }]);
217
+ const surface = [historicalToolResult];
218
+ const session = {
219
+ deriveMessages: () => surface,
220
+ append() {
221
+ throw new Error("历史消息投影不应改写界面事件");
222
+ }
223
+ };
224
+ const controller = new VisionFallbackController(createServiceContext(), () => config);
225
+ const coordinator = new ReplacementCoordinator({ error() {} });
226
+
227
+ try {
228
+ const modelMessages = [...session.deriveMessages(), current];
229
+ const rewritten = await controller.preprocess(modelMessages, modelMessages);
230
+ coordinator.stage(session, modelMessages, rewritten);
231
+
232
+ const projected = session.deriveMessages();
233
+ assert.equal(projected.length, 1);
234
+ assert.equal(projected[0].id, historicalToolResult.id);
235
+ assert.equal(projected[0].content[0].type, "tool-result");
236
+ assert.equal(projected[0].content[0].content[1].type, "text");
237
+ assert.match(projected[0].content[0].content[1].text, /这是主题插件的界面截图/);
238
+ assert.equal(JSON.stringify(projected).includes('"type":"image"'), false);
239
+ assert.equal(surface[0], historicalToolResult);
240
+ assert.equal(surface[0].content[0].content[1].type, "image");
241
+ } finally {
242
+ coordinator.dispose();
243
+ globalThis.fetch = originalFetch;
244
+ }
245
+ });
246
+
201
247
  test("图片能力覆盖可随开关变化并能恢复", async () => {
202
248
  let current = config;
203
249
  const original = async (provider, model) => ({