Support arbitrary koral nodes in AST
diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go
index be11da9..d0ff222 100644
--- a/pkg/parser/parser.go
+++ b/pkg/parser/parser.go
@@ -27,6 +27,9 @@
 	if err := json.Unmarshal(data, &raw); err != nil {
 		return nil, fmt.Errorf("failed to parse JSON: %w", err)
 	}
+	if raw.Type == "" {
+		return nil, fmt.Errorf("missing @type field")
+	}
 	return parseNode(raw)
 }
 
@@ -82,7 +85,45 @@
 		}, nil
 
 	default:
-		return nil, fmt.Errorf("unknown node type: %s", raw.Type)
+		// Store the original JSON content
+		rawContent, err := json.Marshal(raw)
+		if err != nil {
+			return nil, fmt.Errorf("failed to marshal unknown node: %w", err)
+		}
+
+		// Create a catchall node
+		catchall := &ast.CatchallNode{
+			NodeType:   raw.Type,
+			RawContent: rawContent,
+		}
+
+		// Parse wrap if present
+		if raw.Wrap != nil {
+			var wrapRaw rawNode
+			if err := json.Unmarshal(raw.Wrap, &wrapRaw); err != nil {
+				return nil, fmt.Errorf("failed to parse wrap in unknown node: %w", err)
+			}
+			wrap, err := parseNode(wrapRaw)
+			if err != nil {
+				return nil, err
+			}
+			catchall.Wrap = wrap
+		}
+
+		// Parse operands if present
+		if len(raw.Operands) > 0 {
+			operands := make([]ast.Node, len(raw.Operands))
+			for i, op := range raw.Operands {
+				node, err := parseNode(op)
+				if err != nil {
+					return nil, err
+				}
+				operands[i] = node
+			}
+			catchall.Operands = operands
+		}
+
+		return catchall, nil
 	}
 }
 
@@ -122,6 +163,38 @@
 			Value:   n.Value,
 		}
 
+	case *ast.CatchallNode:
+		// For catchall nodes, use the stored raw content
+		if n.RawContent != nil {
+			// If we have operands or wrap that were modified, we need to update the raw content
+			if len(n.Operands) > 0 || n.Wrap != nil {
+				var raw rawNode
+				if err := json.Unmarshal(n.RawContent, &raw); err != nil {
+					return rawNode{}
+				}
+
+				// Update operands if present
+				if len(n.Operands) > 0 {
+					raw.Operands = make([]rawNode, len(n.Operands))
+					for i, op := range n.Operands {
+						raw.Operands[i] = nodeToRaw(op)
+					}
+				}
+
+				// Update wrap if present
+				if n.Wrap != nil {
+					raw.Wrap = json.RawMessage(nodeToRaw(n.Wrap).toJSON())
+				}
+
+				return raw
+			}
+			// If no modifications, return the original content as is
+			var raw rawNode
+			_ = json.Unmarshal(n.RawContent, &raw)
+			return raw
+		}
+		return rawNode{}
+
 	default:
 		return rawNode{}
 	}