refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/guard/README.md
README.md: 73ca00e858c4ddaeb1ba9ccee7fb7e0cfce518f4
README.zh.md: 37b4ff6ff80050af613565cd388e206b35f09e01
README.md: ce5c16dbfbae5c55430f4e8d8bb48ab50989b7ad
README.zh.md: 7fc032bf7aa8b14a89376ae6d3a57ac45447ea60

View File

@@ -6,7 +6,7 @@ Behavioral guard plugins watch the agent loop for unproductive patterns and enfo
| Package | Role | ctx key |
|---|---|---|
| [`repeat-tool-guard/`](repeat-tool-guard/README.md) | Advisory reminders for repeated tool calls | listens on tool and agent events |
| [`repeat-tool-reminder/`](repeat-tool-reminder/README.md) | Advisory reminders for repeated tool calls | listens on tool and agent events |
| [`timeout-policy/`](timeout-policy/README.md) | Arms per-call tool deadlines as deployment policy | registers a `tools/execute` listener |
Reminders travel as `additionalContexts` on the `tools/post-execute` decision and are appended as logged plugin-sourced `user/message` events ([tools](../../docs/subsystems/tools.md)); the timeout split across `dsh-timeout`, capability termination, and this policy layer is recorded in the [timeout-library Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md).

View File

