@deepseek-ai/dsh-repeat-tool-reminder 0.1.7-alpha.2 → 0.1.7-rc.2

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/README.i18n.yaml CHANGED
@@ -1,6 +1,85 @@
1
- # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
- # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
- # after editing either side, bring the other along and re-record with:
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/guard/repeat-tool-reminder/README.md
5
- README.md: 13035b68e80c9eea3e87bb7c8315fbaf9b84a3c3
6
- README.zh.md: 27d142988f77c8ab03a81701ff7b89f55ca1668b
5
+ /:
6
+ en: 8eb3c6cc73da87aa
7
+ zh: 4059266a0486ff38
8
+ /deepseek-ai-dsh-repeat-tool-reminder:
9
+ en: add59e0d04eb964d
10
+ zh: d5224b61ea4e9413
11
+ /deepseek-ai-dsh-repeat-tool-reminder/summary:
12
+ en: 36ac7ba52bc4fee0
13
+ zh: 39894a1c067754c4
14
+ /deepseek-ai-dsh-repeat-tool-reminder/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /deepseek-ai-dsh-repeat-tool-reminder/use-this-package:
18
+ en: dcffe01b4cde60e0
19
+ zh: 9a105a1d0121b990
20
+ /deepseek-ai-dsh-repeat-tool-reminder/use-this-package/when-to-choose-it:
21
+ en: c6b47c1f0034c021
22
+ zh: 5a2097425754d8b0
23
+ /deepseek-ai-dsh-repeat-tool-reminder/use-this-package/setting-the-thresholds-and-scope:
24
+ en: fc64919ff223096d
25
+ zh: dd22a19eeffa9192
26
+ /deepseek-ai-dsh-repeat-tool-reminder/use-this-package/what-you-get:
27
+ en: 5cf91a5809d2c68a
28
+ zh: 8f60231436b863b8
29
+ /deepseek-ai-dsh-repeat-tool-reminder/understand-the-implementation:
30
+ en: dd6d7a1571af5a9a
31
+ zh: eba00095dfc1c92a
32
+ /deepseek-ai-dsh-repeat-tool-reminder/understand-the-implementation/design-philosophy:
33
+ en: 7bca56e3cba01338
34
+ zh: 530fda764d226f39
35
+ /deepseek-ai-dsh-repeat-tool-reminder/understand-the-implementation/detection-the-repeat-chain:
36
+ en: b76a432aad14aa90
37
+ zh: b0cd916f7897fb42
38
+ /deepseek-ai-dsh-repeat-tool-reminder/understand-the-implementation/reminder-delivery:
39
+ en: ebd2a7d704e0a7be
40
+ zh: 438213ad0412a64e
41
+ /deepseek-ai-dsh-repeat-tool-reminder/understand-the-implementation/source-map:
42
+ en: 567a964105e1340b
43
+ zh: 7a8b062b2d9b39d1
44
+ /deepseek-ai-dsh-repeat-tool-reminder/further-exploration:
45
+ en: 77b646e29c1aefa8
46
+ zh: f63b7df7412e524d
47
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience:
48
+ en: 215e7ba838619b7b
49
+ zh: a311da3843709f90
50
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/first-threshold-context-message:
51
+ en: 798ea2afb5efde7c
52
+ zh: 14fae1e31d882e8a
53
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/first-threshold-context-message/what-the-model-sees:
54
+ en: cf01847a41f3fe3d
55
+ zh: aebeb78ac617999c
56
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/first-threshold-context-message/what-the-model-sees/first-threshold-reminder:
57
+ en: 38c7616427fa569d
58
+ zh: fd443fab22a57894
59
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/first-threshold-context-message/token-effect:
60
+ en: 111c6d779b73194b
61
+ zh: 09b0e7073308ba91
62
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/first-threshold-context-message/kv-cache-effect:
63
+ en: f5d5f97a153f7c70
64
+ zh: 51155536aab03002
65
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/later-threshold-context-message:
66
+ en: edfa41e2478fb579
67
+ zh: 76b87439f3bef43c
68
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/later-threshold-context-message/what-the-model-sees:
69
+ en: cddb7a41c335141f
70
+ zh: 7ea33c6570bb2bd0
71
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/later-threshold-context-message/what-the-model-sees/later-threshold-reminder:
72
+ en: 341114f2e5e9c05b
73
+ zh: 8938e7d0f7fc150b
74
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/later-threshold-context-message/token-effect:
75
+ en: 2e92c0d5a90c482b
76
+ zh: 07ba3851b725f06d
77
+ /deepseek-ai-dsh-repeat-tool-reminder/model-experience/later-threshold-context-message/kv-cache-effect:
78
+ en: f5d5f97a153f7c70
79
+ zh: 51155536aab03002
80
+ /deepseek-ai-dsh-repeat-tool-reminder/known-limitations-and-deferred-work:
81
+ en: 59ffa89bf74f8da2
82
+ zh: 7652f49b9eac40d6
83
+ /deepseek-ai-dsh-repeat-tool-reminder/known-limitations-and-deferred-work/dev-note:
84
+ en: ef308dcf213685c7
85
+ zh: 5830b6bbeea9863f
package/lib/index.js CHANGED
@@ -164,6 +164,16 @@ function validateName(subject, value) {
164
164
  //#endregion
165
165
  //#region ../../util/values/src/index.ts
166
166
  /**
167
+ * Mark an unreachable closed-union branch.
168
+ * @param value - impossible value; an unhandled typed variant fails at the call site.
169
+ * @param context - optional switch-site label included in the failure message.
170
+ * @returns never; a runtime value that escaped its type always throws.
171
+ */
172
+ function assertNever(value, context) {
173
+ const rendered = JSON.stringify(value) ?? String(value);
174
+ throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
175
+ }
176
+ /**
167
177
  * Deep-freeze an object graph in place while leaving live AbortSignal objects mutable.
168
178
  * @param value - value to freeze.
169
179
  * @returns the same value after every reachable enumerable child is frozen.
@@ -609,6 +619,89 @@ function projectImagesForTextModel(messages) {
609
619
  };
610
620
  });
611
621
  }
622
+ function withoutDeveloperMessages(messages) {
623
+ const retained = messages.filter((message) => message.role !== "developer");
624
+ return retained.length === messages.length ? messages : retained;
625
+ }
626
+ function toolDeclarations(tools, mode, history) {
627
+ const declarations = new Map(history.tools.map((tool) => [tool.name, tool]));
628
+ for (const update of history.updates) for (const tool of update.additions) if (!declarations.has(tool.name)) declarations.set(tool.name, {
629
+ ...tool,
630
+ deferLoading: true
631
+ });
632
+ switch (mode) {
633
+ case "in-history": return declarations;
634
+ case "addition-only": {
635
+ const activeNames = new Set(tools?.map((tool) => tool.name));
636
+ for (const name of declarations.keys()) if (!activeNames.has(name)) declarations.delete(name);
637
+ return declarations;
638
+ }
639
+ /* v8 ignore next 2 -- closed-union exhaustiveness guard */
640
+ default: return assertNever(mode);
641
+ }
642
+ }
643
+ /**
644
+ * Construct provider declarations from session-folded history without changing logged active tools.
645
+ * Unsupported routes and incomplete history use current declarations without developer updates.
646
+ * Explicitly deferred baseline tools become available only after their first retained addition.
647
+ * @param messages - complete request inputs, or the prefix selected for an auxiliary call.
648
+ * @param tools - currently active tool schemas.
649
+ * @param toolUpdate - the resolved route's update mode.
650
+ * @param history - immutable state folded from committed headers and developer messages.
651
+ * @returns provider declarations and the corresponding filtered history.
652
+ */
653
+ function projectToolUpdates(messages, tools, toolUpdate, history) {
654
+ if (toolUpdate === void 0) {
655
+ let immediateTools = tools;
656
+ if (tools?.some((tool) => tool.deferLoading === true)) immediateTools = tools.map(({ deferLoading: _loading, ...tool }) => tool);
657
+ return {
658
+ messages: withoutDeveloperMessages(messages),
659
+ tools: immediateTools
660
+ };
661
+ }
662
+ if (history === void 0) return {
663
+ messages: withoutDeveloperMessages(messages),
664
+ tools
665
+ };
666
+ const messageIds = new Set(messages.flatMap((message) => message.role === "developer" ? [message.id] : []));
667
+ if (history.updates.some((update) => !messageIds.has(update.messageId))) return {
668
+ messages: withoutDeveloperMessages(messages),
669
+ tools
670
+ };
671
+ const declarations = toolDeclarations(tools, toolUpdate, history);
672
+ const updateIds = new Set(history.updates.map((update) => update.messageId));
673
+ const offered = new Set(history.tools.filter((tool) => !tool.deferLoading).map((tool) => tool.name));
674
+ const projectedMessages = [];
675
+ for (const message of messages) {
676
+ if (message.role !== "developer") {
677
+ projectedMessages.push(message);
678
+ continue;
679
+ }
680
+ if (!updateIds.has(message.id)) continue;
681
+ const content = message.content.filter((block) => {
682
+ switch (block.type) {
683
+ case "tool-addition":
684
+ if (!declarations.has(block.toolName) || offered.has(block.toolName)) return false;
685
+ offered.add(block.toolName);
686
+ return true;
687
+ case "tool-removal":
688
+ if (toolUpdate !== "in-history") return false;
689
+ return offered.delete(block.toolName);
690
+ default: return true;
691
+ }
692
+ });
693
+ if (content.length === 0) continue;
694
+ if (content.length === message.content.length) projectedMessages.push(message);
695
+ else projectedMessages.push({
696
+ ...message,
697
+ content
698
+ });
699
+ }
700
+ return {
701
+ messages: projectedMessages.length === messages.length && projectedMessages.every((message, index) => message === messages[index]) ? messages : projectedMessages,
702
+ tools: [...declarations.values()]
703
+ };
704
+ }
612
705
  //#endregion
