@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 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
 
@@ -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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtlabs/ai-toolkit",
3
- "version": "0.14.3",
3
+ "version": "0.15.0",
4
4
  "description": "AI Toolkit — reusable agents and procedures for developers to build AI-powered applications.",
5
5
  "files": [
6
6
  "docs/",