Explorar el Código

Merge pull request #133 from pzysvip99999/fix/settler-json-sanitizer

fix: sanitize JSON before parsing in settler-delta-parser
Junxian Ma hace 7 meses
padre
commit
07043c3e75

+ 7 - 1
packages/core/src/agents/settler-delta-parser.ts

@@ -8,6 +8,12 @@ export interface SettlerDeltaOutput {
   readonly runtimeStateDelta: RuntimeStateDelta;
   readonly runtimeStateDelta: RuntimeStateDelta;
 }
 }
 
 
+function sanitizeJSON(str: string): string {
+  return str
+    .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "")
+    .replace(/,\s*([}\]])/g, "$1");
+}
+
 export function parseSettlerDeltaOutput(content: string): SettlerDeltaOutput {
 export function parseSettlerDeltaOutput(content: string): SettlerDeltaOutput {
   const extract = (tag: string): string => {
   const extract = (tag: string): string => {
     const regex = new RegExp(
     const regex = new RegExp(
@@ -25,7 +31,7 @@ export function parseSettlerDeltaOutput(content: string): SettlerDeltaOutput {
   const jsonPayload = stripCodeFence(rawDelta);
   const jsonPayload = stripCodeFence(rawDelta);
   let parsed: unknown;
   let parsed: unknown;
   try {
   try {
-    parsed = JSON.parse(jsonPayload);
+    parsed = JSON.parse(sanitizeJSON(jsonPayload));
   } catch (error) {
   } catch (error) {
     throw new Error(`runtime state delta is not valid JSON: ${String(error)}`);
     throw new Error(`runtime state delta is not valid JSON: ${String(error)}`);
   }
   }

+ 69 - 59
packages/core/src/state/state-validator.ts

@@ -17,74 +17,84 @@ export function validateRuntimeState(input: {
   readonly hooks: unknown;
   readonly hooks: unknown;
   readonly chapterSummaries: unknown;
   readonly chapterSummaries: unknown;
 }): RuntimeStateValidationIssue[] {
 }): RuntimeStateValidationIssue[] {
-  const issues: RuntimeStateValidationIssue[] = [];
+  try {
+    const issues: RuntimeStateValidationIssue[] = [];
 
 
-  const manifest = parseOrIssue(
-    StateManifestSchema,
-    input.manifest,
-    issues,
-    "invalid_manifest",
-    "manifest",
-  );
-  const currentState = parseOrIssue(
-    CurrentStateStateSchema,
-    input.currentState,
-    issues,
-    "invalid_current_state",
-    "currentState",
-  );
-  const hooks = parseOrIssue(
-    HooksStateSchema,
-    input.hooks,
-    issues,
-    "invalid_hooks_state",
-    "hooks",
-  );
-  const chapterSummaries = parseOrIssue(
-    ChapterSummariesStateSchema,
-    input.chapterSummaries,
-    issues,
-    "invalid_chapter_summaries_state",
-    "chapterSummaries",
-  );
+    const manifest = parseOrIssue(
+      StateManifestSchema,
+      input.manifest,
+      issues,
+      "invalid_manifest",
+      "manifest",
+    );
+    const currentState = parseOrIssue(
+      CurrentStateStateSchema,
+      input.currentState,
+      issues,
+      "invalid_current_state",
+      "currentState",
+    );
+    const hooks = parseOrIssue(
+      HooksStateSchema,
+      input.hooks,
+      issues,
+      "invalid_hooks_state",
+      "hooks",
+    );
+    const chapterSummaries = parseOrIssue(
+      ChapterSummariesStateSchema,
+      input.chapterSummaries,
+      issues,
+      "invalid_chapter_summaries_state",
+      "chapterSummaries",
+    );
 
 
-  if (hooks) {
-    const seen = new Set<string>();
-    for (const hook of hooks.hooks) {
-      if (seen.has(hook.hookId)) {
-        issues.push({
-          code: "duplicate_hook_id",
-          message: `duplicate hook id: ${hook.hookId}`,
-          path: `hooks.${hook.hookId}`,
-        });
+    if (hooks) {
+      const seen = new Set<string>();
+      for (const hook of hooks.hooks) {
+        if (seen.has(hook.hookId)) {
+          issues.push({
+            code: "duplicate_hook_id",
+            message: `duplicate hook id: ${hook.hookId}`,
+            path: `hooks.${hook.hookId}`,
+          });
+        }
+        seen.add(hook.hookId);
       }
       }
-      seen.add(hook.hookId);
     }
     }
-  }
 
 
-  if (chapterSummaries) {
-    const seen = new Set<number>();
-    for (const row of chapterSummaries.rows) {
-      if (seen.has(row.chapter)) {
-        issues.push({
-          code: "duplicate_summary_chapter",
-          message: `duplicate summary chapter: ${row.chapter}`,
-          path: `chapterSummaries.${row.chapter}`,
-        });
+    if (chapterSummaries) {
+      const seen = new Set<number>();
+      for (const row of chapterSummaries.rows) {
+        if (seen.has(row.chapter)) {
+          issues.push({
+            code: "duplicate_summary_chapter",
+            message: `duplicate summary chapter: ${row.chapter}`,
+            path: `chapterSummaries.${row.chapter}`,
+          });
+        }
+        seen.add(row.chapter);
       }
       }
-      seen.add(row.chapter);
     }
     }
-  }
 
 
-  if (manifest && currentState && currentState.chapter > manifest.lastAppliedChapter) {
-    issues.push({
-      code: "current_state_ahead_of_manifest",
-      message: `current state chapter ${currentState.chapter} exceeds manifest ${manifest.lastAppliedChapter}`,
-      path: "currentState.chapter",
-    });
-  }
+    if (manifest && currentState && currentState.chapter > manifest.lastAppliedChapter) {
+      issues.push({
+        code: "current_state_ahead_of_manifest",
+        message: `current state chapter ${currentState.chapter} exceeds manifest ${manifest.lastAppliedChapter}`,
+        path: "currentState.chapter",
+      });
+    }
 
 
-  return issues;
+    return issues;
+  } catch (error) {
+    return [
+      {
+        code: "validator_crash",
+        message: String(error),
+        path: "",
+      },
+    ];
+  }
 }
 }
 
 
 function parseOrIssue<T>(
 function parseOrIssue<T>(