@dtlabs/ai-toolkit 0.24.2 → 0.24.3
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/CHANGELOG.md +8 -0
- package/lib/workflow-control.js +19 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.24.3](https://github.com/dtlab-tech/ai-toolkit/compare/v0.24.2...v0.24.3) (2026-10-27)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
* don't fail a worker call over a read-only permission denial ([1b4c521](https://github.com/dtlab-tech/ai-toolkit/commit/1b4c5217b42e9c9dcb74c9bbe60f209522644f9c))
|
|
8
|
+
* **workflow-control:** don't fail a worker call over a read-only permission denial ([fb283c6](https://github.com/dtlab-tech/ai-toolkit/commit/fb283c6a0f44f56d0ca152bcf84bf81c1ca10125))
|
|
9
|
+
|
|
2
10
|
## [0.24.2](https://github.com/dtlab-tech/ai-toolkit/compare/v0.24.1...v0.24.2) (2026-10-26)
|
|
3
11
|
|
|
4
12
|
|
package/lib/workflow-control.js
CHANGED
|
@@ -230,10 +230,27 @@ function createControl(options) {
|
|
|
230
230
|
tokenLedger.push({ agent: identity.nativeName, model: definition.model,
|
|
231
231
|
phase_delta_tokens: measuredTokens, usage: result.result?.usage || null, evidence_path: evidence.file });
|
|
232
232
|
evidence.save({ response: result, attempts });
|
|
233
|
+
// A denial on a read-only tool (Read/Glob/Grep) means the agent couldn't fetch some EXTRA
|
|
234
|
+
// context — a real, recurring pattern where a path the model constructs itself (e.g. a
|
|
235
|
+
// single leading backslash with no drive letter) resolves outside the project directory
|
|
236
|
+
// and is correctly denied. Confirmed via production evidence twice, on two different
|
|
237
|
+
// paths: the agent recovers and still produces a complete, structurally valid result, which
|
|
238
|
+
// this unconditional check threw away anyway, wasting the entire call and forcing a resume.
|
|
239
|
+
// Any OTHER tool (Write/Edit/Bash/...) stays an immediate, unconditional failure — a denial
|
|
240
|
+
// there is a near-miss on a real side effect, not a missed read, and must never be silently
|
|
241
|
+
// tolerated regardless of what the rest of the response looks like.
|
|
242
|
+
const READ_ONLY_DENIAL_TOOLS = new Set(['Read', 'Glob', 'Grep'])
|
|
233
243
|
const denials = result.result?.permission_denials;
|
|
234
244
|
if (Array.isArray(denials) && denials.length) {
|
|
235
|
-
const
|
|
236
|
-
|
|
245
|
+
const nonReadOnly = denials.filter(d => !READ_ONLY_DENIAL_TOOLS.has(d.tool_name));
|
|
246
|
+
if (nonReadOnly.length) {
|
|
247
|
+
const tools = [...new Set(nonReadOnly.map(d => d.tool_name || 'unknown'))].join(', ');
|
|
248
|
+
throw new Error('Worker permission denied: ' + id + '; tools: ' + tools);
|
|
249
|
+
}
|
|
250
|
+
// Read-only denial(s) only: fall through to the normal result-validity checks below —
|
|
251
|
+
// an agent that actually proceeded with missing context will still fail there (a bad
|
|
252
|
+
// exitCode/is_error, or the caller's own downstream structural validation), the same way
|
|
253
|
+
// it would if no denial had ever happened.
|
|
237
254
|
}
|
|
238
255
|
if (result.exitCode !== 0 || result.parseError || result.timedOut ||
|
|
239
256
|
!result.result || result.result.is_error !== false) {
|