@adeu/core 1.13.0 → 1.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adeu/core",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -531,4 +531,24 @@ describe("Resolved Bugs Core Engine Verification", () => {
531
531
  const final_comment_parts = doc.pkg.parts.filter(p => p.contentType.includes("comments"));
532
532
  expect(final_comment_parts.length).toBe(0);
533
533
  });
534
+
535
+ it("Double-Serialization Core: process_batch successfully processes double-serialized JSON strings", async () => {
536
+ const doc = await createTestDocument();
537
+ addParagraph(doc, "original text");
538
+ const engine = new RedlineEngine(doc);
539
+
540
+ // On unpatched code, this will throw a raw TypeError: Cannot create property '_applied_status' on string
541
+ // On patched code, it will successfully parse and apply the modify change
542
+ engine.process_batch([
543
+ JSON.stringify({
544
+ type: "modify",
545
+ target_text: "original text",
546
+ new_text: "updated text",
547
+ }),
548
+ ] as any);
549
+
550
+ const buf = await doc.save();
551
+ const text = await extractTextFromBuffer(buf, true);
552
+ expect(text).toContain("updated text");
553
+ });
534
554
  });
package/src/engine.ts CHANGED
@@ -1399,6 +1399,34 @@ export class RedlineEngine {
1399
1399
  changes: DocumentChange[],
1400
1400
  dry_run: boolean = false,
1401
1401
  ): any {
1402
+ // Defensive sanitization: some LLM clients "double-serialize" nested
1403
+ // arrays, delivering each element of `changes` as a JSON string instead of
1404
+ // a parsed object. Downstream code mutates state trackers (e.g.
1405
+ // `edit._applied_status`) and reads `change.type` on these elements, which
1406
+ // throws a TypeError on string primitives. Parse stringified elements back
1407
+ // into objects here, leaving genuine objects (and unparseable strings)
1408
+ // untouched so validation can surface a clear error rather than crashing.
1409
+ if (Array.isArray(changes)) {
1410
+ changes = changes.map((item: any) => {
1411
+ if (typeof item === "string") {
1412
+ try {
1413
+ const parsed = JSON.parse(item);
1414
+ // Only swap in the parsed value if it is an object; a string that
1415
+ // parses to a scalar (e.g. "42") is not a valid change.
1416
+ if (parsed !== null && typeof parsed === "object") {
1417
+ return parsed;
1418
+ }
1419
+ return item;
1420
+ } catch {
1421
+ // Leave malformed strings as-is; the validation pass downstream
1422
+ // will report them rather than crashing on a raw TypeError.
1423
+ return item;
1424
+ }
1425
+ }
1426
+ return item;
1427
+ }) as DocumentChange[];
1428
+ }
1429
+
1402
1430
  if (dry_run) {
1403
1431
  const baselines = new Map<any, Element>();
1404
1432
  for (const part of this.doc.pkg.parts) {