@dtlabs/ai-toolkit 0.14.3 → 0.15.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/CHANGELOG.md +14 -0
- package/lib/workflow-control.js +31 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.15.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.14.3...v0.15.0) (2026-10-01)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* Add isolated parallel task execution and task checkpoints documentation ([e17121f](https://github.com/dtlab-tech/ai-toolkit/commit/e17121fbf118673831634f981294d58e1a665d49))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* **workflows:** never fail-closed inside extractJsonDocument itself ([dd5f0ce](https://github.com/dtlab-tech/ai-toolkit/commit/dd5f0ce07b7145ef73e29479e4282105000cfd42))
|
|
13
|
+
* **workflows:** recover JSON worker output wrapped in prose or a code fence ([b7a0d08](https://github.com/dtlab-tech/ai-toolkit/commit/b7a0d0817cf6056893906aacff87b40578c6625b))
|
|
14
|
+
* **workflows:** recover JSON worker output wrapped in prose or a code fence ([0dbaece](https://github.com/dtlab-tech/ai-toolkit/commit/0dbaeceb193521c2aca59a15de3762def4881935))
|
|
15
|
+
|
|
2
16
|
## [0.14.3](https://github.com/dtlab-tech/ai-toolkit/compare/v0.14.2...v0.14.3) (2026-10-01)
|
|
3
17
|
|
|
4
18
|
|
package/lib/workflow-control.js
CHANGED
|
@@ -217,7 +217,7 @@ function createControl(options) {
|
|
|
217
217
|
if (outputTarget) {
|
|
218
218
|
const content = opts.schema ? payload.report : payload;
|
|
219
219
|
if (typeof content !== 'string' || !content.trim()) throw new Error('Worker output content missing or empty: ' + id);
|
|
220
|
-
writeOutput(outputTarget, content);
|
|
220
|
+
writeOutput(outputTarget, /\.json$/i.test(outputTarget) ? extractJsonDocument(content) : content);
|
|
221
221
|
}
|
|
222
222
|
evidence.save({ outcome: 'done', completedAt: new Date().toISOString() });
|
|
223
223
|
return payload;
|
|
@@ -317,6 +317,36 @@ function extractDelimitedStructuredOutput(text, schema) {
|
|
|
317
317
|
return null;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
// A worker with a declared JSON output has no Write tool (see worker()'s own comment on why):
|
|
321
|
+
// its entire response text becomes the file verbatim. Some models still wrap that deliverable
|
|
322
|
+
// in conversational prose and/or a Markdown code fence despite being told not to (observed
|
|
323
|
+
// empirically with gaia-generate-work-breakdown on haiku: "Perfect. I have all the necessary
|
|
324
|
+
// information..." before the JSON), which is a hard JSON.parse failure for every downstream
|
|
325
|
+
// consumer of this file. Recover the document with the same "try each known convention, always
|
|
326
|
+
// re-validate as parseable JSON, never a blind substring guess" discipline as
|
|
327
|
+
// extractDelimitedStructuredOutput: the content as-is first, then the last fenced code block,
|
|
328
|
+
// then the outermost brace/bracket span. Never throws — genuinely malformed JSON (not merely
|
|
329
|
+
// wrapped) is passed through unchanged so the downstream deterministic validator (wb-validate.js)
|
|
330
|
+
// remains the one thing that fails closed on it; this function only undoes wrapping, it is not
|
|
331
|
+
// itself a validation gate.
|
|
332
|
+
function extractJsonDocument(text) {
|
|
333
|
+
const tryParse = candidate => { try { JSON.parse(candidate); return candidate; } catch (e) { return null; } };
|
|
334
|
+
const whole = tryParse(text.trim());
|
|
335
|
+
if (whole != null) return whole;
|
|
336
|
+
const fenceMatches = [...text.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi)];
|
|
337
|
+
for (let i = fenceMatches.length - 1; i >= 0; i--) {
|
|
338
|
+
const candidate = tryParse(fenceMatches[i][1].trim());
|
|
339
|
+
if (candidate != null) return candidate;
|
|
340
|
+
}
|
|
341
|
+
const first = text.search(/[[{]/);
|
|
342
|
+
const last = Math.max(text.lastIndexOf('}'), text.lastIndexOf(']'));
|
|
343
|
+
if (first >= 0 && last > first) {
|
|
344
|
+
const candidate = tryParse(text.slice(first, last + 1).trim());
|
|
345
|
+
if (candidate != null) return candidate;
|
|
346
|
+
}
|
|
347
|
+
return text;
|
|
348
|
+
}
|
|
349
|
+
|
|
320
350
|
function validateSchema(value, schema) {
|
|
321
351
|
if (schema.type === 'object') {
|
|
322
352
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid structured object');
|