613
706
  //#region ../../llm/llm/src/attribution.ts
614
707
  /**
@@ -1020,7 +1113,8 @@ var LlmError = class extends HarnessError {
1020
1113
  }
1021
1114
  /**
1022
1115
  * Discover models advertised by one registered provider. Catalog membership
1023
- * is advisory and never changes routing or request validation.
1116
+ * does not constrain core routing. Catalog-driven entry points may restrict
1117
+ * selection and submission to the advertised models.
1024
1118
  * @param provider - registered provider route to inspect.
1025
1119
  * @returns detached model metadata in adapter-preferred order.
1026
1120
  */
@@ -1065,6 +1159,8 @@ var LlmError = class extends HarnessError {
1065
1159
  const inputModalities = this.detachedModalities(resolved.inputModalities);
1066
1160
  const systemPromptUpdate = resolved.systemPromptUpdate;
1067
1161
  if (systemPromptUpdate !== void 0 && systemPromptUpdate !== "in-history") throw new LlmError(`adapter returned invalid system prompt update mode for provider "${provider}" model "${model}"`, "INVALID_MODEL_INFO");
1162
+ const toolUpdate = resolved.toolUpdate;
1163
+ if (toolUpdate !== void 0 && toolUpdate !== "in-history" && toolUpdate !== "addition-only") throw new LlmError(`adapter returned invalid tool update mode for provider "${provider}" model "${model}"`, "INVALID_MODEL_INFO");
1068
1164
  const defaultMaxTokens = resolved.defaultMaxTokens;
1069
1165
  if (defaultMaxTokens !== void 0 && (!Number.isSafeInteger(defaultMaxTokens) || defaultMaxTokens <= 0)) throw new LlmError(`adapter returned invalid default maxTokens for provider "${provider}" model "${model}"`, "INVALID_MODEL_MAX_TOKENS");
1070
1166
  const info = {
@@ -1075,7 +1171,8 @@ var LlmError = class extends HarnessError {
1075
1171
  ...inputModalities === void 0 ? {} : { inputModalities },
1076
1172
  ...context === void 0 ? {} : { context: { contextWindow: context.contextWindow } },
1077
1173
  ...defaultMaxTokens === void 0 ? {} : { defaultMaxTokens },
1078
- ...resolved.systemPromptUpdate === void 0 ? {} : { systemPromptUpdate: resolved.systemPromptUpdate }
1174
+ ...resolved.systemPromptUpdate === void 0 ? {} : { systemPromptUpdate: resolved.systemPromptUpdate },
1175
+ ...resolved.toolUpdate === void 0 ? {} : { toolUpdate: resolved.toolUpdate }
1079
1176
  };
1080
1177
  const reasoning = resolved.reasoning;
1081
1178
  if (reasoning === void 0) return info;
@@ -1170,6 +1267,7 @@ var LlmError = class extends HarnessError {
1170
1267
  ...context === void 0 ? {} : { context },
1171
1268
  ...modelInfo.inputModalities === void 0 ? {} : { inputModalities: Object.freeze([...modelInfo.inputModalities]) },
1172
1269
  ...modelInfo.systemPromptUpdate === void 0 ? {} : { systemPromptUpdate: modelInfo.systemPromptUpdate },
1270
+ ...modelInfo.toolUpdate === void 0 ? {} : { toolUpdate: modelInfo.toolUpdate },
1173
1271
  stream: (options) => {
1174
1272
  if (dispatched) throw new LlmError("a prepared LLM call can only be dispatched once", "INVALID_PREPARED_CALL");
1175
1273
  if (!callConfigEquals(options, resolvedConfig)) throw new LlmError("prepared LLM call config changed before adapter dispatch", "INVALID_PREPARED_CALL");
@@ -1259,13 +1357,17 @@ var LlmError = class extends HarnessError {
1259
1357
  let projectedMessages = resolvedOptions.messages;
1260
1358
  if (projectedMessages.some((message) => contentHasFile(message.content))) projectedMessages = projectFilesToText(projectedMessages, (ref) => this.fileReadPath(ref));
1261
1359
  if (modelInfo.inputModalities !== void 0 && !modelInfo.inputModalities.includes("image") && projectedMessages.some((message) => contentHasImage(message.content))) projectedMessages = projectImagesForTextModel(projectedMessages);
1262
- const projectedOptions = projectedMessages === resolvedOptions.messages ? resolvedOptions : Object.isFrozen(resolvedOptions) ? deepFreeze({
1263
- ...resolvedOptions,
1264
- messages: projectedMessages
1265
- }) : {
1266
- ...resolvedOptions,
1267
- messages: projectedMessages
1268
- };
1360
+ const projectedTools = projectToolUpdates(projectedMessages, resolvedOptions.tools, modelInfo.toolUpdate, resolvedOptions.toolHistory);
1361
+ projectedMessages = projectedTools.messages;
1362
+ let projectedOptions = resolvedOptions;
1363
+ if (projectedMessages !== resolvedOptions.messages || projectedTools.tools !== resolvedOptions.tools) {
1364
+ projectedOptions = {
1365
+ ...resolvedOptions,
1366
+ messages: projectedMessages,
1367
+ ...projectedTools.tools === void 0 ? {} : { tools: projectedTools.tools }
1368
+ };
1369
+ if (Object.isFrozen(resolvedOptions)) deepFreeze(projectedOptions);
1370
+ }
1269
1371
  iterator = dispatch(this.forAdapter(projectedOptions, adapter))[Symbol.asyncIterator]();
1270
1372
  } catch (error) {
1271
1373
  yield adapterFailureChunk(error, options.signal);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-repeat-tool-reminder",
3
3
  "description": "Repeat-tool-call guard plugin: advisory reminders when an agent loops on identical tool calls",
4
- "version": "0.1.7-alpha.2",
4
+ "version": "0.1.7-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,18 +30,18 @@
30
30
  "@deepseek-ai/schemastery": "~3.18.4"
31
31
  },
32
32
  "peerDependencies": {
33
+ "@deepseek-ai/dsh-agent": "0.1.7-rc.2",
33
34
  "@deepseek-ai/cordis": "~4.0.4",
34
- "@deepseek-ai/dsh-tools": "0.1.7-alpha.2",
35
- "@deepseek-ai/dsh-agent": "0.1.7-alpha.2"
35
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@deepseek-ai/dsh-agent": "0.1.7-alpha.2",
39
- "@deepseek-ai/dsh-agent-loop": "0.1.7-alpha.2",
40
- "@deepseek-ai/dsh-agent-loop-testkit": "0.1.7-alpha.2",
41
- "@deepseek-ai/dsh-llm": "0.1.7-alpha.2",
42
- "@deepseek-ai/dsh-session": "0.1.7-alpha.2",
43
- "@deepseek-ai/dsh-tools": "0.1.7-alpha.2",
44
- "@deepseek-ai/dsh-session-projection": "0.1.7-alpha.2",
38
+ "@deepseek-ai/dsh-agent": "0.1.7-rc.2",
39
+ "@deepseek-ai/dsh-agent-loop": "0.1.7-rc.2",
40
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
41
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.1.7-rc.2",
42
+ "@deepseek-ai/dsh-session": "0.1.7-rc.2",
43
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2",
44
+ "@deepseek-ai/dsh-session-projection": "0.1.7-rc.2",
45
45
  "@deepseek-ai/cordis": "~4.0.4"
46
46
  }
47
47
  }