@@ -6,7 +6,7 @@
| 包 | 职责 | ctx key |
|---|---|---|
| [`repeat-tool-guard/`](repeat-tool-guard/README.md) | 针对重复工具调用的建议性提醒 | 监听工具和 agent 事件 |
| [`repeat-tool-reminder/`](repeat-tool-reminder/README.md) | 针对重复工具调用的建议性提醒 | 监听工具和 agent 事件 |
| [`timeout-policy/`](timeout-policy/README.md) | 以部署策略形式设置单次工具调用截止时间 | 注册 `tools/execute` 监听器 |
提醒作为 `additionalContexts``tools/post-execute` 决策传递,并作为来源于插件的 `user/message` 事件追加记录([工具](../../docs/subsystems/tools.md));跨 `dsh-timeout`、能力终止与本策略层的超时拆分记录在[超时库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/guard/repeat-tool-guard/README.md
README.md: 4d7b24b1188d021a82ed87b0c721fa539a4e5f0b
README.zh.md: b332d579d10af1a55d0bf75207452ae7d15bb7c9
# pnpm run verify-translation-pairing --write packages/guard/repeat-tool-reminder/README.md
README.md: cfece8150dce8e2fe45b4e942bf7188f46836551
README.zh.md: 439480d876ad07d8bc095b2534a5a2ce732bfc9b

View File

@@ -1,14 +1,14 @@
# @deepseek-ai/dsh-repeat-tool-guard
# @deepseek-ai/dsh-repeat-tool-reminder
English | [中文](README.zh.md)
An advisory loop-breaker, not a model-facing tool: it never appears in the tool list, never vetoes or rewrites a call, and adds exactly one behavior — it watches each agent's stream of tool calls, counts runs of consecutive calls to the same tool with identical canonicalized arguments, and at configured run lengths injects an escalating advisory reminder telling the model to stop repeating itself, re-read the last result, and either change approach or conclude. The decision (retry differently, gather more evidence, or finish) stays entirely with the model: a legitimately repeated call is delayed by nothing and blocked by nothing. Decision record: [the repeat-tool-guard Agent Note](../../../.agents/notes/archived/feature/2026-07-08-repeat-tool-guard.md).
An advisory loop-breaker, not a model-facing tool: it never appears in the tool list, never vetoes or rewrites a call, and adds exactly one behavior — it watches each agent's stream of tool calls, counts runs of consecutive calls to the same tool with identical canonicalized arguments, and at configured run lengths injects an escalating advisory reminder telling the model to stop repeating itself, re-read the last result, and either change approach or conclude. The decision (retry differently, gather more evidence, or finish) stays entirely with the model: a legitimately repeated call is delayed by nothing and blocked by nothing. Decision record: [the repeat-tool-reminder Agent Note](../../../.agents/notes/archived/feature/2026-07-08-repeat-tool-guard.md).
## Config
```yaml
- id: repeat-tool-guard
name: '@deepseek-ai/dsh-repeat-tool-guard'
- id: repeat-tool-reminder
name: '@deepseek-ai/dsh-repeat-tool-reminder'
config:
thresholds: [3, 5, 8] # default; consecutive counts that trigger a reminder
include: [] # tool-name patterns to track; empty ⇒ all tools
@@ -32,7 +32,7 @@ The chain key is `(tool name, canonical arguments)` — canonicalization is a de
## Reminder delivery
Reminders ride the post-execute decision's `additionalContexts` (source `{kind: 'plugin', plugin: 'repeat-tool-guard'}`), never a `content` replacement: the `tool/result` event stays the tool's own output for audit. The loop buffers the context and appends it as an injected `user/message` after the step's tool results, which the session renders as a plain synthetic user message — so the reminder is model-visible, source-attributed, and reconstructable from the session log with no new session event. The guard always delegates via `next()` and prepends its reminder to the downstream decision's context array (both variants — a blocked call still gets the nudge); every entry retains its own source and metadata.
Reminders ride the post-execute decision's `additionalContexts` (source `{kind: 'plugin', plugin: 'repeat-tool-reminder'}`), never a `content` replacement: the `tool/result` event stays the tool's own output for audit. The loop buffers the context and appends it as an injected `user/message` after the step's tool results, which the session renders as a plain synthetic user message — so the reminder is model-visible, source-attributed, and reconstructable from the session log with no new session event. The guard always delegates via `next()` and prepends its reminder to the downstream decision's context array (both variants — a blocked call still gets the nudge); every entry retains its own source and metadata.
## Model Experience

View File

@@ -1,14 +1,14 @@
# @deepseek-ai/dsh-repeat-tool-guard
# @deepseek-ai/dsh-repeat-tool-reminder
[English](README.md) | 中文
这是一个仅提供建议的循环中断器,而非面向模型的工具:它不会出现在工具列表中,不会否决或改写调用,只增加一种行为。它监视每个 agent智能体的工具调用流统计以完全相同的规范化参数连续调用同一工具的次数达到所配置的连续次数时它会注入逐级增强的提示要求模型停止重复、重新阅读上一次结果并改用其他方案或结束任务。究竟是换一种方式重试、收集更多证据还是完成任务仍完全由模型决定合理的重复调用既不会延迟也不会受阻。决策记录见 [repeat-tool-guard Agent Note](../../../.agents/notes/archived/feature/2026-07-08-repeat-tool-guard.md)。
这是一个仅提供建议的循环中断器,而非面向模型的工具:它不会出现在工具列表中,不会否决或改写调用,只增加一种行为。它监视每个 agent智能体的工具调用流统计以完全相同的规范化参数连续调用同一工具的次数达到所配置的连续次数时它会注入逐级增强的提示要求模型停止重复、重新阅读上一次结果并改用其他方案或结束任务。究竟是换一种方式重试、收集更多证据还是完成任务仍完全由模型决定合理的重复调用既不会延迟也不会受阻。决策记录见 [repeat-tool-reminder Agent Note](../../../.agents/notes/archived/feature/2026-07-08-repeat-tool-guard.md)。
## 配置
```yaml
- id: repeat-tool-guard
name: '@deepseek-ai/dsh-repeat-tool-guard'
- id: repeat-tool-reminder
name: '@deepseek-ai/dsh-repeat-tool-reminder'
config:
thresholds: [3, 5, 8] # default; consecutive counts that trigger a reminder
include: [] # tool-name patterns to track; empty ⇒ all tools
@@ -32,7 +32,7 @@
## 提醒传递
提醒通过 post-execute 决策中的 `additionalContexts`(来源为 `{kind: 'plugin', plugin: 'repeat-tool-guard'}`)传递,绝不替换 `content`;用于审计的 `tool/result` 事件仍保留工具自己的输出。循环会缓冲这段上下文,并在该步骤的工具结果之后将其作为注入的 `user/message` 追加会话会将它渲染为普通的合成用户消息。因此提醒对模型可见、带有来源归属并且无需增加会话事件即可从会话日志重建。guard 始终通过 `next()` 委派,并将自己的提醒放在下游决策的上下文数组之前(两种结果都适用:被阻止的调用也会收到提醒);每个条目保留自己的来源和元数据。
提醒通过 post-execute 决策中的 `additionalContexts`(来源为 `{kind: 'plugin', plugin: 'repeat-tool-reminder'}`)传递,绝不替换 `content`;用于审计的 `tool/result` 事件仍保留工具自己的输出。循环会缓冲这段上下文,并在该步骤的工具结果之后将其作为注入的 `user/message` 追加会话会将它渲染为普通的合成用户消息。因此提醒对模型可见、带有来源归属并且无需增加会话事件即可从会话日志重建。guard 始终通过 `next()` 委派,并将自己的提醒放在下游决策的上下文数组之前(两种结果都适用:被阻止的调用也会收到提醒);每个条目保留自己的来源和元数据。
## 模型体验

View File

@@ -1,5 +1,5 @@
{
"name": "@deepseek-ai/dsh-repeat-tool-guard",
"name": "@deepseek-ai/dsh-repeat-tool-reminder",
"description": "Repeat-tool-call guard plugin: advisory reminders when an agent loops on identical tool calls",
"version": "0.0.1-rc.2",
"publishConfig": {
@@ -8,7 +8,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/guard/repeat-tool-guard"
"directory": "packages/guard/repeat-tool-reminder"
},
"type": "module",
"main": "lib/index.js",

View File

@@ -2,8 +2,8 @@
* Advisory per-agent repeat-call detector. It enriches post-execute decisions
* with logged model context without vetoing or rewriting calls. Configuration
* and chain semantics live in the package README; rationale lives in the
* repeat-tool-guard Agent Note.
* @module @deepseek-ai/dsh-repeat-tool-guard
* repeat-tool-reminder Agent Note.
* @module @deepseek-ai/dsh-repeat-tool-reminder
*/
import type { Context } from '@deepseek-ai/cordis'
@@ -14,7 +14,7 @@ import type { MessageSource } from '@deepseek-ai/dsh-llm'
import type { UserMessage } from '@deepseek-ai/dsh-session'
import type { PostToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'
export const name = 'repeat-tool-guard'
export const name = 'repeat-tool-reminder'
/**
* Plugin config, validated by the same-named schemastery schema plus the
@@ -54,7 +54,7 @@ export const Config: z<Config> = z.object({
* the label is load-bearing (an unlabeled context would render as a user
* prompt in derived history).
*/
const PLUGIN_SOURCE: MessageSource = { kind: 'plugin', plugin: 'repeat-tool-guard' }
const PLUGIN_SOURCE: MessageSource = { kind: 'plugin', plugin: 'repeat-tool-reminder' }
/**
* The gentle first-threshold reminder. Keyed to `thresholds[0]`, not a literal
@@ -127,15 +127,15 @@ function previewArguments(canonical: string, cap: number): string {
*/
function validateThresholds(values: number[]): number[] {
if (values.length === 0) {
throw new Error('repeat-tool-guard: `thresholds` must not be empty')
throw new Error('repeat-tool-reminder: `thresholds` must not be empty')
}
for (const value of values) {
if (!Number.isInteger(value) || value < 2) {
throw new Error(`repeat-tool-guard: invalid threshold ${value} — every threshold must be an integer >= 2`)
throw new Error(`repeat-tool-reminder: invalid threshold ${value} — every threshold must be an integer >= 2`)
}
}
if (new Set(values).size !== values.length) {
throw new Error('repeat-tool-guard: `thresholds` must not contain duplicates')
throw new Error('repeat-tool-reminder: `thresholds` must not contain duplicates')
}
return [...values].sort((a, b) => a - b)
}
@@ -167,7 +167,7 @@ export function apply(ctx: Context, config: Config): void {
const excludePatterns = (config.exclude as string[]).map(wildcardToRegExp)
const argumentsPreviewChars = config.argumentsPreviewChars as number
if (!Number.isInteger(argumentsPreviewChars) || argumentsPreviewChars < 1) {
throw new Error(`repeat-tool-guard: invalid argumentsPreviewChars ${argumentsPreviewChars} — must be an integer >= 1`)
throw new Error(`repeat-tool-reminder: invalid argumentsPreviewChars ${argumentsPreviewChars} — must be an integer >= 1`)
}
const chains = new WeakMap<Agent, Chain>()
@@ -182,7 +182,7 @@ export function apply(ctx: Context, config: Config): void {
* Advance the calling agent's chain for one attempt and return the reminder
* to deliver, if this attempt's run length hits a configured threshold.
* Counting happens here in post-execute because denied calls also flow
* through this waterfall (`ToolRegistry.execute` routes a deny through the
* through this waterfall (`ToolRuntime.execute` routes a deny through the
* same pipeline), and a model hammering a denied call is exactly the loop
* worth breaking.
*/

View File

@@ -1,16 +1,16 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-repeat-tool-guard`.
* @module @deepseek-ai/dsh-repeat-tool-guard/invariant
* Package-owned invariant companion for `@deepseek-ai/dsh-repeat-tool-reminder`.
* @module @deepseek-ai/dsh-repeat-tool-reminder/invariant
*/
/* jscpd:ignore-start */
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-repeat-tool-guard'
const PACKAGE_NAME = '@deepseek-ai/dsh-repeat-tool-reminder'
/** Cordis companion plugin name. */
export const name = 'repeat-tool-guard-invariant'
export const name = 'repeat-tool-reminder-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']

View File

@@ -6,8 +6,8 @@ import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
import type { Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import * as RepeatToolGuard from '@deepseek-ai/dsh-repeat-tool-guard'
import type { Config } from '@deepseek-ai/dsh-repeat-tool-guard'
import * as RepeatToolGuard from '@deepseek-ai/dsh-repeat-tool-reminder'
import type { Config } from '@deepseek-ai/dsh-repeat-tool-reminder'
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
const testToolSignal = new AbortController().signal
@@ -49,7 +49,7 @@ function reminders(agent: Agent): { text: string; source: unknown }[] {
// call so a reader sees it without expanding the row.
const guardSource = (tool: string, count: number) => ({
kind: 'plugin',
plugin: 'repeat-tool-guard',
plugin: 'repeat-tool-reminder',
form: 'notice',
summary: `${tool} × ${count}`,
})

View File

@@ -27,7 +27,7 @@
"path": "../../llm/llm"
},
{
"path": "../../support/invariants"
"path": "../../runtime-diagnostics/invariants"
}
]
}

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/guard/timeout-policy/README.md
README.md: 70b1f392bfbce91962447dee86afbbb148817811
README.zh.md: 4433b3cb5f6f2291468dfd92345e2d826d951b47
README.md: 4b0ea4e541f0740289623f5e81d79e168240a402
README.zh.md: b69c9b251ad794244daa3695c5aa40acef4cd9d7

View File

@@ -1,4 +1,4 @@
# dsh-timeout-policy
# dsh-tool-call-timeout-policy
English | [中文](README.zh.md)
@@ -10,7 +10,7 @@ A function/namespace plugin (`name` / `inject` / `apply`), not a service. It reg
```yaml
- id: timeout-policy
name: '@deepseek-ai/dsh-timeout-policy'
name: '@deepseek-ai/dsh-tool-call-timeout-policy'
```
The per-tool budget is declared by the tool plugin (e.g. `dsh-tool-web`'s `fetchTimeoutMs`/`searchTimeoutMs` config, attached as `ToolDefinition.timeoutMs`); this plugin only enforces it, so a mistyped tool name is not possible.

View File

@@ -1,4 +1,4 @@
# dsh-timeout-policy
# dsh-tool-call-timeout-policy
[English](README.md) | 中文
@@ -10,7 +10,7 @@
```yaml
- id: timeout-policy
name: '@deepseek-ai/dsh-timeout-policy'
name: '@deepseek-ai/dsh-tool-call-timeout-policy'
```
每工具预算由工具插件声明(例如 `dsh-tool-web``fetchTimeoutMs``searchTimeoutMs` 配置,会附加为 `ToolDefinition.timeoutMs`);此插件只负责强制执行,因此不可能拼错工具名。

View File

@@ -1,5 +1,5 @@
{
"name": "@deepseek-ai/dsh-timeout-policy",
"name": "@deepseek-ai/dsh-tool-call-timeout-policy",
"description": "Tool-call timeout policy: a tools/execute wrapper that arms a per-tool deadline on exec.signal and returns TOOL_TIMEOUT when it wins",
"version": "0.0.1-rc.2",
"publishConfig": {

View File

@@ -8,7 +8,7 @@
* home; decide at resolution time
* ([regrouping Agent Note](../../../../.agents/notes/implemented/architecture/2026-07-29-package-regrouping.md)).
*
* @module @deepseek-ai/dsh-timeout-policy
* @module @deepseek-ai/dsh-tool-call-timeout-policy
*/
import type { Context } from '@deepseek-ai/cordis'

View File

@@ -1,13 +1,13 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-timeout-policy`.
* @module @deepseek-ai/dsh-timeout-policy/invariant
* Package-owned invariant companion for `@deepseek-ai/dsh-tool-call-timeout-policy`.
* @module @deepseek-ai/dsh-tool-call-timeout-policy/invariant
*/
/* jscpd:ignore-start */
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-timeout-policy'
const PACKAGE_NAME = '@deepseek-ai/dsh-tool-call-timeout-policy'
/** Cordis companion plugin name. */
export const name = 'timeout-policy-invariant'

View File

@@ -1,5 +1,5 @@
/**
* Unit + real-load-path coverage for @deepseek-ai/dsh-timeout-policy. The
* Unit + real-load-path coverage for @deepseek-ai/dsh-tool-call-timeout-policy. The
* timeout-wins cases drive the deadline under fake timers (deterministic — no
* wall-clock race) and use a COOPERATIVE tool that settles only when its
* `exec.signal` aborts, mirroring how a real capability forwards the signal and
@@ -11,9 +11,9 @@ import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import { CallId, HarnessError } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { defineContentToolFixture, TOOL_ABORTED, type ToolExecutionInput, type PostToolDecision } from '@deepseek-ai/dsh-tools'
import * as timeoutPolicy from '@deepseek-ai/dsh-timeout-policy'
import { TOOL_TIMEOUT } from '@deepseek-ai/dsh-timeout-policy'
import ToolRuntime, { defineContentToolFixture, TOOL_ABORTED, type ToolExecutionInput, type PostToolDecision } from '@deepseek-ai/dsh-tools'
import * as timeoutPolicy from '@deepseek-ai/dsh-tool-call-timeout-policy'
import { TOOL_TIMEOUT } from '@deepseek-ai/dsh-tool-call-timeout-policy'
const testToolSignal = new AbortController().signal
@@ -21,7 +21,7 @@ const testToolSignal = new AbortController().signal
async function setup() {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
await ctx.plugin(timeoutPolicy)
return ctx
}
@@ -203,7 +203,7 @@ describe('timeout-policy disposal (HMR safety)', () => {
it('removes its tools/execute listener when the plugin fiber disposes', async () => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
let seenSignal: AbortSignal | undefined
ctx.tools.register(defineContentToolFixture({ name: 'probe', description: 'd', parameters: {}, timeoutMs: 10_000,
async execute(_a, exec) { seenSignal = exec.signal; return [{ type: 'text' as const, text: 'ok' }] } }))
@@ -217,7 +217,7 @@ describe('timeout-policy disposal (HMR safety)', () => {
})
})
describe('dsh-timeout-policy real-load-path guard', () => {
describe('dsh-tool-call-timeout-policy real-load-path guard', () => {
it('has no default export and keeps name/inject through unwrapExports', () => {
expect('default' in timeoutPolicy).toBe(false)
const loader = Object.create(Loader.prototype) as Loader
@@ -231,7 +231,7 @@ describe('dsh-timeout-policy real-load-path guard', () => {
it('boots over ctx.tools through the unwrapped module and wraps a budgeted tool', async () => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
ctx.tools.register(defineContentToolFixture({ name: 'fast', description: 'd', parameters: {}, timeoutMs: 5_000,
async execute() { return [{ type: 'text' as const, text: 'ok' }] } }))
const loader = Object.create(Loader.prototype) as Loader

View File

@@ -25,7 +25,7 @@
"path": "../../core/tools"
},
{
"path": "../../support/invariants"
"path": "../../runtime-diagnostics/invariants"
}
]
}