@tea-agent/loop-agent 0.27.1-beta.0 → 0.27.1-beta.1
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.
|
@@ -395,8 +395,55 @@ export function extractFrontendImplementationJson(text) {
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
};
|
|
398
|
+
const isContract = (candidate) => {
|
|
399
|
+
const record = asRecord(candidate);
|
|
400
|
+
return (record?.schemaVersion === 1 &&
|
|
401
|
+
asRecord(record.targets) !== null &&
|
|
402
|
+
Array.isArray(record.requirements) &&
|
|
403
|
+
Array.isArray(record.verificationTargets));
|
|
404
|
+
};
|
|
398
405
|
if (trimmed.startsWith("{") && trimmed.endsWith("}"))
|
|
399
406
|
return parse(trimmed);
|
|
407
|
+
const balancedObjects = [];
|
|
408
|
+
for (let start = 0; start < trimmed.length; start += 1) {
|
|
409
|
+
if (trimmed[start] !== "{")
|
|
410
|
+
continue;
|
|
411
|
+
let depth = 0;
|
|
412
|
+
let inString = false;
|
|
413
|
+
let escaped = false;
|
|
414
|
+
for (let index = start; index < trimmed.length; index += 1) {
|
|
415
|
+
const character = trimmed[index];
|
|
416
|
+
if (inString) {
|
|
417
|
+
if (escaped)
|
|
418
|
+
escaped = false;
|
|
419
|
+
else if (character === "\\")
|
|
420
|
+
escaped = true;
|
|
421
|
+
else if (character === '"')
|
|
422
|
+
inString = false;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (character === '"')
|
|
426
|
+
inString = true;
|
|
427
|
+
else if (character === "{")
|
|
428
|
+
depth += 1;
|
|
429
|
+
else if (character === "}" && --depth === 0) {
|
|
430
|
+
try {
|
|
431
|
+
const candidate = parse(trimmed.slice(start, index + 1));
|
|
432
|
+
if (candidate && typeof candidate === "object" && !Array.isArray(candidate))
|
|
433
|
+
balancedObjects.push(candidate);
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
// Continue scanning for a later complete JSON object.
|
|
437
|
+
}
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const balancedContracts = balancedObjects.filter(isContract);
|
|
443
|
+
if (balancedContracts.length === 1)
|
|
444
|
+
return balancedContracts[0];
|
|
445
|
+
if (balancedObjects.length === 1)
|
|
446
|
+
return balancedObjects[0];
|
|
400
447
|
const blocks = [...trimmed.matchAll(/```json\s*\n([\s\S]*?)\n```/gi)];
|
|
401
448
|
if (blocks.length === 0)
|
|
402
449
|
throw new Error("output must contain exactly one fenced json object");
|
|
@@ -412,9 +459,14 @@ export function extractFrontendImplementationJson(text) {
|
|
|
412
459
|
// block may still be deterministically recoverable.
|
|
413
460
|
}
|
|
414
461
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
462
|
+
const contractCandidates = candidates.filter(isContract);
|
|
463
|
+
if (contractCandidates.length === 1)
|
|
464
|
+
return contractCandidates[0];
|
|
465
|
+
if (candidates.length === 1)
|
|
466
|
+
return candidates[0];
|
|
467
|
+
if (candidates.length === 0)
|
|
468
|
+
throw new Error("output must contain exactly one valid fenced json object (found 0)");
|
|
469
|
+
throw new Error(`output must contain exactly one valid frontend contract json object (found ${contractCandidates.length || candidates.length})`);
|
|
418
470
|
}
|
|
419
471
|
/**
|
|
420
472
|
* Build the authoritative frontend-implementation-contract sourceBinding from
|