dsh-thinking-guard 0.1.16 → 0.1.19
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.md +80 -170
- package/lib/client.js +3 -3
- package/lib/guard-core.js +2 -14
- package/lib/guard-core.js.map +1 -1
- package/lib/guard-loop-core.js +122 -19
- package/lib/guard-loop-core.js.map +1 -1
- package/lib/guard.js +210 -83
- package/lib/guard.js.map +1 -1
- package/lib/invoke.js +16 -10
- package/lib/invoke.js.map +1 -1
- package/lib/policy.js +154 -8
- package/lib/policy.js.map +1 -1
- package/lib/registry.js +12 -2
- package/lib/registry.js.map +1 -1
- package/lib/types/guard-core.d.ts +22 -5
- package/lib/types/guard-core.d.ts.map +1 -1
- package/lib/types/guard-loop-core.d.ts +49 -3
- package/lib/types/guard-loop-core.d.ts.map +1 -1
- package/lib/types/guard.d.ts.map +1 -1
- package/lib/types/invoke.d.ts.map +1 -1
- package/lib/types/policy.d.ts +12 -0
- package/lib/types/policy.d.ts.map +1 -1
- package/lib/types/registry.d.ts.map +1 -1
- package/package.json +144 -144
package/README.md
CHANGED
|
@@ -1,170 +1,80 @@
|
|
|
1
|
-
# dsh-thinking-guard
|
|
2
|
-
|
|
3
|
-
一个 DSH 插件:把**能力目录与按需调用**(Capability Menu 思路)和 **Agent 效率守卫**(重复/循环调用提醒)整合在一起。当前版本 **0.1.
|
|
4
|
-
|
|
5
|
-
## 它做什么
|
|
6
|
-
|
|
7
|
-
- `meta_search` 按需查找能力,`meta_invoke` 调用能力;工具与技能按 Resident / On-demand / Disabled 三档分层,Disabled 会在执行入口拒绝调用,不只是隐藏 Schema。
|
|
8
|
-
- Guard 按 Agent / Turn 隔离,用工具签名与 Step 的结果/错误指纹做重复与 A-B 循环检测;只在证据满足门槛时对匹配调用局部拒绝一次,**不停止整个 Turn**。
|
|
9
|
-
- 设置页提供四个页面:总览、防循环、调用统计、能力目录。
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
## 安装
|
|
18
|
-
|
|
19
|
-
一条命令:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
dsh plugin --profile desktop add dsh-thinking-guard
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
装完**重启 DSH**。
|
|
26
|
-
|
|
27
|
-
###
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
dsh plugin
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
### 装完看不到插件
|
|
82
|
-
|
|
83
|
-
先查这一条,它解释了绝大多数情况:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
node -e "const m=require(process.env.HOME+'/.dsh/profiles/desktop/package.json'); console.log('bundled:', m.dsh.profile.bundles.includes('dsh-thinking-guard'))"
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
`false` → 当初是手改 JSON + `pnpm install` 装的,包在但没挂载。补跑一次 `dsh plugin add <附件URL>`,或手工把包名加进 `dsh.profile.bundles`,然后重启。
|
|
90
|
-
|
|
91
|
-
`true` 却仍看不到 → 确认**已重启** DSH;再看 设置 → 插件 是否有加载报错。
|
|
92
|
-
|
|
93
|
-
### 装进去的还是旧版本
|
|
94
|
-
|
|
95
|
-
几乎一定是缓存问题。比对安装目录与 Release 附件里的 `lib/client.js` 大小:
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
ls -la ~/.dsh/profiles/desktop/node_modules/dsh-thinking-guard/lib/client.js
|
|
99
|
-
tar -tzvf <下载的 tgz> | grep 'lib/client.js'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
不一致说明命中了旧缓存——确认你 pin 的是**带哈希**的附件名。
|
|
103
|
-
|
|
104
|
-
### `dsh plugin add` 超时 / `fetch failed`
|
|
105
|
-
|
|
106
|
-
GitHub 附件下载在部分网络下会 `ETIMEDOUT`。先确认代理可用,再重跑:
|
|
107
|
-
|
|
108
|
-
```powershell
|
|
109
|
-
pwsh -NoProfile -File scripts/install.ps1 -Proxy http://127.0.0.1:7890
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
或手动设环境变量后重跑方式三:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
export HTTPS_PROXY=http://127.0.0.1:7890
|
|
116
|
-
export HTTP_PROXY=http://127.0.0.1:7890
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
注意:**API 与附件下载的可达性可能不一致**。本机实测 `api.github.com` 与 `releases/download/...` 直连可通,但 `github.com/.../releases/latest` 这个 HTML 页会超时——所以脚本只用 API,不抓网页。
|
|
120
|
-
|
|
121
|
-
### `ERR_PNPM_FETCH_404` / `is not in the npm registry`
|
|
122
|
-
|
|
123
|
-
用了裸包名。本包不在 npm registry,必须给完整附件 URL。
|
|
124
|
-
|
|
125
|
-
### `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`
|
|
126
|
-
|
|
127
|
-
用了 git 来源。换 Release 附件,不要去改 `allowBuilds`。
|
|
128
|
-
|
|
129
|
-
### 提示 DSH 版本不兼容
|
|
130
|
-
|
|
131
|
-
`dsh plugin add` 会在安装前读包的 manifest,比对它对 DSH 的版本要求(`engines` 与 `peerDependencies`),不匹配就拒绝安装并回滚 manifest 与 lockfile。它会在输出里打印一条
|
|
132
|
-
`dsh plugin --profile desktop allow-version <包@版本> --dsh-version <版本> --accept-risk`。
|
|
133
|
-
**那条命令会跳过兼容检查**,只在确认风险后执行。
|
|
134
|
-
|
|
135
|
-
### 找不到 DSH runtime / profile
|
|
136
|
-
|
|
137
|
-
`~/.dsh/profiles/desktop` 与 `~/.dsh/dsh-runtimes/` 都是 DSH 首次启动才生成的。先正常启动一次 DSH。
|
|
138
|
-
|
|
139
|
-
### 想看 pnpm 到底报了什么
|
|
140
|
-
|
|
141
|
-
`dsh plugin` 把完整 pnpm 输出写到 profile 下的日志目录:
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
ls -t ~/.dsh/profiles/desktop/.plugin-manager/logs | head
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## 配置
|
|
148
|
-
|
|
149
|
-
默认 Guard 模式为 `normal`。当前 Step、Tool 次数、Duration 阈值只保留配置校验和计数,不触发软提醒或硬停止。`stepLimitMode: hard-stop` 会归一为 `warn-only`,`duplicateHardBlock: true` 会归一为 `false`,启动时分别记录弃用提示。开启 `duplicateDetection` 时,连续重复的第 3/5/8 次与有证据的 A-B 循环会提醒;只有相同操作及结果/错误指纹满足策略门槛后,才会通过 `tools.guard` 对匹配调用局部拒绝一次,不停止整个 Turn。配置位于插件 root entry 的 `config.guard`,状态按 Agent / Turn 隔离。
|
|
150
|
-
|
|
151
|
-
## 文档入口
|
|
152
|
-
|
|
153
|
-
本文件就是代码仓的 README(工作区里位于 `01_Source_Code/README.md`)。其余文档在**工作区根**,不随仓库发布:
|
|
154
|
-
|
|
155
|
-
| 想做的事 | 去哪 |
|
|
156
|
-
|---|---|
|
|
157
|
-
| 找任意文档 | 根目录 `PROJECT_INDEX.md` |
|
|
158
|
-
| 看现在做到哪 / 接手 | 根目录 `HANDOFF.md` |
|
|
159
|
-
| 长期规则与开发规范 | 根目录 `AGENTS.md` |
|
|
160
|
-
| 查架构与模块方案 | 见项目层 `02_Design/` 下的方案文档(不在本仓库内) |
|
|
161
|
-
| 查历史与归档 | 根目录 `04_Docs/02_history/README.md` |
|
|
162
|
-
|
|
163
|
-
## 目录约定
|
|
164
|
-
|
|
165
|
-
- 本目录是**唯一代码仓**(独立 git,远端 `YAN3RA/dsh-thinking-guard`)。
|
|
166
|
-
- 仓库只发布**源码 + 测试 + 本 README**。`AGENTS.md` / `PROJECT_INDEX.md` / `HANDOFF.md` 与 `docs/` 均为本地保留,不提交。
|
|
167
|
-
|
|
168
|
-
## 来源与许可
|
|
169
|
-
|
|
170
|
-
本项目基于 Capability Menu 实现,并按 Apache License 2.0 发布。详见 [LICENSE](LICENSE)。
|
|
1
|
+
# dsh-thinking-guard
|
|
2
|
+
|
|
3
|
+
一个 DSH 插件:把**能力目录与按需调用**(Capability Menu 思路)和 **Agent 效率守卫**(重复/循环调用提醒)整合在一起。当前版本 **0.1.19**。
|
|
4
|
+
|
|
5
|
+
## 它做什么
|
|
6
|
+
|
|
7
|
+
- `meta_search` 按需查找能力,`meta_invoke` 调用能力;工具与技能按 Resident / On-demand / Disabled 三档分层,Disabled 会在执行入口拒绝调用,不只是隐藏 Schema。
|
|
8
|
+
- Guard 按 Agent / Turn 隔离,用工具签名与 Step 的结果/错误指纹做重复与 A-B 循环检测;只在证据满足门槛时对匹配调用局部拒绝一次,**不停止整个 Turn**。
|
|
9
|
+
- 设置页提供四个页面:总览、防循环、调用统计、能力目录。
|
|
10
|
+
|
|
11
|
+
## 宿主兼容性
|
|
12
|
+
|
|
13
|
+
- **实测版本:DSH 0.2.2-rc.1**。本插件的开发、类型检查与测试都以该版本为基线(`devDependencies` 中 `@deepseek-ai/dsh-*` 均为 `0.2.2-rc.1`,`node_modules/@deepseek-ai/dsh-tools` 实测也是 `0.2.2-rc.1`)。
|
|
14
|
+
- `peerDependencies` 声明的范围是 `^0.2.0 || ^0.2.1-rc.3 || ^0.2.2-rc.1`。范围内**除 0.2.2-rc.1 以外的版本均未实测**,属于理论兼容,不代表已验证可用。
|
|
15
|
+
- 只有 `0.2.2-rc.1` 上跑过完整检查;其他 0.2.x 版本请按「提示 DSH 版本不兼容」一节的流程自行评估风险。
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
一条命令:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
dsh plugin --profile desktop add dsh-thinking-guard
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
装完**重启 DSH**。
|
|
26
|
+
|
|
27
|
+
### 指定版本 / 升级
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
dsh plugin --profile desktop add dsh-thinking-guard@0.1.19 # 锁定版本
|
|
31
|
+
dsh plugin --profile desktop add dsh-thinking-guard # 升到最新
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 不支持的安装来源
|
|
35
|
+
|
|
36
|
+
| 来源 | 结果 | 原因 |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `git+https://…` / `github:owner/repo` | ❌ | 仓库不含 `lib/`,pnpm 需现场跑 `prepare` 构建,而 pnpm 默认拦截 git 依赖的构建脚本 |
|
|
39
|
+
| `link:<本地目录>` | ⚠️ 能装不推荐 | 软链到源码目录,peer 依赖不从目标 `node_modules` 解析 |
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## 升级
|
|
43
|
+
|
|
44
|
+
重跑方式一 / 方式二即可。脚本会比对当前 pin 与最新附件:
|
|
45
|
+
|
|
46
|
+
- 一致 → 只重装同样的字节,`bundles` 不动
|
|
47
|
+
- 不一致 → 打印 `from:` / `to:`,走 `dsh plugin add` 换版本
|
|
48
|
+
|
|
49
|
+
手动升级就是把方式三第 2 条的 URL 换成新版本的哈希附件再跑一次。
|
|
50
|
+
|
|
51
|
+
## 配置
|
|
52
|
+
|
|
53
|
+
默认 Guard 模式为 `normal`。Guard 的行为由三个开关各自独立控制:
|
|
54
|
+
|
|
55
|
+
- `duplicateDetection`:精确重复提醒。开启后,同一操作及相同结果/错误指纹构成的连续重复达到第 3/5/8 次时给出提醒。
|
|
56
|
+
- `alternatingCycleDetection`:交替循环识别。开启后,识别有证据的 A-B 交替循环并给出提醒;它与 `duplicateDetection` 互相独立,关掉其一不影响另一个。
|
|
57
|
+
- `noProgressProtection`:局部拒绝的总闸。关闭时 Guard 仍然提醒,但**绝不拒绝任何调用**;只有开启、且相同操作及结果/错误指纹满足门槛时,才通过 `tools.guard` 对匹配调用局部拒绝一次,不停止整个 Turn。
|
|
58
|
+
|
|
59
|
+
Quick / Normal / Deep 里的 `soft*` / `hard*` 阈值(Step、Tool 次数、Duration)只是兼容字段与计数口径,**不产生提醒,也不停止任务**。`stepLimitMode: hard-stop` 会归一为 `warn-only`,`duplicateHardBlock: true` 会归一为 `false`,启动时分别记录弃用提示。配置位于插件 root entry 的 `config.guard`,状态按 Agent / Turn 隔离。
|
|
60
|
+
|
|
61
|
+
## 文档入口
|
|
62
|
+
|
|
63
|
+
本文件就是代码仓的 README(工作区里位于 `01_Source_Code/README.md`)。其余文档在**工作区根**,不随仓库发布:
|
|
64
|
+
|
|
65
|
+
| 想做的事 | 去哪 |
|
|
66
|
+
|---|---|
|
|
67
|
+
| 找任意文档 | 根目录 `PROJECT_INDEX.md` |
|
|
68
|
+
| 看现在做到哪 / 接手 | 根目录 `HANDOFF.md` |
|
|
69
|
+
| 长期规则与开发规范 | 根目录 `AGENTS.md` |
|
|
70
|
+
| 查架构与模块方案 | 见项目层 `02_Design/` 下的方案文档(不在本仓库内) |
|
|
71
|
+
| 查历史与归档 | 根目录 `04_Docs/02_history/README.md` |
|
|
72
|
+
|
|
73
|
+
## 目录约定
|
|
74
|
+
|
|
75
|
+
- 本目录是**唯一代码仓**(独立 git,远端 `YAN3RA/dsh-thinking-guard`)。
|
|
76
|
+
- 仓库只发布**源码 + 测试 + 本 README**。`AGENTS.md` / `PROJECT_INDEX.md` / `HANDOFF.md` 与 `docs/` 均为本地保留,不提交。
|
|
77
|
+
|
|
78
|
+
## 来源与许可
|
|
79
|
+
|
|
80
|
+
本项目基于 Capability Menu 实现,并按 Apache License 2.0 发布。详见 [LICENSE](LICENSE)。
|
package/lib/client.js
CHANGED
|
@@ -8323,7 +8323,7 @@ function MarkdownFilePreview(props) {
|
|
|
8323
8323
|
* `node_modules`, because the panel said nothing about itself. The value is
|
|
8324
8324
|
* inlined by `tsdown` (see `src/client/env.d.ts`).
|
|
8325
8325
|
*/
|
|
8326
|
-
const PLUGIN_VERSION = "0.1.
|
|
8326
|
+
const PLUGIN_VERSION = "0.1.19";
|
|
8327
8327
|
const PLUGIN_TITLE = `capability-menu v${PLUGIN_VERSION}`;
|
|
8328
8328
|
/**
|
|
8329
8329
|
* dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
|
|
@@ -10513,7 +10513,7 @@ async function apply(ctx) {
|
|
|
10513
10513
|
tierDisabled: "禁用",
|
|
10514
10514
|
guardTitle: "思考限制",
|
|
10515
10515
|
guardDesc: "只针对有证据的重复与无进展拦截具体操作;不因总步数、工具总次数或任务时长终止任务。",
|
|
10516
|
-
guardBoundary: "统计与限制是两件事:Step
|
|
10516
|
+
guardBoundary: "统计与限制是两件事:Step、工具调用次数、任务时长只用于统计与诊断,不构成硬停止条件。Quick / Normal / Deep 三档里的 Step、工具次数、时长阈值(soft* / hard*)是兼容字段与统计口径,只参与配置校验与计数,不产生提醒,也不停止任务。单个工具自身的超时另由宿主或该工具负责。",
|
|
10517
10517
|
guardReset: "恢复默认",
|
|
10518
10518
|
guardSectionA: "A · 相同调用重复提醒",
|
|
10519
10519
|
guardSectionB: "B · 交替循环识别",
|
|
@@ -10699,7 +10699,7 @@ async function apply(ctx) {
|
|
|
10699
10699
|
tierDisabled: "Disabled",
|
|
10700
10700
|
guardTitle: "Thinking Limits",
|
|
10701
10701
|
guardDesc: "Blocks specific operations only when there is evidence of repetition or no progress; never stops a task for step count, tool count or duration.",
|
|
10702
|
-
guardBoundary: "Counting and limiting are separate: steps, tool calls and task duration are statistics and diagnostics only, never hard-stop conditions. A single tool timeout stays with the host or that tool.",
|
|
10702
|
+
guardBoundary: "Counting and limiting are separate: steps, tool calls and task duration are statistics and diagnostics only, never hard-stop conditions. The per-level (Quick / Normal / Deep) step, tool-call and duration thresholds (soft* / hard*) are compatibility fields and counting units: they only take part in config validation and tallies, never producing a reminder or stopping a task. A single tool timeout stays with the host or that tool.",
|
|
10703
10703
|
guardReset: "Restore defaults",
|
|
10704
10704
|
guardSectionA: "A · Repeated identical calls",
|
|
10705
10705
|
guardSectionB: "B · Alternating cycle detection",
|
package/lib/guard-core.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
import { createGuardLoopState } from "./guard-loop-core.js";
|
|
3
2
|
export const DEFAULT_GUARD_PROFILES = {
|
|
4
3
|
quick: {
|
|
@@ -99,17 +98,6 @@ export function validateGuardConfig(input = {}) {
|
|
|
99
98
|
hostIntegration: host,
|
|
100
99
|
};
|
|
101
100
|
}
|
|
102
|
-
export function canonicalJson(value) {
|
|
103
|
-
if (value === null || typeof value !== 'object')
|
|
104
|
-
return JSON.stringify(value) ?? 'undefined';
|
|
105
|
-
if (Array.isArray(value))
|
|
106
|
-
return `[${value.map(canonicalJson).join(',')}]`;
|
|
107
|
-
const object = value;
|
|
108
|
-
return `{${Object.keys(object).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(object[key])}`).join(',')}}`;
|
|
109
|
-
}
|
|
110
|
-
export function toolSignature(name, argumentsValue) {
|
|
111
|
-
return createHash('sha256').update(name).update('\0').update(canonicalJson(argumentsValue)).digest('hex');
|
|
112
|
-
}
|
|
113
101
|
export class GuardStateStore {
|
|
114
102
|
states = new WeakMap();
|
|
115
103
|
beginTurn(agent, turn, now = Date.now()) {
|
|
@@ -124,7 +112,7 @@ export class GuardStateStore {
|
|
|
124
112
|
dispatchRecords: new Map(),
|
|
125
113
|
pendingLoopObservations: new Map(),
|
|
126
114
|
loopState: createGuardLoopState(turn),
|
|
127
|
-
|
|
115
|
+
pendingBlockCandidates: new Map(),
|
|
128
116
|
blockedLoopSignatures: new Set(),
|
|
129
117
|
deniedCallIds: new Set(),
|
|
130
118
|
localLoopDenials: 0,
|
|
@@ -165,7 +153,7 @@ export class EfficiencyGuard {
|
|
|
165
153
|
state.dispatchRecords.clear();
|
|
166
154
|
state.pendingLoopObservations.clear();
|
|
167
155
|
state.loopState = createGuardLoopState(state.turn);
|
|
168
|
-
state.
|
|
156
|
+
state.pendingBlockCandidates.clear();
|
|
169
157
|
state.blockedLoopSignatures.clear();
|
|
170
158
|
state.deniedCallIds.clear();
|
|
171
159
|
state.localLoopDenials = 0;
|
package/lib/guard-core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard-core.js","sourceRoot":"","sources":["../src/guard-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"guard-core.js","sourceRoot":"","sources":["../src/guard-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAyE,MAAM,sBAAsB,CAAA;AAuHlI,MAAM,CAAC,MAAM,sBAAsB,GAAqD;IACtF,KAAK,EAAE;QACL,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,cAAc,EAAE,OAAO;QACvB,cAAc,EAAE,OAAO;KACxB;IACD,MAAM,EAAE;QACN,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,OAAO;QACvB,cAAc,EAAE,OAAO;KACxB;IACD,IAAI,EAAE;QACJ,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,aAAa,EAAE,EAAE;QACjB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,OAAO;QACvB,cAAc,EAAE,SAAS;KAC1B;CACF,CAAA;AAED,MAAM,CAAC,MAAM,wBAAwB,GAA0B;IAC7D,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,YAAY;IACtB,oBAAoB,EAAE,IAAI;IAC1B,qBAAqB,EAAE,IAAI;IAC3B,2BAA2B,EAAE,CAAC;IAC9B,mBAAmB,EAAE,CAAC;IACtB,aAAa,EAAE,gBAAgB;CAChC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,QAAQ;IACjB,aAAa,EAAE,WAAW;IAC1B,kBAAkB,EAAE,IAAI;IACxB,yBAAyB,EAAE,IAAI;IAC/B,oBAAoB,EAAE,IAAI;IAC1B,kBAAkB,EAAE,CAAC;IACrB,kBAAkB,EAAE,KAAK;IACzB,QAAQ,EAAE,sBAAsB;IAChC,eAAe,EAAE,wBAAwB;CAC1C,CAAA;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAc;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,kCAAkC,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,OAAqB;IAC1D,qBAAqB,CAAC,GAAG,IAAI,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IAC7D,qBAAqB,CAAC,GAAG,IAAI,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IAC7D,qBAAqB,CAAC,GAAG,IAAI,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IACrE,qBAAqB,CAAC,GAAG,IAAI,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IACrE,qBAAqB,CAAC,GAAG,IAAI,iBAAiB,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACvE,qBAAqB,CAAC,GAAG,IAAI,iBAAiB,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACvE,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wCAAwC,CAAC,CAAA;IAC5G,IAAI,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,gDAAgD,CAAC,CAAA;IAC5H,IAAI,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,kDAAkD,CAAC,CAAA;IAChI,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAA8B,EAAE;IAClE,MAAM,QAAQ,GAAG,EAAE,GAAG,sBAAsB,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAA;IACzE,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAU;QAAE,eAAe,CAAC,YAAY,IAAI,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5G,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC3D,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;QACnG,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACjE,CAAC;IACD,MAAM,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,IAAI,oBAAoB,CAAC,kBAAkB,CAAA;IAC9F,qBAAqB,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,EAAE,GAAG,wBAAwB,EAAE,GAAG,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,CAAA;IAC9E,qBAAqB,CAAC,6CAA6C,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAA;IACtG,qBAAqB,CAAC,qCAAqC,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAA;IACtF,IAAI,IAAI,CAAC,aAAa,KAAK,gBAAgB;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACpH,OAAO;QACL,GAAG,oBAAoB;QACvB,GAAG,KAAK;QACR,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,oBAAoB,CAAC,OAAO;QACtD,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,oBAAoB,CAAC,OAAO;QACtD,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,IAAI,oBAAoB,CAAC,kBAAkB;QACvF,yBAAyB,EACvB,KAAK,CAAC,yBAAyB,IAAI,oBAAoB,CAAC,yBAAyB;QACnF,oBAAoB,EAAE,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,CAAC,oBAAoB;QAC7F,aAAa,EAAE,WAAW;QAC1B,kBAAkB,EAAE,KAAK;QACzB,QAAQ;QACR,kBAAkB;QAClB,eAAe,EAAE,IAAI;KACtB,CAAA;AACH,CAAC;AAED,MAAM,OAAO,eAAe;IACT,MAAM,GAAG,IAAI,OAAO,EAAqB,CAAA;IAE1D,SAAS,CAAC,KAAa,EAAE,IAAY,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACrD,MAAM,KAAK,GAAc;YACvB,IAAI;YACJ,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,CAAC;YACZ,oBAAoB,EAAE,CAAC;YACvB,gBAAgB,EAAE,CAAC;YACnB,qBAAqB,EAAE,KAAK;YAC5B,cAAc,EAAE,IAAI,GAAG,EAAE;YACzB,eAAe,EAAE,IAAI,GAAG,EAAE;YAC1B,uBAAuB,EAAE,IAAI,GAAG,EAAE;YAClC,SAAS,EAAE,oBAAoB,CAAC,IAAI,CAAC;YACrC,sBAAsB,EAAE,IAAI,GAAG,EAAE;YACjC,qBAAqB,EAAE,IAAI,GAAG,EAAE;YAChC,aAAa,EAAE,IAAI,GAAG,EAAE;YACxB,gBAAgB,EAAE,CAAC;YACnB,YAAY,EAAE,IAAI,GAAG,EAAE;YACvB,UAAU,EAAE,CAAC;YACb,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,GAAG;SACpB,CAAA;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC7B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,GAAG,CAAC,KAAa;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,IAAY,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACtC,OAAO,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;IAC5E,CAAC;IAED,KAAK,CAAC,KAAa;QACjB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,eAAe;IACjB,MAAM,CAAa;IACnB,MAAM,CAAiB;IAEhC,YAAY,SAA+B,EAAE,EAAE,MAAM,GAAG,IAAI,eAAe,EAAE;QAC3E,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACpC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAM;QAC/B,KAAK,CAAC,SAAS,IAAI,CAAC,CAAA;QACpB,KAAK,CAAC,oBAAoB,GAAG,CAAC,CAAA;QAC9B,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAA;QAC1B,KAAK,CAAC,qBAAqB,GAAG,KAAK,CAAA;QACnC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,CAAA;QAC7B,KAAK,CAAC,uBAAuB,CAAC,KAAK,EAAE,CAAA;QACrC,KAAK,CAAC,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAClD,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,CAAA;QACpC,KAAK,CAAC,qBAAqB,CAAC,KAAK,EAAE,CAAA;QACnC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;QAC3B,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAA;QAC1B,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,CAAA;IAC5B,CAAC;IAED,SAAS,CAAC,KAAa,EAAE,IAAY,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACrD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;QACtD,KAAK,CAAC,UAAU,IAAI,CAAC,CAAA;QACrB,KAAK,CAAC,cAAc,GAAG,GAAG,CAAA;QAC1B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IAC1B,CAAC;IACD,WAAW,CAAC,KAAa,EAAE,IAAY,EAAE,WAAgC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACzF,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAM;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;QACtD,IAAI,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,YAAY;YAAE,OAAM;QAC1D,KAAK,CAAC,iBAAiB,IAAI,CAAC,CAAA;QAC5B,KAAK,CAAC,cAAc,GAAG,GAAG,CAAA;IAC5B,CAAC;CACF"}
|
package/lib/guard-loop-core.js
CHANGED
|
@@ -1,46 +1,133 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
export const LOOP_HISTORY_LIMIT = 16;
|
|
3
3
|
export const DEFAULT_REPEAT_THRESHOLDS = Object.freeze([3, 5, 8]);
|
|
4
|
+
/**
|
|
5
|
+
* Fingerprint budgets, checked *while* canonicalizing.
|
|
6
|
+
*
|
|
7
|
+
* Exceeding any budget abandons the fingerprint entirely. A truncated canonical
|
|
8
|
+
* form would still look complete and could make two different results compare
|
|
9
|
+
* equal — the false "no progress" evidence this guard must never invent.
|
|
10
|
+
*
|
|
11
|
+
* - `FINGERPRINT_MAX_NODES`: values visited (object keys charge bytes only).
|
|
12
|
+
* A 1000-row x 5-field tool result is ~6k nodes; 20k still fingerprints a
|
|
13
|
+
* ~3300-row table in a few milliseconds.
|
|
14
|
+
* - `FINGERPRINT_MAX_DEPTH`: nesting levels, root = 1. The recursive walk
|
|
15
|
+
* overflows the stack at ~2000 object levels on the bundled Node 22 runtime
|
|
16
|
+
* with the default stack (measured), so 64 keeps ~30x headroom and is still
|
|
17
|
+
* far deeper than any real payload (`content[] -> block -> value` is depth 4).
|
|
18
|
+
* - `FINGERPRINT_MAX_BYTES`: UTF-8 bytes of the canonical JSON. 256 KiB covers
|
|
19
|
+
* ordinary file reads, search hits and multi-hundred-KB JSON in under 1 ms,
|
|
20
|
+
* while a multi-megabyte result is rejected before it is fully materialized.
|
|
21
|
+
*/
|
|
22
|
+
export const FINGERPRINT_MAX_NODES = 20_000;
|
|
23
|
+
export const FINGERPRINT_MAX_DEPTH = 64;
|
|
24
|
+
export const FINGERPRINT_MAX_BYTES = 256 * 1024;
|
|
25
|
+
/** Internal signal: this value cannot be fingerprinted within budget. */
|
|
26
|
+
export class GuardFingerprintBudgetError extends Error {
|
|
27
|
+
limit;
|
|
28
|
+
constructor(limit) {
|
|
29
|
+
super(`guard fingerprint budget exceeded: ${limit}`);
|
|
30
|
+
this.name = 'GuardFingerprintBudgetError';
|
|
31
|
+
this.limit = limit;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
4
34
|
export function createGuardLoopState(turn) {
|
|
5
35
|
return { turn, history: [] };
|
|
6
36
|
}
|
|
7
|
-
function
|
|
37
|
+
function chargeBytes(budget, text) {
|
|
38
|
+
budget.bytes += Buffer.byteLength(text, 'utf8');
|
|
39
|
+
if (budget.bytes > FINGERPRINT_MAX_BYTES)
|
|
40
|
+
throw new GuardFingerprintBudgetError('bytes');
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Canonical JSON with hard node/depth/byte budgets.
|
|
45
|
+
*
|
|
46
|
+
* Throws `GuardFingerprintBudgetError` the moment a budget is crossed instead of
|
|
47
|
+
* returning a partial string, because a partial string is indistinguishable from
|
|
48
|
+
* a complete one. The cycle check stays a `TypeError` so cyclic input keeps its
|
|
49
|
+
* own error class.
|
|
50
|
+
*/
|
|
51
|
+
function canonicalJson(value, depth, budget, seen) {
|
|
52
|
+
budget.nodes += 1;
|
|
53
|
+
if (budget.nodes > FINGERPRINT_MAX_NODES)
|
|
54
|
+
throw new GuardFingerprintBudgetError('nodes');
|
|
55
|
+
if (depth > FINGERPRINT_MAX_DEPTH)
|
|
56
|
+
throw new GuardFingerprintBudgetError('depth');
|
|
8
57
|
if (value === null)
|
|
9
|
-
return 'null';
|
|
58
|
+
return chargeBytes(budget, 'null');
|
|
10
59
|
if (typeof value === 'string' || typeof value === 'boolean')
|
|
11
|
-
return JSON.stringify(value);
|
|
60
|
+
return chargeBytes(budget, JSON.stringify(value));
|
|
12
61
|
if (typeof value === 'number')
|
|
13
|
-
return Number.isFinite(value) ? JSON.stringify(value) : 'null';
|
|
62
|
+
return chargeBytes(budget, Number.isFinite(value) ? JSON.stringify(value) : 'null');
|
|
14
63
|
if (typeof value === 'undefined')
|
|
15
|
-
return '"[undefined]"';
|
|
16
|
-
if (typeof value === 'bigint')
|
|
17
|
-
return JSON.stringify(value.toString());
|
|
64
|
+
return chargeBytes(budget, '"[undefined]"');
|
|
18
65
|
if (typeof value !== 'object')
|
|
19
|
-
return JSON.stringify(String(value));
|
|
66
|
+
return chargeBytes(budget, JSON.stringify(String(value)));
|
|
20
67
|
if (seen.has(value))
|
|
21
68
|
throw new TypeError('Cannot fingerprint a cyclic value');
|
|
22
69
|
seen.add(value);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
70
|
+
const isArray = Array.isArray(value);
|
|
71
|
+
const open = chargeBytes(budget, isArray ? '[' : '{');
|
|
72
|
+
let body = '';
|
|
73
|
+
if (isArray) {
|
|
74
|
+
const items = value;
|
|
75
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
76
|
+
if (index > 0)
|
|
77
|
+
body += chargeBytes(budget, ',');
|
|
78
|
+
body += canonicalJson(items[index], depth + 1, budget, seen);
|
|
79
|
+
}
|
|
26
80
|
}
|
|
27
81
|
else {
|
|
28
82
|
const record = value;
|
|
29
|
-
|
|
83
|
+
for (const key of Object.keys(record).sort()) {
|
|
84
|
+
if (body.length > 0)
|
|
85
|
+
body += chargeBytes(budget, ',');
|
|
86
|
+
body += chargeBytes(budget, `${JSON.stringify(key)}:`);
|
|
87
|
+
body += canonicalJson(record[key], depth + 1, budget, seen);
|
|
88
|
+
}
|
|
30
89
|
}
|
|
90
|
+
const close = chargeBytes(budget, isArray ? ']' : '}');
|
|
31
91
|
seen.delete(value);
|
|
32
|
-
return
|
|
92
|
+
return `${open}${body}${close}`;
|
|
33
93
|
}
|
|
34
94
|
function sha256(value) {
|
|
35
95
|
return createHash('sha256').update(value).digest('hex');
|
|
36
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Fingerprint one canonical form, or `undefined` when it cannot be proven.
|
|
99
|
+
*
|
|
100
|
+
* Fail-open by construction: budget overflow, cycles, and a throwing getter on
|
|
101
|
+
* an otherwise plain object all resolve to "no fingerprint", never an exception
|
|
102
|
+
* for the caller. `undefined` is *not* evidence that two values are equal — see
|
|
103
|
+
* `fingerprintsMatch`.
|
|
104
|
+
*/
|
|
105
|
+
function boundedHash(prefix, value) {
|
|
106
|
+
try {
|
|
107
|
+
const canonical = canonicalJson(value, 1, { nodes: 0, bytes: 0 }, new Set());
|
|
108
|
+
return sha256(`${prefix}\0${canonical}`);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Bounded argument signature; `undefined` means this call is not comparable. */
|
|
37
115
|
export function guardToolSignature(name, argumentsValue) {
|
|
38
|
-
return
|
|
116
|
+
return boundedHash(name, argumentsValue);
|
|
39
117
|
}
|
|
40
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Fingerprint a result/error transiently; raw content is never returned or
|
|
120
|
+
* retained. `undefined` means "cannot prove the result did not change".
|
|
121
|
+
*/
|
|
41
122
|
export function guardResultFingerprint(outcome, value) {
|
|
42
|
-
return
|
|
123
|
+
return boundedHash(outcome, value);
|
|
43
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Two entries match only when *both* carry a fingerprint. A missing fingerprint
|
|
127
|
+
* means "cannot prove no progress happened" and must never be read as "results
|
|
128
|
+
* are identical" — otherwise every oversized result would become no-progress
|
|
129
|
+
* evidence and could be denied.
|
|
130
|
+
*/
|
|
44
131
|
function fingerprintsMatch(a, b) {
|
|
45
132
|
return a.outcome === b.outcome
|
|
46
133
|
&& a.resultFingerprint !== undefined
|
|
@@ -78,6 +165,10 @@ function noopDecision() {
|
|
|
78
165
|
* Pure bounded detector. It never enforces a step, call-count, or duration budget.
|
|
79
166
|
* Alternating cycles warn; a local-block candidate requires matching result/error
|
|
80
167
|
* fingerprints for every leg of the repeated cycle.
|
|
168
|
+
*
|
|
169
|
+
* An observation whose argument signature exceeds the fingerprint budget is
|
|
170
|
+
* dropped exactly like an excluded tool: without a stable signature there is no
|
|
171
|
+
* comparable history entry, so no reminder and no block candidate is produced.
|
|
81
172
|
*/
|
|
82
173
|
export function observeGuardLoop(previousState, turn, observation, options = {}) {
|
|
83
174
|
const base = previousState.turn === turn ? previousState : createGuardLoopState(turn);
|
|
@@ -85,6 +176,8 @@ export function observeGuardLoop(previousState, turn, observation, options = {})
|
|
|
85
176
|
return { state: base, decision: noopDecision() };
|
|
86
177
|
}
|
|
87
178
|
const signature = observation.signature ?? guardToolSignature(observation.name, observation.arguments);
|
|
179
|
+
if (signature === undefined)
|
|
180
|
+
return { state: base, decision: noopDecision() };
|
|
88
181
|
const fingerprint = observation.resultFingerprint
|
|
89
182
|
?? (Object.hasOwn(observation, 'result') ? guardResultFingerprint(observation.outcome, observation.result) : undefined);
|
|
90
183
|
const current = {
|
|
@@ -122,12 +215,21 @@ export function observeGuardLoop(previousState, turn, observation, options = {})
|
|
|
122
215
|
throw new Error('repeatThresholds must contain unique integers >= 2');
|
|
123
216
|
}
|
|
124
217
|
const repeatThreshold = thresholds.includes(repeatCount) ? repeatCount : undefined;
|
|
125
|
-
|
|
218
|
+
// Repeat detection and cycle detection are independent switches: neither may
|
|
219
|
+
// suppress the other. `repeatCount` keeps counting even when reminders are off.
|
|
220
|
+
const repeatEnabled = options.repeatDetection !== false;
|
|
221
|
+
const warning = alternatingCycle ? 'alternating-cycle' : repeatEnabled && repeatThreshold !== undefined ? 'repeat' : 'none';
|
|
126
222
|
const noProgressEvidence = cycleEvidence !== 'none' ? cycleEvidence : exactEvidence;
|
|
223
|
+
const repeatCandidate = repeatEnabled && exactEvidence !== 'none' && repeatCount >= thresholds[thresholds.length - 1];
|
|
224
|
+
const cycleCandidate = cycleEvidence !== 'none';
|
|
225
|
+
const blockSource = cycleCandidate
|
|
226
|
+
? 'cycle'
|
|
227
|
+
: repeatCandidate
|
|
228
|
+
? 'repeat'
|
|
229
|
+
: undefined;
|
|
127
230
|
const blockCandidate = options.noProgressProtection !== false
|
|
128
231
|
&& !options.warnOnlyTools?.has(observation.name)
|
|
129
|
-
&&
|
|
130
|
-
|| (exactEvidence !== 'none' && repeatCount >= thresholds[thresholds.length - 1]));
|
|
232
|
+
&& blockSource !== undefined;
|
|
131
233
|
return {
|
|
132
234
|
state: { turn, history: nextHistory },
|
|
133
235
|
decision: {
|
|
@@ -138,6 +240,7 @@ export function observeGuardLoop(previousState, turn, observation, options = {})
|
|
|
138
240
|
warning,
|
|
139
241
|
...(repeatThreshold !== undefined ? { repeatThreshold } : {}),
|
|
140
242
|
blockCandidate,
|
|
243
|
+
...(blockCandidate && blockSource !== undefined ? { blockSource } : {}),
|
|
141
244
|
noProgressEvidence,
|
|
142
245
|
},
|
|
143
246
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard-loop-core.js","sourceRoot":"","sources":["../src/guard-loop-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AACpC,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"guard-loop-core.js","sourceRoot":"","sources":["../src/guard-loop-core.ts"],"names":[],"mappings":"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"}
|